非托管调用约定
调用约定将描述有关方法参数和返回值如何在调用方与调用方法之间传递的低级别详细信息。
重要的是,P/Invoke 声明中声明的非托管调用约定需要与本机实现使用的非托管调用约定相匹配。 非托管调用约定中的不匹配会导致数据损坏和严重崩溃,这需要低级别的调试技能进行诊断。
平台默认调用约定
大多数平台都会使用一个标准的调用约定,因此在大多数情况下,都不需要显式指定调用约定。
对于 x86 体系结构,默认的调用约定时特定于平台的。 Stdcall
(“标准调用”)是 Windows x86 上的默认调用约定,大多数 Win32 API 都使用该调用约定。 Cdecl
是 Linux x86 上的默认调用约定。 源自 Unix 的开源库的 Windows 端口通常使用 Cdecl
调用约定,即使在 Windows x86 上也是如此。 必须在 P/Invoke 声明中显式指定 Cdecl
调用约定,以便与这些库实现互操作。
对于非 x86 体系结构,Stdcall
和 Cdecl
调用约定都被视为标准的平台默认调用约定。
在托管 P/Invoke 声明中指定调用约定
调用约定由 System.Runtime.CompilerServices
命名空间中的类型或其组合指定:
- CallConvCdecl
- CallConvFastcall
- CallConvMemberFunction
- CallConvStdcall
- CallConvSuppressGCTransition
- CallConvThiscall
显式指定的调用约定的示例:
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
// P/Invoke declaration using SuppressGCTransition calling convention.
[LibraryImport("kernel32.dll")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSuppressGCTransition) })]
extern static ulong GetTickCount64();
// Unmanaged callback with Cdecl calling convention.
[UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl) })]
static unsafe int NativeCallback(void* context);
// Method returning function pointer with combination of Cdecl and MemberFunction calling conventions.
static unsafe delegate* unmanaged[Cdecl, MemberFunction]<int> GetHandler();
在早期 .NET 版本中指定调用约定
.NET Framework 和 .NET 5 之前的 .NET 版本仅限于一小部分可由 CallingConvention 枚举描述的调用约定。
显式指定的调用约定的示例:
using System.Runtime.InteropServices;
// P/Invoke declaration using Cdecl calling convention
[DllImport("ucrtbase.dll", CallingConvention=CallingConvention.Cdecl)]
static void* malloc(UIntPtr size);
// Delegate marshalled as callback with Cdecl calling convention
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void Callback(IntPtr context);