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 világ sztringjei lehetnek Unicode ("széles"), nem Unicode, 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ű ANSI-sztringként legyen elküldve, a következőképpen teheti meg:

[DllImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPStr)] 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 CharSet struktúra függvényében. Tekintse meg a charset dokumentációját.
System.Char char16_t* Vagy char* a P/Invoke vagy a CharSet struktúra függvényében. 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 LayoutKindExplicit 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 BSTRs

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.

[DllImport("kernel32.dll")]
static extern void GetSystemTime(SystemTime systemTime);

[StructLayout(LayoutKind.Sequential)]
class 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.