Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O marshalling é o processo de transformação de tipos quando eles precisam se cruzar entre código gerenciado e nativo.
O marshalling é necessário porque os tipos são diferentes, no código gerenciado e não gerenciado. No código gerenciado, por exemplo, você tem um string, enquanto cadeias de caracteres não gerenciadas podem ser codificação .NET string (UTF-16), codificação de página de código ANSI, UTF-8, com terminação nula, ASCII etc. Por padrão, o subsistema P/Invoke tenta executar a ação correta com base no comportamento padrão, descrito neste artigo. No entanto, para as situações em que você precisa de controle extra, você pode empregar o atributo MarshalAs para especificar qual é o tipo esperado no lado não gerenciado. Por exemplo, se você quiser que a cadeia de caracteres seja enviada como uma cadeia de caracteres UTF-8 terminada em nulo, você poderá fazer isso da seguinte maneira:
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPUTF8Str)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
Se você aplicar o atributo System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute à montagem, as regras na seção a seguir não se aplicarão. Para obter informações sobre como os valores de .NET são expostos ao código nativo quando esse atributo é aplicado, consulte desabilitação do marshalling de tempo de execução.
Regras padrão para tipos comuns de marshalling
Geralmente, o runtime tenta fazer a "coisa certa" ao realizar marshal para exigir a menor quantidade de trabalho de você. As tabelas a seguir descrevem como cada tipo é processado por padrão quando usado em um parâmetro ou campo. Os tipos de inteiro e caractere de largura fixa do C99/C++11 são usados para garantir que a tabela a seguir esteja correta para todas as plataformas. Você pode usar qualquer tipo nativo que tenha os mesmos requisitos de alinhamento e tamanho que esses tipos.
Esta primeira tabela descreve os mapeamentos de tipos para os quais o registro é o mesmo para P/Invoke e registro de campo.
Importante
Ao chamar uma função C que usa long, use CLong ou CULong (.NET 6+) em vez de C# long. Para obter detalhes e soluções alternativas para versões de .NET anteriores, consulte Considerações sobre tipos de dados entre plataformas.
Observação
O tipo wchar_t é UTF-16 (2 bytes) em Windows mas é definido pelo compilador em outras plataformas— normalmente UTF-32 (4 bytes) no Linux e no macOS. Por isso, wchar_t* é difícil usar como uma única ABI multiplataforma. Ao criar uma API nativa multiplataforma, prefira char* com um contrato de codificação claramente definido (por exemplo, UTF-8) em vez de wchar_t*.
Observação
Cadeias de caracteres nativas char* usam a codificação que a biblioteca ou a plataforma define. Quando você chama uma função C que usa char*, alinhe-se à codificação esperada escolhendo a opção correta de marshalling de strings, como StringMarshalling.Utf8 para UTF-8, StringMarshalling.Utf16 para UTF-16 ou StringMarshalling.Custom para outros tipos de codificação.
| Palavra-chave C# | Tipo de .NET | Tipo nativo |
|---|---|---|
byte |
System.Byte |
uint8_t |
sbyte |
System.SByte |
int8_t |
short |
System.Int16 |
int16_t |
ushort |
System.UInt16 |
uint16_t |
int |
System.Int32 |
int32_t |
uint |
System.UInt32 |
uint32_t |
long |
System.Int64 |
int64_t |
ulong |
System.UInt64 |
uint64_t |
char |
System.Char |
char Ou char16_t dependendo da codificação do P/Invoke ou da estrutura. Consulte a documentação do conjunto de caracteres. |
System.Char |
char* Ou char16_t* dependendo da codificação do P/Invoke ou da estrutura. Consulte a documentação do conjunto de caracteres. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
tipos de ponteiro .NET (por exemplo, void*) |
void* |
|
Tipo derivado de System.Runtime.InteropServices.SafeHandle |
void* |
|
Tipo derivado de System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Tipo Win32 BOOL |
decimal |
System.Decimal |
Struct DECIMAL COM |
| Representante do .NET | Ponteiro de função nativo | |
System.DateTime |
Tipo Win32 DATE |
|
System.Guid |
Tipo Win32 GUID |
Algumas categorias de marshalling têm padrões diferentes se você estiver realizando marshaling como um parâmetro ou estrutura.
| Tipo de .NET | Tipo nativo (parâmetro) | Tipo nativo (campo) |
|---|---|---|
| matriz .NET | Um ponteiro para o início de uma matriz de representações nativas dos elementos da matriz. | Não permitido sem um [MarshalAs] atributo |
Uma classe com um LayoutKind de Sequential ou Explicit |
Um ponteiro para a representação nativa da classe | A representação nativa da classe |
A tabela a seguir inclui as regras de marshalling padrão que são exclusivas para o Windows. Em plataformas não Windows, não é possível realizar marshaling desses tipos.
| Tipo de .NET | Tipo nativo (parâmetro) | Tipo nativo (campo) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Interface COM | Não permitido sem um [MarshalAs] atributo |
System.ArgIterator |
va_list |
Não permitido |
System.Collections.IEnumerator |
IEnumVARIANT* |
Não permitido |
System.Collections.IEnumerable |
IDispatch* |
Não permitido |
System.DateTimeOffset |
int64_t representando o número de tiques desde a meia-noite de 1° de janeiro de 1601 |
int64_t representando o número de tiques desde a meia-noite de 1° de janeiro de 1601 |
Alguns tipos só podem ser empacotados como parâmetros e não como campos. Esses tipos são listados na tabela a seguir:
| Tipo de .NET | Tipo nativo (somente parâmetro) |
|---|---|
System.Text.StringBuilder |
char* ou char16_t* dependendo do CharSet do P/Invoke. Consulte a documentação do conjunto de caracteres. |
System.ArgIterator |
va_list (somente em Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Se esses padrões não fizerem exatamente o que você deseja, personalize como os parâmetros têm o marshal realizado. O artigo sobre processamento de parâmetro explica como personalizar o modo como diferentes tipos de parâmetros são empacotados.
Marshalling padrão em cenários COM
Quando você está chamando métodos em objetos COM em .NET, o runtime .NET altera as regras de marshalling padrão para corresponder à semântica COM comum. A tabela a seguir lista as regras que .NET runtimes usa em cenários COM:
| Tipo de .NET | Tipo nativo (chamadas de método COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Tipos de delegado |
_Delegate* no .NET Framework. Não permitido no .NET Core e .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| matriz .NET | SAFEARRAY |
System.String[] |
SAFEARRAY de BSTRs |
Marshalling de classes e structs
Outro aspecto do marshalling de tipos é como passar um struct para um método não gerenciado. Por exemplo, alguns dos métodos não gerenciados exigem um struct como um parâmetro. Nesses casos, você precisa criar um struct correspondente ou uma classe em parte gerenciada do mundo para usá-lo como parâmetro. No entanto, apenas definir a classe não é suficiente, você também precisa instruir o marshaller como mapear campos na classe para o struct não gerenciado. Aqui, o StructLayout atributo se torna útil.
using System;
using System.Runtime.InteropServices;
Win32Interop.GetSystemTime(out Win32Interop.SystemTime systemTime);
Console.WriteLine(systemTime.Year);
internal static partial class Win32Interop
{
[LibraryImport("kernel32.dll")]
internal static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
internal ref struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Millisecond;
}
}
O código anterior mostra um exemplo simples de chamada à GetSystemTime() função. A parte interessante está na linha 13. O atributo especifica que os campos da classe devem ser mapeados sequencialmente para o struct no outro lado (não gerenciado). Isso significa que a nomenclatura dos campos não é importante, apenas sua ordem é importante, pois ela precisa corresponder ao struct não gerenciado, mostrado no exemplo a seguir:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Às vezes, o marshalling padrão para sua estrutura não faz o que você precisa. O artigo Personalizando o marshalling de estrutura ensina como personalizar a forma como é feito o marshalling de sua estrutura.