Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El marshaling es el proceso de transformación de tipos cuando necesitan cruzar entre código administrado y nativo.
La marshalización es necesaria porque los tipos del código administrado y no administrado son diferentes. En código administrado, por ejemplo, tiene una string, mientras que las cadenas no administradas pueden utilizar codificación de .NET string (UTF-16), codificación de página de códigos ANSI, UTF-8, terminadas en null, ASCII, etc. De forma predeterminada, el subsistema P/Invoke intenta actuar correctamente según el comportamiento predeterminado descrito en este artículo. Sin embargo, para aquellas situaciones en las que necesita un control adicional, puede emplear el atributo MarshalAs para especificar cuál es el tipo esperado en el lado no administrado. Por ejemplo, si desea que la cadena se envíe como una cadena UTF-8 terminada en NULL, puede hacerlo de la siguiente manera:
[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);
Si aplica el atributo al ensamblado, no se aplican las reglas de la sección siguiente. Para obtener información sobre cómo se exponen los valores de .NET al código nativo cuando se aplica este atributo, vea marshalling en tiempo de ejecución desactivado.
Reglas predeterminadas para serializar tipos comunes
Por lo general, el entorno de ejecución intenta hacer "lo correcto" al serializar para requerir la menor cantidad de trabajo. En las tablas siguientes se describe cómo se procesa por defecto cada tipo cuando se usa en un parámetro o campo. Los tipos de caracteres y enteros de ancho fijo de C99/C++11 se usan para asegurarse de que la tabla siguiente es correcta para todas las plataformas. Puede usar cualquier tipo nativo que tenga los mismos requisitos de alineación y tamaño que estos tipos.
La primera tabla describe las asignaciones de los tipos para los que el ordenamiento es el mismo tanto para P/Invoke como para el ordenamiento de campos.
Importante
Al llamar a una función de C que usa long, use CLong o CULong (.NET 6+) en lugar de C# long. Para obtener más información y soluciones alternativas para versiones anteriores de .NET, consulte Consideraciones sobre el tipo de datos multiplataforma.
Nota:
El tipo de wchar_t es UTF-16 (2 bytes) en Windows pero está definido por el compilador en otras plataformas, normalmente UTF-32 (4 bytes) en Linux y macOS. Debido a esto, es difícil de usar como ABI multiplataforma única. Al diseñar una API nativa multiplataforma, prefiera con un contrato de codificación claramente definido (por ejemplo, UTF-8) en lugar de .
Nota:
Las cadenas nativas usan la codificación que define la biblioteca o la plataforma. Cuando se llama a una función de C que toma , coincide con la codificación esperada eligiendo la opción de serialización de cadenas correcta, como para UTF-8, para UTF-16 o para otras codificaciones.
| Palabra clave de 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 |
Ya sea o en función de la codificación de P/Invoke o de la estructura. Consulte la documentación del conjunto de caracteres. |
System.Char |
Ya sea o en función de la codificación de P/Invoke o de la estructura. Consulte la documentación del conjunto de caracteres. | |
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
tipos de puntero .NET (por ejemplo, void*) |
void* |
|
| Tipo derivado de | void* |
|
| Tipo derivado de | void* |
|
bool |
System.Boolean |
Tipo Win32 |
decimal |
System.Decimal |
Estructura de COM |
| delegado de .NET | Puntero de función nativa | |
System.DateTime |
Tipo Win32 | |
System.Guid |
Tipo Win32 |
Algunas categorías de serialización tienen distintos valores predeterminados si está serializando un parámetro o una estructura.
| Tipo de .NET | Tipo nativo (parámetro) | Tipo nativo (campo) |
|---|---|---|
| Matriz de .NET | Un puntero al inicio de una matriz de representaciones nativas de los elementos de matriz. | No permitido sin un atributo |
| Una clase con , o | Un puntero a la representación nativa de la clase | Representación nativa de la clase |
En la tabla siguiente se incluyen las reglas de marshalling predeterminadas exclusivas para Windows. En plataformas que no son de Windows, no se pueden serializar estos tipos.
| Tipo de .NET | Tipo nativo (parámetro) | Tipo nativo (campo) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Interfaz COM | No permitido sin un atributo |
System.ArgIterator |
va_list |
No permitida |
System.Collections.IEnumerator |
IEnumVARIANT* |
No permitida |
System.Collections.IEnumerable |
IDispatch* |
No permitida |
System.DateTimeOffset |
que representa el número de tics desde la medianoche del 1 de enero de 1601 | que representa el número de tics desde la medianoche del 1 de enero de 1601 |
Algunos tipos solo se pueden organizar como parámetros y no como campos. Estos tipos se enumeran en la tabla siguiente:
| Tipo de .NET | Tipo nativo (solo parámetro) |
|---|---|
System.Text.StringBuilder |
Ya sea o dependiendo de de P/Invoke. Consulte la documentación del conjunto de caracteres. |
System.ArgIterator |
va_list (solo en Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Si estos valores predeterminados no hacen exactamente lo que desea, puede personalizar cómo se gestionan los parámetros. El artículo de serialización de parámetros le guía a través de cómo personalizar cómo se serializan los distintos tipos de parámetros.
Serialización predeterminada en escenarios COM
Cuando se llama a métodos en objetos COM en .NET, el entorno de ejecución de .NET cambia las reglas de procesamiento de datos predeterminadas para que coincidan con la semántica COM común. En la tabla siguiente se enumeran las reglas que los entornos de ejecución de .NET usan en escenarios COM.
| Tipo de .NET | Tipo nativo (llamadas al método COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Tipos delegados |
_Delegate* en .NET Framework. No permitido en .NET Core y .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| matriz de .NET | SAFEARRAY |
System.String[] |
de |
Serializar clases y estructuras
Otro aspecto de la serialización de tipos es cómo pasar una estructura a un método no administrado. Por ejemplo, algunos de los métodos no administrados requieren una estructura como parámetro. En estos casos, debe crear una estructura correspondiente o una clase en parte administrada del mundo para usarla como parámetro. Pero no basta con definir la clase. También es necesario indicarle al serializador cómo asignar campos de la clase a la estructura no administrada. Aquí el atributo resulta ú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;
}
}
El código anterior muestra un ejemplo sencillo de llamar a una función. La parte interesante está en la línea 13. El atributo especifica que los campos de la clase se deben asignar secuencialmente a la estructura del otro lado (no administrado). Esto significa que la nomenclatura de los campos no es importante, solo su orden es importante, ya que debe corresponder a la estructura no administrada, que se muestra en el ejemplo siguiente:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
A veces, la serialización predeterminada para la estructura no hace lo que necesita. El artículo Personalización de la serialización de estructuras enseña a personalizar el modo de serializar la estructura.