【问题标题】:Different behavior when marshaling structures in 32 bit than in 64 bit runtimes在 32 位和 64 位运行时编组结构时的不同行为
【发布时间】:2015-07-16 16:02:53
【问题描述】:

我在 PInvoking SetupDiCreateDeviceInfoList 时发现了这一点。

C++ 函数签名是:

HDEVINFO SetupDiCreateDeviceInfoList(
  _In_opt_ const GUID *ClassGuid,
  _In_opt_       HWND hwndParent
);

在 C# 中,我定义了这样的 GUID 结构:

[StructLayout(LayoutKind.Sequential)]
public struct GUID
{
    public uint Data1;
    public ushort Data2;
    public ushort Data3;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] Data4;
}

还有这样的功能:

[DllImport("Setupapi.dll")]
public static extern IntPtr SetupDiCreateDeviceInfoList(GUID ClassGuid, IntPtr hwndParent);

由于在 C# 中结构默认通过副本传递(与类不同),因此此函数签名不应匹配。确实在 32 位运行时调用函数时:

GUID classGuid = new GUID();
IntPtr deviceInfoSet = SetupDiCreateDeviceInfoList(classGuid, IntPtr.Zero);

我收到一个错误:

SetupDiCreateDeviceInfoList' 使堆栈失衡。这很可能 因为托管的 PInvoke 签名与非托管的不匹配 目标签名。检查调用约定和参数 PInvoke 签名与目标非托管签名匹配。

但在 64 位运行时,上面的代码可以工作。为什么???

当然,如果我改为通过引用传递结构,该函数在 32 位和 64 位运行时都可以正常工作:

[DllImport("Setupapi.dll")]
public static extern IntPtr SetupDiCreateDeviceInfoList(ref GUID ClassGuid, IntPtr hwndParent);

GUID classGuid = new GUID();
IntPtr deviceInfoSet = SetupDiCreateDeviceInfoList(ref classGuid, IntPtr.Zero);

【问题讨论】:

  • @PeterDuniho 在 x64 上,我收到一个指向设备信息集的有效指针。我是在64位环境下开发的,每次都成功安装了驱动。当我决定在 32 位环境中进行测试时,我才发现错误的函数签名。

标签: c# winapi data-structures pinvoke marshalling


【解决方案1】:

x64 调用约定非常不同于 x86 约定。您可以在this MSDN page 中找到概述。重要的部分是:

任何不适合 8 个字节或不是 1、2、4 或 8 个字节的参数都必须通过引用传递。

x64 编译器在必要时强制执行此要求,创建结构的副本并在程序按值传递此类结构时传递指向它的指针。在这种情况下,pinvoke marshaller 会处理它。所以,没有堆栈不平衡。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    • 1970-01-01
    • 2015-03-19
    • 2020-03-17
    相关资源
    最近更新 更多