Typ marshalling
Marshalling is het proces van het transformeren van typen wanneer ze moeten kruisen tussen beheerde en systeemeigen code.
Marshalling is nodig omdat de typen in de beheerde en onbeheerde code verschillen. In beheerde code hebt u bijvoorbeeld een string
, terwijl niet-beheerde tekenreeksen .NET-codering string
(UTF-16), ANSI-codepaginacodering, UTF-8, null-beëindigd, ASCII, enzovoort kunnen zijn. Standaard probeert het subsysteem P/Invoke het juiste te doen op basis van het standaardgedrag, zoals beschreven in dit artikel. Voor situaties waarin u echter extra controle nodig hebt, kunt u het kenmerk MarshalAs gebruiken om op te geven wat het verwachte type aan de onbeheerde zijde is. Als u bijvoorbeeld wilt dat de tekenreeks wordt verzonden als een door null beëindigde UTF-8-tekenreeks, kunt u dit als volgt doen:
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPStr)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
Als u het System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
kenmerk toepast op de assembly, zijn de regels in de volgende sectie niet van toepassing. Zie uitgeschakelde runtime-marshalling voor informatie over hoe .NET-waarden worden blootgesteld aan systeemeigen code wanneer dit kenmerk wordt toegepast.
Standaardregels voor het marshallen van algemene typen
Over het algemeen probeert de runtime het 'juiste' te doen wanneer marshalling de minste hoeveelheid werk van u vereist. In de volgende tabellen wordt beschreven hoe elk type standaard wordt ge marshalld wanneer dit wordt gebruikt in een parameter of veld. Het geheel getal met vaste breedte en tekentypen C99/C++11 worden gebruikt om ervoor te zorgen dat de volgende tabel juist is voor alle platforms. U kunt elk systeemeigen type gebruiken dat dezelfde uitlijnings- en groottevereisten heeft als deze typen.
In deze eerste tabel worden de toewijzingen beschreven voor verschillende typen voor wie de marshalling hetzelfde is voor zowel P/Invoke als veld marshalling.
C#-trefwoord | .NET-type | Systeemeigen type |
---|---|---|
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 |
char16_t Of char afhankelijk van de codering van de P/Invoke of structuur. Zie de documentatie over charset. |
System.Char |
char16_t* Of char* afhankelijk van de codering van de P/Invoke of structuur. Zie de documentatie over charset. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET-aanwijzertypen (bijvoorbeeld void* ) |
void* |
|
Type afgeleid van System.Runtime.InteropServices.SafeHandle |
void* |
|
Type afgeleid van System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Win32-type BOOL |
decimal |
System.Decimal |
COM-struct DECIMAL |
.NET-gemachtigde | Systeemeigen functiepointer | |
System.DateTime |
Win32-type DATE |
|
System.Guid |
Win32-type GUID |
Een aantal categorieën marshalling hebben verschillende standaardwaarden als u marshallt als parameter of structuur.
.NET-type | Systeemeigen type (parameter) | Systeemeigen type (veld) |
---|---|---|
.NET-matrix | Een aanwijzer naar het begin van een matrix met systeemeigen weergaven van de matrixelementen. | Niet toegestaan zonder kenmerk [MarshalAs] |
Een klas met een LayoutKind of Sequential Explicit |
Een aanwijzer naar de systeemeigen weergave van de klasse | De systeemeigen weergave van de klasse |
De volgende tabel bevat de standaard marshallregels die alleen windows zijn. Op niet-Windows-platforms kunt u deze typen niet marshalen.
.NET-type | Systeemeigen type (parameter) | Systeemeigen type (veld) |
---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM-interface | Niet toegestaan zonder kenmerk [MarshalAs] |
System.ArgIterator |
va_list |
Niet toegestaan |
System.Collections.IEnumerator |
IEnumVARIANT* |
Niet toegestaan |
System.Collections.IEnumerable |
IDispatch* |
Niet toegestaan |
System.DateTimeOffset |
int64_t het aantal tikken sinds middernacht op 1 januari 1601 |
int64_t het aantal tikken sinds middernacht op 1 januari 1601 |
Sommige typen kunnen alleen worden ge marshalld als parameters en niet als velden. Deze typen worden vermeld in de volgende tabel:
.NET-type | Systeemeigen type (alleen parameter) |
---|---|
System.Text.StringBuilder |
char16_t* Of char* afhankelijk van de CharSet P/Invoke. Zie de documentatie over charset. |
System.ArgIterator |
va_list (alleen op Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Als deze standaardinstellingen niet precies doen wat u wilt, kunt u aanpassen hoe parameters worden marshalled. In het artikel over het marshallen van parameters leert u hoe u kunt aanpassen hoe verschillende parametertypen worden marshalled.
Standaard marshalling in COM-scenario's
Wanneer u methoden aanroept voor COM-objecten in .NET, wijzigt de .NET-runtime de standaard marshallregels zodat deze overeenkomen met algemene COM-semantiek. De volgende tabel bevat de regels die .NET-runtimes gebruiken in COM-scenario's:
.NET-type | Systeemeigen type (COM-methode-aanroepen) |
---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
Typen gemachtigden | _Delegate* in .NET Framework. Niet toegestaan in .NET Core en .NET 5+. |
System.Drawing.Color |
OLECOLOR |
.NET-matrix | SAFEARRAY |
System.String[] |
SAFEARRAY van BSTR s |
Marshallklassen en structs
Een ander aspect van type marshalling is het doorgeven van een struct aan een onbeheerde methode. Voor sommige onbeheerde methoden is bijvoorbeeld een struct als parameter vereist. In deze gevallen moet u een bijbehorende struct of een klasse in een beheerd deel van de wereld maken om deze als parameter te gebruiken. Het is echter niet voldoende om alleen de klasse te definiëren, maar u moet ook de marshaller instrueren hoe u velden in de klasse toewijst aan de onbeheerde struct. Hier wordt het StructLayout
kenmerk nuttig.
[LibraryImport("kernel32.dll")]
static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
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;
}
public static void Main(string[] args)
{
SystemTime st = new SystemTime();
GetSystemTime(st);
Console.WriteLine(st.Year);
}
In de vorige code ziet u een eenvoudig voorbeeld van het aanroepen GetSystemTime()
van functies. De interessante bit staat op regel 4. Het kenmerk geeft aan dat de velden van de klasse opeenvolgend moeten worden toegewezen aan de struct aan de andere (onbeheerde) zijde. Dit betekent dat de naamgeving van de velden niet belangrijk is, alleen hun volgorde is belangrijk, omdat deze moet overeenkomen met de onbeheerde struct, zoals weergegeven in het volgende voorbeeld:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
Soms doet de standaard marshalling voor uw structuur niet wat u nodig hebt. In het artikel Aanpassingsstructuur marshalling leert u hoe u kunt aanpassen hoe uw structuur wordt marshalled.