Неуправляемые соглашения о вызовах

Соглашения о вызовах описывают низкоуровневые сведения о том, как аргументы метода и возвращаемые значения передаются между вызывающим и вызываемым методом.

Важно, чтобы неуправляемое соглашение о вызовах, объявленное в объявлении P/Invoke, соответствовало неуправляемой соглашению о вызове, используемому собственной реализацией. Несоответствия в неуправляемых соглашениях о вызовах приводят к повреждению данных и смертельным сбоям, которые требуют низкоуровневых навыков отладки для диагностики.

Соглашение о вызовах платформы по умолчанию

Большинство платформ используют одно каноническое соглашение о вызове, и явно указанное соглашение о вызове не требуется в большинстве случаев.

Для архитектуры x86 соглашение о вызовах по умолчанию зависит от платформы. Stdcall ("Стандартный вызов") — это соглашение о вызовах по умолчанию в Windows x86, которое используется большинством API Win32. Cdecl — это соглашение о вызовах по умолчанию в Linux x86. Порты Windows библиотек с открытым исходным кодом, возникшие в Unix, часто используют Cdecl соглашение о вызовах даже в Windows x86. Необходимо явно указать Cdecl соглашение о вызовах в объявлениях P/Invoke для взаимодействия с этими библиотеками.

Для архитектур, отличных от x86, StdcallCdecl и соглашения о вызовах рассматриваются как каноническое соглашение о вызове платформы по умолчанию.

Указание соглашений о вызовах в объявлениях управляемого P/Invoke

Соглашения о вызовах задаются типами в System.Runtime.CompilerServices пространстве имен или их сочетаниями:

Примеры явно указанных соглашений о вызовах:

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 до .NET 5 ограничены подмножеством соглашений о вызовах, которые могут быть описаны в разделе Перечисление 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);

См. также