Convenções de chamadas não gerenciadas

As convenções de chamada descrevem detalhes de baixo nível sobre como os argumentos do método e os valores de retorno são passados entre o chamador e o método chamado.

É importante que a convenção de chamada não gerenciada declarada em uma declaração P/Invoke corresponda à convenção de chamada não gerenciada usada pela implementação nativa. Incompatibilidades em convenções de chamadas não gerenciadas levam a corrupções de dados e falhas fatais que exigem habilidades de depuração de baixo nível para diagnóstico.

Convenção de chamada padrão da plataforma

A maioria das plataformas usa uma convenção de chamada canônica e uma convenção de chamada explicitamente especificada é desnecessária na maioria dos casos.

Para a arquitetura x86, a convenção de chamada padrão é específica da plataforma. Stdcall ("chamada padrão") é a convenção padrão de chamadas no Windows x86 e é usada pela maioria das APIs Win32. Cdecl é a convenção de chamada padrão no Linux x86. Portas Windows de bibliotecas de código aberto que se originaram no Unix usam frequentemente a convenção de chamadas Cdecl mesmo em plataformas Windows x86. É necessário especificar explicitamente a Cdecl convenção de chamada nas declarações P/Invoke para interoperabilidade com essas bibliotecas.

Para arquiteturas não-x86, as convenções de chamada Stdcall e Cdecl são tratadas como a convenção de chamada padrão canónica da plataforma.

Quando você pode omitir a convenção de chamada

Nas arquiteturas x64, ARM e ARM64, existe apenas uma convenção de chamada, pelo que especificar uma explicitamente é desnecessário. Só precisa de especificar uma convenção de chamada ao direcionar Windows x86 (32 bits), onde Stdcall e Cdecl diferem.

  • ✔️ Especifique explicitamente a convenção de chamada quando o alvo for Windows x86.
  • ✔️ O DO omite a convenção de chamadas em x64, ARM e ARM64 — o atributo não tem efeito nestas arquiteturas.

Especificando convenções de chamada em declarações P/Invoke gerenciadas

As convenções de chamada são especificadas por tipos no System.Runtime.CompilerServices namespace ou suas combinações:

Exemplos de convenções de chamada explicitamente especificadas:

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();

Especificação das convenções de chamada em versões anteriores de .NET

.NET Framework e .NET versões anteriores ao .NET 5 estão limitadas a um subconjunto de convenções de chamada que podem ser descritas pela enumeração CallingConvention.

Exemplos de convenções de chamada explicitamente especificadas:

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);

Ver também