Zařazování typů
Marshalling je proces transformace typů, když potřebují křížit mezi spravovaným a nativním kódem.
Seřazování je potřeba, protože typy ve spravovaném a nespravovaném kódu se liší. Ve spravovaném string
kó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.LPStr)] 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 vystaveny nativnímu kódu, najdete v tématu zařazování zablokovaného modulu runtime.
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 je každý typ ve výchozím nastavení při 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 různé typy, pro které je seřaďování stejné pro přiřazování nespravovaného kódu i pro řazení 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í volání nespravovaného kódu nebo struktury. Viz dokumentace ke znakové sadě. |
System.Char |
Buď char* nebo char16_t* v závislosti na kódování volání nespravovaného kódu nebo struktury. Viz dokumentace 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 nativních reprezentací prvků pole. | Nepovoleno bez atributu [MarshalAs] |
Třída s LayoutKind Sequential Explicit |
Ukazatel na nativní reprezentaci třídy | Nativní reprezentace třídy |
Následující tabulka obsahuje výchozí pravidla zařazová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í MODELU 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 volání nespravovaného kódu. 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 MODELU COM v .NET modul runtime .NET změní výchozí pravidla zařazování tak, aby odpovídala běžné sémantice modelu COM. Následující tabulka uvádí pravidla, která moduly runtime .NET používají ve scénářích MODELU COM:
Typ .NET | Nativní typ (volání metody MODELU 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. Stačí ale jenom definovat třídu, musíte také instrukci instruovat, jak mapovat pole ve třídě na nespravovanou strukturu. Tady se StructLayout
atribut stane užitečným.
[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);
}
Předchozí kód ukazuje jednoduchý příklad volání do GetSystemTime()
funkce. Zajímavý bit je na řádku 4. Atribut určuje, že pole třídy by měla být namapována postupně na strukturu na druhé (nespravované) straně. 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;
Někdy výchozí zařazování struktury nedělá to, co potřebujete. Článek Přizpůsobení struktury seřazováním vás naučí, jak přizpůsobit způsob zařazování struktury.