Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
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.