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