我在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);
答案 0 :(得分:5)
x64调用约定非常与x86约定不同。您可以在this MSDN page中找到概述。必不可少的部分是:
任何不适合8个字节或不是1,2,4或8个字节的参数必须通过引用传递。
x64编译器在必要时强制执行此要求,创建结构的副本,并在程序按值传递此类结构时向其传递指针。在这种情况下,pinvoke marshaller负责处理它。所以,没有堆栈不平衡。