为PInvoke正确声明SP_DEVICE_INTERFACE_DETAIL_DATA

GSe*_*erg 9 c# pinvoke winapi memory-alignment

SP_DEVICE_INTERFACE_DETAIL_DATA结构:

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
  DWORD cbSize;
  TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;
Run Code Online (Sandbox Code Playgroud)

如何在C#中声明它才能Marshal.SizeOf正常工作?

我没有分配动态缓冲区的问题.我只想以cbSize适当的,非硬编码的方式进行计算.

PInvoke.net定义是错误的.
PInvoke.net的解释也是错误的:

SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :)
Run Code Online (Sandbox Code Playgroud)

不要相信他.
4 + Marshal.SystemDefaultCharSize仅在x86上有效.同样的sizeof(int) + Marshal.SystemDefaultCharSize.在x64上它失败了.

这是非托管C++给出的:

x86
结构尺寸A:5
设备路径偏移A:4
结构尺寸W:6
设备路径偏移W:4

x64
结构尺寸A:8
设备路径偏移A:4
结构尺寸W:8
设备路径偏移W:4

我想每一个可能的组合StructLayoutMarshalAs参数,但我不能让它返回上述值.

什么是正确的声明?

Han*_*ant 9

结构的关键点是你不知道应该有多大.您必须两次调用SetupDiGetDeviceInterfaceDetail(),在第一次调用时,您有意为DeviceInterfaceDetailSize参数传递0.那当然会失败,但RequiredSize参数会告诉你结构需要有多大.然后,您分配一个正确大小的结构并再次调用它.

pinvoke marshaller或C#语言不直接支持动态调整结构大小.因此宣布结构根本没有帮助,不要尝试.你应该使用Marshal.AllocHGlobal().这将为您提供一个指针,您可以将其作为DeviceInterfaceDetailData参数传递.使用Marshal.WriteInt32设置cbSize.现在打电话.并使用Marshal.PtrToStringUni()检索返回的字符串.Marshal.FreeHGlobal清理.谷歌搜索从方法名称执行此操作的代码应该没有任何问题.


cbSize成员是一个问题,SetupApi.h SDK头文件包含以下内容:

#ifdef _WIN64
#include <pshpack8.h>   // Assume 8-byte (64-bit) packing throughout
#else
#include <pshpack1.h>   // Assume byte packing throughout (32-bit processor)
#endif
Run Code Online (Sandbox Code Playgroud)

这很难看,C编译器会认为在数组之后有2个字节的填充,即使没有.在C#代码中,对于32位代码与64位代码,StructLayoutAttribute.Pack值需要不同.没有声明两个结构就没有办法干净地做到这一点.并根据IntPtr.Size的值在它们之间进行选择.或者只是对其进行硬编码,因为无论如何结构声明都没有用,它在32位模式下为6,在64位模式下为8.在两种情况下,字符串从偏移量4开始.当然假设使用Unicode字符串,使用ansi字符串毫无意义.

  • 不,不要那样做.当托管代码构建为以AnyCPU为目标时,可以在32位或64位模式下运行.#ifdef无法解决该问题,您必须在运行时执行此操作. (3认同)