Típusközvetíté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 marshalling azért szükséges, mert a felügyelt és a nem felügyelt kód típusai különböznek egymástól. A felügyelt kódban például string található, míg a nem felügyelt karakterláncok lehetnek .NET string kódolásban (UTF-16), ANSI kódlap kódolásban, UTF-8, null lezárt, ASCII stb. A P/Invoke alrendszer alapértelmezés szerint a helyes működésre törekszik az alapértelmezett viselkedés alapján, amelyet ebben a cikkben ismertetünk. 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. Például, ha azt szeretné, hogy a karakterlánc nullával lezárt UTF-8 karakterláncként legyen elküldve, akkor ezt így teheti meg:

[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);

Ha az attribútumot System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute a szerelvényre alkalmazza, az alábbi szakaszban szereplő szabályok nem érvényesek. Az attribútum alkalmazásakor a .NET értékek natív kódnak való megjelenítéséről a disabled runtime marshalling című témakörben olvashat.

Alapértelmezett szabályok a gyakori típusok összeállításához

Á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 típusoknak a leképezését ismerteti, amelyek esetében a rendezés megegyezik a P/Invoke és a mezőrendezés esetében is.

Fontos

Az long használó C függvény meghívásakor a C# CLong helyett CULong vagy long (.NET 6+) függvényt kell használni. A korábbi .NET verziókra vonatkozó részletekért és kerülő megoldásokért lásd: Cross-platform adattípussal kapcsolatos megfontolások.

Megjegyzés:

A wchar_t típus Windows rendszeren UTF-16 (2 bájt), de más platformokon a fordító definiálja – általában Linuxon és macOS rendszeren UTF-32 (4 bájt). Emiatt wchar_t* nehéz egyetlen platformfüggetlen ABI-ként használni. Platformfüggetlen natív API tervezésekor részesítsük előnyben az egyértelműen meghatározott kódolási megállapodást (például UTF-8) használó char* a wchar_t* helyett.

Megjegyzés:

A natív char* sztringek a kódtár vagy a platform által definiált kódolást használják. Amikor egy olyan C függvényt hív meg, amely egy adott kódolást vár, válassza ki a megfelelő "string marshalling" beállítást, például StringMarshalling.Utf8 a UTF-8-hoz, StringMarshalling.Utf16 a UTF-16-hoz, vagy StringMarshalling.Custom egyéb kódolásokhoz.

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 char vagy char16_t 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 char* vagy char16_t* 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 (például void*) void*
"System.Runtime.InteropServices.SafeHandle-ból/ből származtatott típus" void*
"System.Runtime.InteropServices.CriticalHandle-ból/ből származtatott típus" void*
bool System.Boolean Win32 BOOL típus
decimal System.Decimal COM DECIMAL struktúra
.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 Egy mutató a tömbelemek natív ábrázolásaiból álló tömb kezdőpontjára. Attribútum nélkül [MarshalAs] nem engedélyezett
Egy osztály, amelynek LayoutKind értéke Sequential vagy 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 tartalmazza a Windows rendszeren alapértelmezett marshaling szabályokat. 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 Tilos
System.Collections.IEnumerator IEnumVARIANT* Tilos
System.Collections.IEnumerable IDispatch* Tilos
System.DateTimeOffset int64_t a tikek számát jelenti 1601. január 1-jén éjfél óta int64_t a tikek számát jelenti 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*, vagy char16_t*, a P/Invoke CharSet-jétől függően. 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 metódusokat hív meg a COM-objektumokon a .NET, a .NET futtatókörnyezet az alapértelmezett rendezési szabályokat módosítja a közös COM-szemantikának megfelelően. Az alábbi táblázat felsorolja a szabályokat, amelyeket a .NET futtatókörnyezet a COM-szcenáriókban használ.

.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* a .NET-keretrendszerben. Nem engedélyezett a .NET Core és .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.

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;
    }
}

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 13. 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, *LPSYSTEMTIME;

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.