C# 在32位和64位运行时编组动态结构

C# 在32位和64位运行时编组动态结构,c#,pinvoke,setupapi,C#,Pinvoke,Setupapi,我正在调用SetupDiGetDeviceInterfaceDetail(),SP_设备_接口_细节_数据结构未正确封送。可以找到结构定义。我试着使用PInvoke.net中的这个结构的定义,但是没有用 到目前为止,当对函数的调用成功(即封送处理程序没有抛出错误)时,返回值为1784(无效的用户缓冲区)。更重要的是,当这段代码从我的机器上的32位进程执行时,所有这些都可以正常工作。当它在64位进程中运行时,我遇到了这个问题 我当前的SetupDigietInterfaceDetailData()

我正在调用SetupDiGetDeviceInterfaceDetail(),SP_设备_接口_细节_数据结构未正确封送。可以找到结构定义。我试着使用PInvoke.net中的这个结构的定义,但是没有用

到目前为止,当对函数的调用成功(即封送处理程序没有抛出错误)时,返回值为1784(无效的用户缓冲区)。更重要的是,当这段代码从我的机器上的32位进程执行时,所有这些都可以正常工作。当它在64位进程中运行时,我遇到了这个问题

我当前的SetupDigietInterfaceDetailData()签名如下所示:

[DllImport(@"c:\Windows\System32\SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
    SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
    ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
    IntPtr deviceInterfaceDetailData,
    uint deviceInterfaceDetailDataSize,
    IntPtr requiredSize,
    IntPtr deviceInfoData);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
    public UInt32 cbSize;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string DevicePath;
}
目前,我正在使用Marshal.AllocHGlobal()分配内存,并使用Marshal.*函数族从该缓冲区写入/读取数据

作为参考,以下是我正在做的:

public string GetPathToDevice(SafeHandleZeroOrMinusOneIsInvalid hDevList,
                              SP_DEVICE_INTERFACE_DATA devIntfData)
{
    uint sizeNeeded = 0;
    // get's the size needed
    SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
                                                 ref devIntfData,
                                                 IntPtr.Zero,
                                                 0,
                                                 ref sizeNeeded,
                                                 IntPtr.Zero);

    IntPtr pBuffer = Marshal.AllocHGlobal((int)(sizeNeeded + 4)); // +4 for cbSize
    SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
                                                 ref devIntfData,
                                                 pBuffer,
                                                 sizeNeeded,
                                                 IntPtr.Zero,
                                                 IntPtr.Zero);

    // copy bytes from unmanaged space in pBuffer to a manged byte array
    // free unmanaged memory

    return theStringParsedFromByteArray;
}
正如我所提到的,我已经尝试为SP_DEVICE_INTERFACE_DETAIL_DATA(参见上面的链接)定义一个PInvoke.net概述的结构,并创建了一个新的PInvoke方法签名来处理该结构。从64位系统运行时,我遇到了相同的问题,即函数返回1784。原因似乎是C#中的引用在64位运行时运行时是8字节对齐的(在另一篇StackOverflow文章中发现)。我已经尝试了该结构的各种布局,试图将布局(使用显式和字段偏移量)强制为4字节对齐的结构,但这对我也不起作用。我有编译时的问题

我尝试过使用各种装饰来设置PInvoke方法的符号参数。例如,MarshalAs(UnmanagedType.LPStruct),我一直在不正确地与之配对。我现在已经到了需要帮助的地步

我真的不明白为什么会这样。即使它在32位运行时在我的机器上运行,64位运行时不只是将我连接到正确的64位版本的安装API吗?有什么问题吗

谢谢你的帮助, 安迪

问题已解决

好的是,现在问题已经解决了,令人恼火的是我不喜欢在发布后一两个小时内解决问题。因此,问题确实是它是一个64位的问题。Marshal.GetLastWin32Error()中的错误代码告诉我该问题。cbSize值不正确。我把它改为8,现在一切正常

请,有人,解释一下为什么现在的大小是8对64位?结构现在在上面(一位评论员要求我将其包括在内)。该结构由两个成员组成,一个DWORD和一个TCHAR[ANYSIZE_数组]。ANYSIZE_数组的计算结果为1,如果为Unicode,则TCHAR始终为WCHAR,否则为char。DWORD始终是32位数量(4字节),Unicode的单个TCHAR为2字节。所以,4+2=6。为什么是8?这是因为64位结构的字节对齐吗?我真的很想了解这一点


无论如何,将cbSize成员设置为8(64位)和6(32位)都有效,我可以使用上面定义的结构,而不是原始内存分配/释放和封送处理。

我也遇到了这个问题,因为我从这个答案复制了代码: 它在结构定义中有一个错误。 也许你也犯了同样的错误

查看SP_DEVINFO_数据在其最后一个参数处的定义是指针,而不是另一篇文章中的uint

由于我将结构定义更改为:

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
    /// <summary>Size of the structure, in bytes.</summary>
    public uint cbSize;
    /// <summary>GUID of the device interface class.</summary>
    public Guid ClassGuid;
    /// <summary>Handle to this device instance.</summary>
    public uint DevInst;
    /// <summary>Reserved; do not use.</summary>
    public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential)]
私有结构SP_设备信息_数据
{
///结构的大小,以字节为单位。
公共单位cbSize;
///设备接口类的GUID。
公共Guid类Guid;
///此设备实例的句柄。
公共设备;
///保留;不要使用。
保留公共IntPtr;
}
它起作用了


Marshall.SizeOf(新的SP_DEVINFO_DATA())现在返回32而不是28,并且显示了我的串行端口。

发布您在C中定义的结构,您还需要明确定义结构的大小。@Ramhound目前,该结构只是我正在单独写入的原始内存。我在PInvoke中定义的结构位于我发布的链接中。我将编辑以在那里显示它。将
c:\Windows\System32\SetupApi.dll
作为dll名称是个坏主意。硬编码的路径是危险的。只要使用
setupapi.dll
。如果我是你,我根本不会在pinvoke中进行
SP\u设备\u接口\u细节\u数据
结构声明
ByValTStr
在这里没有用处。使用
AllocHGlobal
进行操作,并手动整理内容。虽然已经有一段时间了,但要回答您在“问题已解决”中提出的问题--是的,64位系统总是在8字节边界上对齐,因此,x64My上的6字节结构被填充为8字节。问题不在于SP_DEVINFO_数据的定义,而在于SP_DEVICE_INTERFACE_DETAIL_数据。我相信解决方法是使用David Heffernan的建议,简单地使用AllocHGlobal分配所需的内存,然后手工完成艰苦的工作。