【问题标题】:Marshalling dynamic structures in 32 and 64 bit runtimes在 32 位和 64 位运行时编组动态结构
【发布时间】:2012-03-09 16:34:14
【问题描述】:

我正在调用 SetupDiGetDeviceInterfaceDetail() here 并且 SP_DEVICE_INTERFACE_DETAIL_DATA 结构未正确编组。结构定义可以在here 找到。我尝试使用 PInvoke.net 中的此结构的定义here,但无济于事。

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

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

[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 位版本的 Setup API 吗?有什么问题?

感谢您的帮助, 安迪

问题解决了

好消息是,现在已经解决了,烦人的是我不喜欢在发帖后的一两个小时内解决问题。所以,问题确实是它是一个 64 位的问题。 Marshal.GetLastWin32Error() 的错误代码告诉我问题所在。 cbSize 值不正确。我将其更改为 8,现在一切正常。

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

无论如何,将 cbSize 成员设置为 8(对于 64 位)和 6(对于 32 位)是可行的,并且我能够使用上面定义的结构而不是原始内存分配/解除分配和封送处理。

【问题讨论】:

  • 发布你在C#中定义的结构你还需要精确定义结构的大小。
  • @Ramhound 目前,该结构只是我正在单独写入的原始内存。我从 PInvoke 定义的结构位于我发布的链接中。我将编辑以显示它。
  • c:\Windows\System32\SetupApi.dll 作为您的 DLL 名称是个坏主意。硬编码该路径是危险的。只需使用setupapi.dll
  • 如果我是你,我根本不会在 pinvoke 中创建 SP_DEVICE_INTERFACE_DETAIL_DATA 结构声明。 ByValTStr 在这里没用。使用AllocHGlobal 进行操作并手动编组内容。
  • 已经有一段时间了,但要回答您在“问题已解决”中提出的问题——是的,64 位系统总是在 8 字节边界上对齐,所以 6 字节结构是在 x64 上填充到 8 字节

标签: c# pinvoke setupapi


【解决方案1】:

我也遇到了这个问题,因为我从这个答案中复制了代码:https://stackoverflow.com/a/2937588/1070906 在结构定义中有错误。 也许你也犯了同样的错误。

查看http://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx 处 SP_DEVINFO_DATA 的定义,它的最后一个参数是一个指针,而不是其他帖子中的 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;
}

有效!

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

【讨论】:

  • 我的问题不在于 SP_DEVINFO_DATA 的定义,而在于 SP_DEVICE_INTERFACE_DETAIL_DATA。我相信解决方案是使用 David Heffernan 的建议,并简单地使用 AllocHGlobal 分配所需的内存并手动完成工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-20
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 2014-02-12
相关资源
最近更新 更多