Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Marshalling ist der Prozess der Transformation von Typen, wenn sie zwischen verwaltetem und systemeigenem Code wechseln müssen.
Marshalling ist erforderlich, da die Typen im verwalteten und nicht verwalteten Code unterschiedlich sind. In verwaltetem Code haben Sie beispielsweise eine string, während nicht verwaltete Zeichenfolgen .NET string-Codierung (UTF-16), ANSI Code Page-Codierung, UTF-8, null-terminated, ASCII usw. sein können. Standardmäßig versucht das P/Invoke-Subsystem, das richtige Zu tun, basierend auf dem Standardverhalten, das in diesem Artikel beschrieben wird. In situationen, in denen Sie zusätzliche Kontrolle benötigen, können Sie jedoch das MarshalAs-Attribut verwenden, um anzugeben, was der erwartete Typ auf der nicht verwalteten Seite ist. Wenn die Zeichenfolge beispielsweise als NULL-beendete UTF-8-Zeichenfolge gesendet werden soll, können Sie dies wie folgt tun:
[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);
Wenn Sie das System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute Attribut auf die Assembly anwenden, gelten die Regeln im folgenden Abschnitt nicht. Informationen dazu, wie .NET-Werte dem nativen Code zur Verfügung gestellt werden, wenn dieses Attribut angewendet wird, finden Sie unter disabled runtime marshalling.
Standardregeln für das Marshallen von häufig verwendeten Typen
Allgemein versucht die Runtime, beim Marshallen das „Richtige“ zu tun, damit Sie möglichst wenig Arbeitsaufwand haben. In den folgenden Tabellen wird beschrieben, wie jeder Typ standardmäßig gemarshallt wird, wenn er in einem Parameter oder Feld verwendet wird. Die Ganzzahl- und Zeichentypen C99/C++11 mit fester Breite werden verwendet, um sicherzustellen, dass die folgende Tabelle für alle Plattformen korrekt ist. Sie können jeden systemeigenen Typ verwenden, der die gleichen Ausrichtungs- und Größenanforderungen wie diese Typen aufweist.
Die erste Tabelle beschreibt die Zuordnungen für Typen, für die das Marshallen für P/Invoke und Feldmarshalling gleich ist.
Von Bedeutung
Verwenden Sie beim Aufrufen einer C-Funktion, die long verwendet, CLong oder CULong (.NET 6+) anstelle von C# long. Ausführliche Informationen und Problemumgehungen für frühere .NET-Versionen finden Sie unter Cross-Platform-Datentypüberlegungen.
Hinweis
Der typ wchar_t ist UTF-16 (2 Bytes) auf Windows, ist aber auf anderen Plattformen compilerdefiniert – in der Regel UTF-32 (4 Byte) unter Linux und macOS. Aus diesem Grund ist es schwierig, wchar_t* als eine plattformübergreifende ABI zu verwenden. Wenn Sie eine plattformübergreifende native API entwerfen, bevorzugen Sie char* mit einem klar definierten Codierungsvertrag (z. B. UTF-8) anstelle von wchar_t*.
Hinweis
Systemeigene char* Zeichenfolgen verwenden die Codierung, die von der Bibliothek oder Plattform definiert wird. Wenn Sie eine C-Funktion aufrufen, die das char* verwendet, stimmen Sie mit der erwarteten Codierung überein, indem Sie die richtige Zeichenfolgen-Marshallingoption auswählen, z.B. StringMarshalling.Utf8 für UTF-8, StringMarshalling.Utf16 für UTF-16 oder StringMarshalling.Custom für andere Codierungen.
| C#-Schlüsselwort | .NET Typ | Nativer 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 |
Entweder char oder char16_t abhängig von der Codierung des P/Invoke oder der Struktur. Weitere Informationen finden Sie in der Zeichensatzdokumentation. |
System.Char |
Entweder char* oder char16_t* abhängig von der Codierung des P/Invoke oder der Struktur. Weitere Informationen finden Sie in der Zeichensatzdokumentation. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET Zeigertypen (z. B. void*) |
void* |
|
Abgeleitet vom Typ System.Runtime.InteropServices.SafeHandle |
void* |
|
Abgeleitet vom Typ System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Win32-Typ BOOL |
decimal |
System.Decimal |
COM-DECIMAL-Struktur |
| .NET Stellvertreter | Systemeigener Funktionszeiger | |
System.DateTime |
Win32-Typ DATE |
|
System.Guid |
Win32-Typ GUID |
Einige Marshallingkategorien weisen unterschiedliche Standardwerte auf, wenn Sie das Marshalling als Parameter oder Struktur durchführen.
| .NET Typ | Nativer Typ (Parameter) | Nativer Typ (Feld) |
|---|---|---|
| .NET Array | Ein Zeiger auf den Anfang eines Arrays aus nativen Darstellungen der Arrayelemente | Ohne Attribut [MarshalAs] nicht zulässig |
Eine Klasse mit einem LayoutKind-Wert vom Typ Sequential oder Explicit |
Ein Zeiger auf die native Darstellung der Klasse | Die native Darstellung der Klasse |
Die folgende Tabelle enthält die Standard-Marshallingregeln, die nur für Windows gelten. Auf Nicht-Windows-Plattformen können Sie diese Typen nicht marshallen.
| .NET Typ | Nativer Typ (Parameter) | Nativer Typ (Feld) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM-Schnittstelle | Ohne Attribut [MarshalAs] nicht zulässig |
System.ArgIterator |
va_list |
Nicht zulässig |
System.Collections.IEnumerator |
IEnumVARIANT* |
Nicht zulässig |
System.Collections.IEnumerable |
IDispatch* |
Nicht zulässig |
System.DateTimeOffset |
int64_t – repräsentiert die Anzahl von Takten seit dem 1. Januar 1601 um Mitternacht |
int64_t – repräsentiert die Anzahl von Takten seit dem 1. Januar 1601 um Mitternacht |
Einige Typen können nur als Parameter und nicht als Felder gemarshallt werden. Diese Typen sind in der folgenden Tabelle aufgeführt:
| .NET Typ | Nativer Typ (nur Parameter) |
|---|---|
System.Text.StringBuilder |
Entweder char* oder char16_t*, je nach CharSet des P/Invoke. Weitere Informationen finden Sie in der Zeichensatzdokumentation. |
System.ArgIterator |
va_list (nur auf Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Wenn diese Standardeinstellungen nicht genau das tun, was Sie möchten, können Sie anpassen, wie Parameter zugeordnet werden. Der Artikel zum Marshalling von Parametern führt Sie durch das Anpassen, wie verschiedene Parametertypen ge marshallt werden.
Standardmarshalling in COM-Szenarios
Wenn Sie Methoden für COM-Objekte in .NET aufrufen, ändert die .NET Laufzeit die Standard-Marshallregeln so, dass sie mit allgemeinen COM-Semantiken übereinstimmen. In der folgenden Tabelle sind die Regeln aufgeführt, die die .NET-Laufzeit in COM-Szenarien verwendet:
| .NET Typ | Nativer Typ (COM-Methodenaufrufe) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Delegattypen |
_Delegate* im .NET Framework. Unzulässig in .NET Core und .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| .NET Array | SAFEARRAY |
System.String[] |
SAFEARRAY aus BSTRs |
Marshallen von Klassen und Strukturen
Ein weiterer Aspekt des Marshallens von Typen ist die Übergabe einer Struktur an eine nicht verwaltete Methode. Beispielsweise erfordern einige der nicht verwalteten Methoden eine Struktur als Parameter. In diesen Fällen müssen Sie eine entsprechende Struktur oder eine Klasse in verwalteten Teilen der Welt erstellen, um sie als Parameter zu verwenden. Das Definieren der Klasse reicht jedoch nicht aus, Sie müssen auch den Marshaller anweisen, wie Felder in der Klasse der nicht verwalteten Struktur zugeordnet werden. Hier wird das StructLayout Attribut nützlich.
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;
}
}
Der vorherige Code zeigt ein einfaches Beispiel für das Aufrufen der GetSystemTime() Funktion. Das interessante Bit befindet sich in Zeile 13. Das Attribut gibt an, dass die Felder der Klasse sequenziell der Struktur auf der anderen Seite (nicht verwaltet) zugeordnet werden sollen. Dies bedeutet, dass die Benennung der Felder nicht wichtig ist, nur ihre Reihenfolge ist wichtig, da sie der nicht verwalteten Struktur entsprechen muss, wie im folgenden Beispiel gezeigt:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Manchmal führt das standardmäßige Marshalling für Ihre Struktur nicht zum gewünschten Ergebnis. Im Artikel Anpassen des Marshallens für Strukturen erfahren Sie, wie Sie das Marshalling für Ihre Struktur anpassen.