Anteckning
Å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-kodning string (UTF-16), kodning av ANSI-kodsida, UTF-8, null-terminerad, ASCII osv. Som standard försöker P/Invoke-undersystemet att 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 inbyggd kod när det här attributet tillämpas finns i avsnittet om inaktiverad 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.
| 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 dokumentationen om teckenuppsättningen. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET-pekartyper (till exempel 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-struct DECIMAL |
| .NET-ombud | 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-matris | 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 förvalda regler för marshalling som enbart gäller för Windows. På plattformar som inte är Windows kan du inte konvertera 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 dokumentationen om teckenuppsättning. |
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-runtimes använder i COM-scenarier:
| .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-matris | 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.