Неуправляемые соглашения о вызовах
Соглашения о вызовах описывают низкоуровневые сведения о том, как аргументы метода и возвращаемые значения передаются между вызывающим и вызываемым методом.
Важно, чтобы неуправляемое соглашение о вызовах, объявленное в объявлении P/Invoke, соответствовало неуправляемой соглашению о вызове, используемому собственной реализацией. Несоответствия в неуправляемых соглашениях о вызовах приводят к повреждению данных и смертельным сбоям, которые требуют низкоуровневых навыков отладки для диагностики.
Соглашение о вызовах платформы по умолчанию
Большинство платформ используют одно каноническое соглашение о вызове, и явно указанное соглашение о вызове не требуется в большинстве случаев.
Для архитектуры x86 соглашение о вызовах по умолчанию зависит от платформы. Stdcall
("Стандартный вызов") — это соглашение о вызовах по умолчанию в Windows x86, которое используется большинством API Win32. Cdecl
— это соглашение о вызовах по умолчанию в Linux x86. Порты Windows библиотек с открытым исходным кодом, возникшие в Unix, часто используют Cdecl
соглашение о вызовах даже в Windows x86. Необходимо явно указать Cdecl
соглашение о вызовах в объявлениях P/Invoke для взаимодействия с этими библиотеками.
Для архитектур, отличных от 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 до .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);