Típus-rendezés
A rendezés a típusok átalakításának folyamata, amikor át kell haladniuk a felügyelt és a natív kód között.
A rendezéshez azért van szükség, mert a felügyelt és a nem felügyelt kód típusai eltérőek. A felügyelt kódban például van egy string
, míg a nem felügyelt sztringek lehetnek .NET-kódolás string
(UTF-16), ANSI-kódlap kódolása, UTF-8, null-végződésű, ASCII stb. A P/Invoke alrendszer alapértelmezés szerint a jelen cikkben ismertetett alapértelmezett viselkedés alapján próbálja meg a helyesen cselekedni. Azokban a helyzetekben azonban, ahol további vezérlésre van szükség, használhatja a MarshalAs attribútumot annak meghatározására, hogy mi a várt típus a nem felügyelt oldalon. Ha például azt szeretné, hogy a sztring null értékű UTF-8 sztringként legyen elküldve, a következőképpen teheti meg:
[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);
Ha az attribútumot System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
a szerelvényre alkalmazza, az alábbi szakaszban szereplő szabályok nem érvényesek. További információ arról, hogy a .NET-értékek hogyan lesznek elérhetők a natív kódnak az attribútum alkalmazásakor: letiltott futtatókörnyezeti rendezés.
A gyakori típusok rendezési szabályai
Általában a futtatókörnyezet megpróbálja megtenni a "helyes dolgot" a rendezés során, hogy a lehető legkevesebb munkát követelje meg Öntől. Az alábbi táblázatok azt írják le, hogy az egyes típusokat alapértelmezés szerint hogyan rendezi a rendszer, amikor egy paraméterben vagy mezőben használják. A C99/C++11 rögzített szélességű egész szám és karaktertípusok biztosítják, hogy az alábbi táblázat minden platformhoz megfelelő legyen. Bármilyen natív típust használhat, amelynek igazítási és méretkövetelményei megegyeznek az ilyen típusokkal.
Ez az első táblázat azoknak a különböző típusoknak a leképezéseit ismerteti, amelyek esetében a rendezés ugyanaz a P/Invoke és a mezőrendezés esetében is.
C# kulcsszó | .NET-típus | Natív típus |
---|---|---|
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 Vagy char a P/Invoke vagy a struktúra kódolásától függően. Tekintse meg a charset dokumentációját. |
System.Char |
char16_t* Vagy char* a P/Invoke vagy a struktúra kódolásától függően. Tekintse meg a charset dokumentációját. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET-mutatótípusok (pl. void* ) |
void* |
|
Típus származtatása System.Runtime.InteropServices.SafeHandle |
void* |
|
Típus származtatása System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Win32 BOOL típus |
decimal |
System.Decimal |
COM-struktúra DECIMAL |
.NET-meghatalmazott | Natív függvénymutató | |
System.DateTime |
Win32 DATE típus |
|
System.Guid |
Win32 GUID típus |
A rendezés néhány kategóriája eltérő alapértelmezett értékekkel rendelkezik, ha paraméterként vagy struktúraként rendezi azokat.
.NET-típus | Natív típus (paraméter) | Natív típus (mező) |
---|---|---|
.NET-tömb | Mutató a tömbelemek natív ábrázolásaiból álló tömb elejére. | Attribútum nélkül [MarshalAs] nem engedélyezett |
Egy osztály egy vagy Sequential több LayoutKind Explicit |
Mutató az osztály natív ábrázolására | Az osztály natív ábrázolása |
Az alábbi táblázat a csak Windows rendszerű alapértelmezett rendezési szabályokat tartalmazza. Nem Windows-platformokon ezek a típusok nem helyezhetők át.
.NET-típus | Natív típus (paraméter) | Natív típus (mező) |
---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM-felület | Attribútum nélkül [MarshalAs] nem engedélyezett |
System.ArgIterator |
va_list |
Nem engedélyezett |
System.Collections.IEnumerator |
IEnumVARIANT* |
Nem engedélyezett |
System.Collections.IEnumerable |
IDispatch* |
Nem engedélyezett |
System.DateTimeOffset |
int64_t a kullancsok számát 1601. január 1-jén éjfél óta |
int64_t a kullancsok számát 1601. január 1-jén éjfél óta |
Egyes típusok csak paraméterekként és nem mezőként rendezhetők. Ezek a típusok a következő táblázatban találhatók:
.NET-típus | Natív típus (csak paraméter) |
---|---|
System.Text.StringBuilder |
Vagy char* a char16_t* P/Invoke függvénytől függően CharSet . Tekintse meg a charset dokumentációját. |
System.ArgIterator |
va_list (csak Windows x86/x64/arm64 rendszeren) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Ha ezek az alapértelmezett értékek nem pontosan azt teszik, amit szeretne, testre szabhatja a paraméterek rendezésének módját. A paraméter-rendezési cikk bemutatja, hogyan szabhatja testre a különböző paramétertípusokat.
Alapértelmezett rendezés COM-forgatókönyvekben
Amikor a .NET-ben COM-objektumokon hív meg metódusokat, a .NET-futtatókörnyezet az alapértelmezett rendezési szabályokat a közös COM szemantikának megfelelően módosítja. Az alábbi táblázat azokat a szabályokat sorolja fel, amelyeket a .NET-futtatókörnyezetek a COM-forgatókönyvekben használnak:
.NET-típus | Natív típus (COM-metódushívások) |
---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
Delegálási típusok | _Delegate* .NET-keretrendszer. Nem engedélyezett a .NET Core és a .NET 5+ rendszerben. |
System.Drawing.Color |
OLECOLOR |
.NET-tömb | SAFEARRAY |
System.String[] |
SAFEARRAY az BSTR s |
Osztály- és szerkezetrendezés
A típus-rendezés másik aspektusa, hogy hogyan lehet átadni egy szerkezetet egy nem felügyelt metódusnak. A nem felügyelt metódusok némelyikéhez például szükség van egy struktúra paraméterként való megadására. Ezekben az esetekben létre kell hoznia egy megfelelő szerkezetet vagy osztályt a világ felügyelt részén, hogy paraméterként használhassa. Az osztály definiálása azonban nem elég, azt is meg kell adnia a rendezőnek, hogyan képezheti le az osztály mezőit a nem felügyelt szerkezetre. Itt az StructLayout
attribútum hasznossá válik.
[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);
}
Az előző kód egy egyszerű példát mutat be a függvénybe való behívásra GetSystemTime()
. Az érdekes bit a 4. sorban van. Az attribútum azt határozza meg, hogy az osztály mezőit egymás után kell leképezni a másik (nem felügyelt) oldalon lévő szerkezetre. Ez azt jelenti, hogy a mezők elnevezése nem fontos, csak a sorrendjük fontos, mivel meg kell felelnie a nem felügyelt szerkezetnek, amely az alábbi példában látható:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
Előfordulhat, hogy a struktúrához tartozó alapértelmezett rendezés nem azt teszi, amire szüksége van. A testreszabási struktúra-rendezési cikk bemutatja, hogyan szabhatja testre a struktúrát.