Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Соглашения о вызовах описывают низкоуровневые сведения о том, как аргументы метода и возвращаемые значения передаются между вызывающим и вызываемым методом.
Важно, чтобы неуправляемое соглашение о вызове, объявленное в декларации 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);