Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Marshalling är processen för att transformera typer när de behöver korsa mellan hanterad och intern kod.
Marshalling krävs eftersom typerna i den hanterade och ohanterade koden skiljer sig åt. I hanterad kod har du till exempel en string, medan ohanterade strängar kan vara .NET string-kodning (UTF-16), ANSI-kodsidokodning, UTF-8, nollterminerade, ASCII osv. Som standard försöker delsystemet P/Invoke agera korrekt baserat på standardbeteende, som beskrivs i den här artikeln. För de situationer där du behöver extra kontroll kan du dock använda attributet MarshalAs för att ange vilken typ som förväntas på den ohanterade sidan. Om du till exempel vill att strängen ska skickas som en null-avslutad UTF-8-sträng kan du göra så här:
[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);
Om du använder System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute attributet för sammansättningen gäller inte reglerna i följande avsnitt. Information om hur .NET värden exponeras för intern kod när det här attributet tillämpas finns i disabled runtime marshalling.
Standardregler för sortering av vanliga typer
I allmänhet försöker runtime-miljön göra det "rätta" när den serialiserar för att du ska behöva göra så lite arbete som möjligt. I följande tabeller beskrivs hur varje typ ordnas som standard när den används i en parameter eller ett fält. Heltals- och teckentyperna C99/C++11 med fast bredd används för att säkerställa att följande tabell är korrekt för alla plattformar. Du kan använda alla inbyggda typer som har samma justerings- och storlekskrav som dessa typer.
Den här första tabellen beskriver mappningarna för typer för vilka marshallingen är densamma för både P/Invoke och field marshalling.
Viktigt!
När du anropar en C-funktion som använder long använder du CLong eller CULong (.NET 6+) i stället för C# long. Mer information och lösningar för tidigare .NET-versioner finns i Överväganden för datatyper mellan plattformar.
Anmärkning
Typen wchar_t är UTF-16 (2 byte) på Windows men är kompilatordefinierad på andra plattformar – vanligtvis UTF-32 (4 byte) på Linux och macOS.
wchar_t* Därför är det svårt att använda som en enda plattformsoberoende ABI. När du utformar ett plattformsoberoende inbyggt API, föredra char* med ett tydligt definierat kodningskontrakt (till exempel UTF-8) i stället för wchar_t*.
Anmärkning
Interna char* strängar använder den kodning som biblioteket eller plattformen definierar. När du anropar en C-funktion som tar char*, matcha den förväntade kodningen genom att välja rätt alternativ för strängmarshalling, till exempel StringMarshalling.Utf8 för UTF-8, StringMarshalling.Utf16 för UTF-16 eller StringMarshalling.Custom för andra kodningar.
| C#-nyckelord | .NET typ | Ursprunglig typ |
|---|---|---|
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 |
Antingen char eller char16_t beroende på kodningen av P/Invoke eller strukturen. Se teckenuppsättningsdokumentationen. |
System.Char |
Antingen char* eller char16_t* beroende på kodningen av P/Invoke eller strukturen. Se teckenuppsättningsdokumentationen. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET pekartyper (till ex. void*) |
void* |
|
Typ härledd från System.Runtime.InteropServices.SafeHandle |
void* |
|
Typ härledd från System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Win32-typ BOOL |
decimal |
System.Decimal |
COM DECIMAL struct |
| .NET-delegat | Inbyggd funktionspekare | |
System.DateTime |
Win32-typ DATE |
|
System.Guid |
Win32-typ GUID |
Några kategorier av marshalling har olika standardvärden om du marshallar som parameter eller struktur.
| .NET typ | Ursprunglig typ (parameter) | Inbyggd typ (fält) |
|---|---|---|
| .NET-array | En pekare till början av en matris med inbyggda representationer av matriselementen. | Tillåts inte utan ett [MarshalAs] attribut |
En klass med LayoutKind av Sequential eller Explicit |
En pekare till den inbyggda representationen av klassen | Den inbyggda representationen av klassen |
Följande tabell innehåller standardregler för marshalling som endast gäller för Windows. På plattformar som inte är Windows kan du inte hantera dessa typer.
| .NET typ | Ursprunglig typ (parameter) | Inbyggd typ (fält) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM-gränssnitt | Tillåts inte utan ett [MarshalAs] attribut |
System.ArgIterator |
va_list |
Tillåts inte |
System.Collections.IEnumerator |
IEnumVARIANT* |
Tillåts inte |
System.Collections.IEnumerable |
IDispatch* |
Tillåts inte |
System.DateTimeOffset |
int64_t representerar antalet fästingar sedan midnatt den 1 januari 1601 |
int64_t representerar antalet fästingar sedan midnatt den 1 januari 1601 |
Vissa typer kan bara ordnas som parametrar och inte som fält. Dessa typer visas i följande tabell:
| .NET typ | Inbyggd typ (endast parameter) |
|---|---|
System.Text.StringBuilder |
Antingen char* eller char16_t* beroende på CharSet P/Invoke. Se teckenuppsättningsdokumentationen. |
System.ArgIterator |
va_list (endast på Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Om dessa standardvärden inte gör exakt vad du vill kan du anpassa hur parametrarna är ordnade. Artikeln om parameter marshalling visar hur du kan anpassa hur olika parametertyper hanteras.
Förvald marshalling i COM-scenarier
När du anropar metoder för COM-objekt i .NET ändrar .NET-körningen standardreglerna för marshalling så att de matchar vanliga COM-semantik. I följande tabell visas de regler som .NET-körmiljöer använder i COM-miljöer.
| .NET typ | Inbyggd typ (COM-metodanrop) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Delegattyper |
_Delegate* i .NET Framework. Tillåts inte i .NET Core och .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| .NET-array | SAFEARRAY |
System.String[] |
SAFEARRAY av BSTRs |
Rangeringsklasser och strukturer
En annan aspekt av typmarshallning är hur man överför en struktur till en oövervakad metod. Vissa av de ohanterade metoderna kräver till exempel en struct som parameter. I dessa fall måste du skapa en motsvarande struct eller en klass i en hanterad del av världen för att använda den som en parameter. Men det räcker inte att bara definiera klassen. Du måste också instruera marshallern hur fälten i klassen ska mappas till den ohanterade structen. Här blir attributet StructLayout användbart.
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;
}
}
Föregående kod visar ett enkelt exempel på anrop till GetSystemTime() funktionen. Den intressanta biten är på rad 13. Attributet anger att fälten i klassen ska mappas sekventiellt till structen på den andra (ohanterade) sidan. Det innebär att namngivning av fälten inte är viktigt, bara deras ordning är viktig, eftersom den måste motsvara den ohanterade struct som visas i följande exempel:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Ibland gör standard-marshalling för din struktur inte det du behöver. I artikeln Anpassning av strukturmarshaling lär du dig hur du anpassar hur din struktur överförs.