Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Marshalling je proces transformace typů, když potřebují přecházet mezi spravovaným a nativním kódem.
Marshalling je potřeba, protože typy ve spravovaném a nespravovaném kódu se liší. Ve spravovaném stringkódu máte například , zatímco nespravované řetězce mohou být kódování .NET string (UTF-16), kódování znakové stránky ANSI, UTF-8, null-terminated, ASCII atd. Ve výchozím nastavení se subsystém P/Invoke pokusí provést správnou věc na základě výchozího chování popsaného v tomto článku. V takových situacích, kdy potřebujete další kontrolu, ale můžete použít atribut MarshalAs k určení očekávaného typu na nespravované straně. Pokud například chcete, aby se řetězec odeslal jako řetězec UTF-8 s ukončenou hodnotou null, můžete to udělat takto:
[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);
Pokud použijete System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute atribut na sestavení, pravidla v následující části se nevztahují. Informace o tom, jak jsou hodnoty .NET při použití tohoto atributu dostupné nativnímu kódu, najdete v tématu zakázané zařazování za běhu.
Výchozí pravidla pro zařazování běžných typů
Obecně platí, že modul runtime se pokusí provést "správnou věc" při zařazování, aby vyžadoval nejnižší množství práce od vás. Následující tabulky popisují, jak se každý typ ve výchozím nastavení zpracovává, když je použit v parametru nebo poli. Celočíselné a znakové typy C99/C++11 s pevnou šířkou se používají k zajištění správnosti následující tabulky pro všechny platformy. Můžete použít libovolný nativní typ, který má stejné požadavky na zarovnání a velikost jako tyto typy.
Tato první tabulka popisuje mapování pro typy, pro které je zprostředkování stejné pro volání platformy i zprostředkování polí.
| Klíčové slovo jazyka C# | Typ .NET | Nativní 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 |
Buď char nebo char16_t v závislosti na kódování P/Invoke nebo struktury. Podívejte se na dokumentaci ke znakové sadě. |
System.Char |
Buď char* nebo char16_t* v závislosti na kódování P/Invoke nebo struktury. Podívejte se na dokumentaci ke znakové sadě. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
Typy ukazatelů .NET (např. void*) |
void* |
|
Typ odvozený z System.Runtime.InteropServices.SafeHandle |
void* |
|
Typ odvozený z System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Typ Win32 BOOL |
decimal |
System.Decimal |
COM DECIMAL – struktura |
| Delegát .NET | Ukazatel nativní funkce | |
System.DateTime |
Typ Win32 DATE |
|
System.Guid |
Typ Win32 GUID |
Pokud řazování zařazujete jako parametr nebo strukturu, má několik kategorií řazení jiné výchozí hodnoty.
| Typ .NET | Nativní typ (parametr) | Nativní typ (pole) |
|---|---|---|
| Pole .NET | Ukazatel na začátek pole s nativními reprezentacemi jeho prvků. | Nepovoleno bez atributu [MarshalAs] |
Třída s LayoutKindSequentialExplicit |
Ukazatel na nativní reprezentaci třídy | Nativní reprezentace třídy |
Následující tabulka obsahuje výchozí pravidla zprostředkování, která jsou pouze pro Windows. Na jiných platformách než Windows nemůžete tyto typy zařašovat.
| Typ .NET | Nativní typ (parametr) | Nativní typ (pole) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Rozhraní COM | Nepovoleno bez atributu [MarshalAs] |
System.ArgIterator |
va_list |
Nepovoleno |
System.Collections.IEnumerator |
IEnumVARIANT* |
Nepovoleno |
System.Collections.IEnumerable |
IDispatch* |
Nepovoleno |
System.DateTimeOffset |
int64_t představující počet ticků od půlnoci 1. ledna 1601 |
int64_t představující počet ticků od půlnoci 1. ledna 1601 |
Některé typy lze zařaďovat pouze jako parametry, nikoli jako pole. Tyto typy jsou uvedeny v následující tabulce:
| Typ .NET | Nativní typ (pouze parametr) |
|---|---|
System.Text.StringBuilder |
Buď char* nebo char16_t* v závislosti na CharSet v rámci P/Invoke volání. Viz dokumentace ke znakové sadě. |
System.ArgIterator |
va_list (jenom ve Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Pokud tyto výchozí hodnoty nedělají přesně to, co potřebujete, můžete přizpůsobit, jak se parametry zařaďují. Článek o zařazování parametrů vás provede přizpůsobením způsobu zařazování různých typů parametrů.
Výchozí zařazování ve scénářích modelu COM
Při volání metod pro objekty COM v .NET runtime .NET změní výchozí pravidla maršálování tak, aby odpovídala běžné sémantice COM. Následující tabulka uvádí pravidla, která prostředí runtime .NET používají ve scénářích COM.
| Typ .NET | Nativní typ (volání metod COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Typy delegátů |
_Delegate* v rozhraní .NET Framework. Zakázáno v .NET Core a .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| Pole .NET | SAFEARRAY |
System.String[] |
SAFEARRAY z BSTR |
Zařazování tříd a struktur
Dalším aspektem zařazování typů je předání struktury nespravované metodě. Například některé nespravované metody vyžadují strukturu jako parametr. V těchto případech potřebujete vytvořit odpovídající strukturu nebo třídu ve spravované části světa, abyste ji mohli použít jako parametr. Pro definování třídy však nestačí, také musíte dát instrukci marshalleru, jak mapovat pole ve třídě na nespravovanou strukturu. Tady se StructLayout atribut stane užitečným.
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;
}
}
Předchozí kód ukazuje jednoduchý příklad volání do GetSystemTime() funkce. Zajímavá část je na řádku 13. Atribut určuje, že pole třídy by měla být postupně namapována na strukturu na druhé straně, která je nespravovaná. To znamená, že pojmenování polí není důležité, pouze jejich pořadí je důležité, protože musí odpovídat nespravované struktuře, jak je znázorněno v následujícím příkladu:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Někdy výchozí mapování pro vaši strukturu nefunguje podle vašich potřeb. Článek Přizpůsobení struktury maršálováním vás naučí, jak přizpůsobit způsob maršálování struktury.