在32位中编组结构时的行为与64位运行时中的行为不同

Chr*_*ris 1 c# pinvoke winapi marshalling data-structures

我在PInvoking SetupDiCreateDeviceInfoList时发现了这个.

C++函数签名是:

HDEVINFO SetupDiCreateDeviceInfoList(
  _In_opt_ const GUID *ClassGuid,
  _In_opt_       HWND hwndParent
);
Run Code Online (Sandbox Code Playgroud)

在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;
}
Run Code Online (Sandbox Code Playgroud)

和这样的功能:

[DllImport("Setupapi.dll")]
public static extern IntPtr SetupDiCreateDeviceInfoList(GUID ClassGuid, IntPtr hwndParent);
Run Code Online (Sandbox Code Playgroud)

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

GUID classGuid = new GUID();
IntPtr deviceInfoSet = SetupDiCreateDeviceInfoList(classGuid, IntPtr.Zero);
Run Code Online (Sandbox Code Playgroud)

我收到一个错误:

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);
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 6

x64调用约定与x86约定非常不同.您将在此MSDN页面中找到概述.必不可少的部分是:

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

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