Delen via


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 SequentialExplicit 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 BSTRs

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.