【问题标题】:buffer size is incorrect when marshaling to native code编组为本机代码时缓冲区大小不正确
【发布时间】:2011-11-18 17:15:44
【问题描述】:

我正在尝试对 SetupApi 进行一些调用。我特别遇到SetupDiGetDeviceInterfaceDetail() 的问题。

这是我对本机方法的定义:

class NativeMethods {
    [DllImport("SetupApi.dll", SetLastError = true)]
    [return : MarshalAs(UnmanagedType.Bool)]
    public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevs,
                    ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
                    ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
                    uint deviceInterfaceDetailDataSize,
                    ref uint requiredSize,
                    ref SP_DEVINFO_DATA deviceInfoData);
}

以下是所涉及结构的本地与托管定义:

[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct GUID
{                    
    public Int32 Data1;
    public Int16 Data2;
    public Int16 Data3;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] Data4;

    public GUID(Int32 d1, Int16 d2, Int16 d3, byte[] d4)
    {
        Data1 = d1;
        Data2 = d2;
        Data3 = d3;
        Data4 = new byte[8];
        Array.Copy(d4, Data4, d4.Length);
    }
}

typedef struct _SP_DEVINFO_DATA {
    DWORD cbSize;
    GUID  ClassGuid;
    DWORD DevInst;    // DEVINST handle
    ULONG_PTR Reserved;
} SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;

[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
    public uint cbSize;
    public GUID  ClassGuid;
    public uint DevInst;
    public UIntPtr Reserved;
}

typedef struct _SP_DEVICE_INTERFACE_DATA {
    DWORD cbSize;
    GUID  InterfaceClassGuid;
    DWORD Flags;
    ULONG_PTR Reserved;
} SP_DEVICE_INTERFACE_DATA, *PSP_DEVICE_INTERFACE_DATA;

[StructLayout(LayoutKind.Sequential)]
struct SP_DEVICE_INTERFACE_DATA
{
    public uint cbSize;
    public GUID  InterfaceClassGuid;
    public uint Flags;
    public UIntPtr Reserved;
}

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA_A {
    DWORD  cbSize;
    CHAR   DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA_A, *PSP_DEVICE_INTERFACE_DETAIL_DATA_A;

[StructLayout(LayoutKind.Sequential)]
struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
    public uint cbSize;
    public IntPtr devicePath;
}

对于最后一个结构,我在这里阅读了之前的一篇文章,其中说结构,定义为 SP_DEVICE_INTERFACE_DETAIL_DATA 是一个可变长度数组,C# 结构应该使用 IntPtr。为此,我通过 Marshal.AllocHGlobal() 分配内存,如下所示。以下是我使用 SetupDiGetDeviceInterfaceDetail() 的方式:

uint requiredSize = 0; // is ignored in this usage
byte[] foo = new byte[1024];

SP_DEVINFO_DATA spDevInfoData = new SP_DEVINFO_DATA();
spDevInfoData.cbSize = (uint)Marshal.SizeOf(spDevInfoData);

SP_DEVICE_INTERFACE_DATA spDevInfData = new SP_DEVICE_INTERFACE_DATA();
spDevInfData.cbSize = (uint)Marshal.SizeOf(spDevInfData);

SP_DEVICE_INTERFACE_DETAIL_DATA spDevInfDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
spDevInfDetailData.cbSize = 5; // the size needs to be 5
spDevInfDetailData.devicePath = Marshal.AllocHGlobal(foo.Length); 

NativeMethods.SetupDiGetDeviceInterfaceDetail(devList,
                                              ref spDevInfData,
                                              ref spDevInfDetailData, 
                                              spDevInfDetailData.cbSize,
                                              ref requiredSize,
                                              ref spDevInfoData);

在使用 SetupDiGetDeviceInterfaceDetail() 之前,我正在使用 SetupDiGetClassDevs() 和 SetupDiEnumDeviceInterfaces()。在这两种情况下,这些方法都有效。我从第一个函数中提供了设备列表,并正在使用 enum 调用遍历列表。

但是,当我调用 SetupDiGetDeviceInterfaceDetail() 时,函数失败并且 Win32 GetLastError() 返回错误 122,我发现它是:ERROR_INSUFFICIENT_BUFFER。我不明白为什么我的缓冲区大小不足。我基本上是在做我在 C++“测试应用程序”中所做的事情,因为我无法在 C# 中让它工作。在该应用程序中,我使用分配给 SP_DEVICE_INTERFACE_DETAIL_DATA 结构的 char[] 数组 1024 个成员。这就是我在 C# 中使用 1024 个成员的字节数组的原因。

非常感谢任何帮助或见解。

【问题讨论】:

  • 后来你看requiredSize了吗?

标签: c# pinvoke


【解决方案1】:

是的,您没有正确使用该功能。 SP_DEVICE_INTERFACE_DETAIL_DATA 是可变大小的结构,ANYSIZE_ARRAY 没有意义。您必须调用该函数两次。在第一次调用中,为 DeviceInterfaceDetailData 传递 IntPtr.Zero,为 DeviceInterfaceDetailDataSize 传递 0。返回的RequiredSize 告诉您要为结构分配多少内存。再次分配和调用,现在传递指针和大小。

【讨论】:

  • 汉斯,谢谢你的帮助。更仔细地阅读 API 参考资料表明我只需要关注“第一次”调用。 SP_DEVICE_INTERFACE_DETAIL_DATA 中的信息对我试图做的事情并不重要。我把这个问题归结为两个不同的事情。 1) 我对 API 不熟悉,2) 我使用的是我在网上找到的一个 C++ 示例,该示例可以正常工作(使用错误)。无论如何,我认为 API 中存在错误,因为在分配适当的内存后,我仍然收到错误 122(buff 太小)。现在可以使用了,谢谢。
猜你喜欢
  • 2020-09-05
  • 2018-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-25
相关资源
最近更新 更多