Unmanaged calling conventions
Calling conventions describe low-level details for how method arguments and return values are passed between the caller and the called method.
It's important that the unmanaged calling convention declared in a P/Invoke declaration matches the unmanaged calling convention used by the native implementation. Mismatches in unmanaged calling conventions lead to data corruptions and fatal crashes that require low-level debugging skills to diagnose.
Platform default calling convention
Most platforms use one canonical calling convention and an explicitly specified calling convention is unnecessary in most cases.
For the x86 architecture, the default calling convention is platform specific. Stdcall
("standard call") is the default calling convention on Windows x86
and it is used by most Win32 APIs. Cdecl
is the default calling convention on Linux x86. Windows ports of open-source libraries that
originated on Unix often use the Cdecl
calling convention even on Windows x86. It's necessary to explicitly specify the Cdecl
calling
convention in P/Invoke declarations for interop with these libraries.
For non-x86 architectures, both Stdcall
and Cdecl
calling conventions are treated as the canonical platform default calling convention.
Specifying calling conventions in managed P/Invoke declarations
The calling conventions are specified by types in the System.Runtime.CompilerServices
namespace or their combinations:
- CallConvCdecl
- CallConvFastcall
- CallConvMemberFunction
- CallConvStdcall
- CallConvSuppressGCTransition
- CallConvThiscall
Examples of explicitly specified calling conventions:
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();
Specifying calling conventions in earlier .NET versions
.NET Framework and .NET versions prior to .NET 5 are limited to a subset of calling conventions that can described by the CallingConvention enumeration.
Examples of explicitly specified calling conventions:
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);