microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
MIT License
2.07k stars 87 forks source link

VariableLengthInlineArray and method return #1287

Closed Ephron-WL closed 1 week ago

Ephron-WL commented 1 week ago

Actual behavior

SP_DEVICE_INTERFACE_DETAIL_DATA_W is generated with a property DevicePath of type VariableLengthInlineArray<char>

An instance of SP_DEVICE_INTERFACE_DETAIL_DATA_W is used as an argument to SetupDiGetDeviceInterfaceDetail.

To obtain the DevicePath value I use new string(&detail.DeviocePath.eb0). This works great. If I execute this code within any method other than the Main method, I get an access violation at the point in which the method returns. If I create the SP_DEVICE_INTERFACE_DETAIL_DATA_W inside of the Main method and pass a pointer into the method and instantiate the string in the Main method, it works.

Great library, otherwise.

Expected behavior

I would expect to be able to execute my code within a method other than Main and return the results in the form of a string from a method other than Main.

Repro steps

  1. NativeMethods.txt content:
    
    SetupDiGetClassDevs
    CreateFile
    DeviceIoControl
    SetupDiGetDeviceInterfaceDetail
    SetupDiEnumDeviceInterfaces
    DISK_GEOMETRY_EX
    ReadFile
    SetFilePointer
    FSCTL_ALLOW_EXTENDED_DASD_IO
    STORAGE_PROPERTY_QUERY
    STORAGE_DEVICE_DESCRIPTOR
    IOCTL_STORAGE_GET_DEVICE_NUMBER
    IOCTL_STORAGE_QUERY_PROPERTY
    STORAGE_DEVICE_NUMBER
    DRIVE_LAYOUT_INFORMATION_EX
    IOCTL_DISK_GET_PARTITION_INFO_EX
    DRIVE_LAYOUT_INFORMATION
    IOCTL_DISK_GET_PARTITION_INFO
    PARTITION_INFORMATION_EX
    IOCTL_DISK_GET_DRIVE_LAYOUT_EX 
    IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
    GENERIC_ACCESS_RIGHTS

2. `NativeMethods.json` content (if present):
```json
{
  "$schema": "https://aka.ms/CsWin32.schema.json",
  "emitSingleFile": true,
  "public": true
}
  1. Any of your own code that should be shared?

    Context

Ephron-WL commented 1 week ago

Rookie mistake. When the method returns references to unmanaged memory are lost. To return a string, even if the string is copied, requires that the unmanaged memory remain, I presume due to compilation optimizations leading to memory assignments that avoid allocating to the heap. By assigning variables that hold unmanaged memroy to a context that survives method calls I can ensure the memory remains accessible.