See the question and my original answer on StackOverflow

You need to replace the following line:

Marshal.WriteInt32(DeviceInterfaceDetailData, (int)size);


Marshal.WriteInt32(DeviceInterfaceDetailData, IntPtr.Size == 8 ? 8 : 6);

Because SetupDiGetDeviceInterfaceDetail documentation explicitely states this:

The caller must set DeviceInterfaceDetailData.cbSize to sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) before calling this function. The cbSize member always contains the size of the fixed part of the data structure, not a size reflecting the variable-length string at the end.

sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) depends on the process bitness, it's 6 with an X86 process (byte packing + 1 char, auto -> unicode -> 4 + 2*1) and 8 with an X64 process (8 bytes packing anyway).

Also note your definition of SetupDiGetDeviceInterfaceDetail should use ref parameters for structures. Once you have changed all that you can get the device path like this:

string devicePath = Marshal.PtrToStringAuto(DeviceInterfaceDetailData + 4);

(PtrToStringAuto because you choose Auto charset on your p/invoke definitions)