Struktúra-rendezés testreszabása
Néha a struktúrák alapértelmezett rendezési szabályai nem pontosan azok, amelyekre szüksége van. A .NET-futtatókörnyezetek néhány bővítménypontot biztosítanak a struktúra elrendezésének és a mezők rendezési módjának testreszabásához. A struktúraelrendezés testreszabása minden forgatókönyv esetében támogatott, de a mezőrendezés testreszabása csak olyan forgatókönyvekben támogatott, ahol engedélyezve van a futásidejű rendezés. Ha a futásidejű rendezés le van tiltva, akkor minden mezőrendezést manuálisan kell elvégezni.
Feljegyzés
Ez a cikk nem foglalkozik a forrás által létrehozott interophoz tartozó rendezés testreszabásával. Ha forrás által létrehozott interop-ot használ a P/Invokes vagy a COM számára, tekintse meg a rendezés testreszabását.
Struktúraelrendezés testreszabása
A .NET biztosítja az System.Runtime.InteropServices.StructLayoutAttribute attribútumot és az System.Runtime.InteropServices.LayoutKind enumerálást, hogy testre szabhassa a mezők memóriabeli elhelyezését. Az alábbi útmutató segít elkerülni a gyakori problémákat.
✔️ FONTOLJA meg, LayoutKind.Sequential
hogy amikor csak lehetséges.
✔️ A DO csak akkor használható LayoutKind.Explicit
a rendezés során, ha a natív szerkezet explicit elrendezéssel is rendelkezik, például egy egyesítéssel.
❌ NE használjon osztályokat összetett natív típusok öröklés útján történő kifejezéséhez.
❌ NE használja LayoutKind.Explicit
a struktúrák nem Windows-platformokon történő rendezésekor, ha a futtatókörnyezeteket a .NET Core 3.0 előtt kell megcélolnia. A 3.0 előtti .NET Core-futtatókörnyezet nem támogatja a explicit struktúrák érték szerinti átadását natív függvényeknek Intel vagy AMD 64 bites, nem Windows rendszerű rendszereken. A futtatókörnyezet azonban minden platformon támogatja az explicit struktúrák átadását.
Logikai mezőrendezés testreszabása
A natív kód számos különböző logikai megjelenítéssel rendelkezik. Csak Windows rendszeren háromféleképpen jelölhet logikai értékeket. A futtatókörnyezet nem ismeri a struktúra natív definícióját, ezért a legjobb megoldás, ha kitalálja, hogyan hozhatja létre a logikai értékeket. A .NET-futtatókörnyezet segítségével jelezheti, hogyan hozhatja létre a logikai mezőt. Az alábbi példák azt mutatják be, hogyan lehet a .NET-et bool
különböző natív logikai típusokra létrehozni.
A logikai értékek alapértelmezés szerint natív 4 bájtos Win32-értékként BOOL
lesznek rendezve az alábbi példában látható módon:
public struct WinBool
{
public bool b;
}
struct WinBool
{
public BOOL b;
};
Ha explicit szeretne lenni, az UnmanagedType.Bool érték használatával ugyanazt a viselkedést érheti el, mint a fentiekben:
public struct WinBool
{
[MarshalAs(UnmanagedType.Bool)]
public bool b;
}
struct WinBool
{
public BOOL b;
};
Az UnmanagedType.U1
alábbi értékek használatával UnmanagedType.I1
megadhatja a futtatókörnyezetnek, hogy 1 bájtos natív bool
típusként alkalmazza a b
mezőt.
public struct CBool
{
[MarshalAs(UnmanagedType.U1)]
public bool b;
}
struct CBool
{
public bool b;
};
Windows rendszeren az UnmanagedType.VariantBool érték használatával megadhatja a futtatókörnyezetnek, hogy a logikai értéket 2 bájtos VARIANT_BOOL
értékre alkalmazza:
public struct VariantBool
{
[MarshalAs(UnmanagedType.VariantBool)]
public bool b;
}
struct VariantBool
{
public VARIANT_BOOL b;
};
Feljegyzés
VARIANT_BOOL
különbözik, mint a legtöbb bool típusok, hogy VARIANT_TRUE = -1
és VARIANT_FALSE = 0
. Emellett minden olyan érték, amely nem egyenlő, VARIANT_TRUE
hamisnak minősül.
Tömbmező-rendezés testreszabása
A .NET néhány módszert is tartalmaz a tömbrendezés testreszabására.
A .NET alapértelmezés szerint az elemek egybefüggő listájára mutató mutatóként helyezi el a tömböket:
public struct DefaultArray
{
public int[] values;
}
struct DefaultArray
{
int32_t* values;
};
Ha COM API-kkal együttműködik, előfordulhat, hogy objektumként SAFEARRAY*
kell létrehoznia a tömböket. Az és az System.Runtime.InteropServices.MarshalAsAttributeUnmanagedType.SafeArray érték használatával megadhatja a futtatókörnyezetnek, hogy egy tömböt a következőként SAFEARRAY*
adjon meg:
public struct SafeArrayExample
{
[MarshalAs(UnmanagedType.SafeArray)]
public int[] values;
}
struct SafeArrayExample
{
SAFEARRAY* values;
};
Ha testre kell szabnia, hogy milyen típusú elem található a SAFEARRAY
fájlban, akkor a MarshalAsAttribute.SafeArraySubType mezőkkel MarshalAsAttribute.SafeArrayUserDefinedSubType testre szabhatja a pontos elemtípust SAFEARRAY
.
Ha helyben kell létrehoznia a tömböt, az UnmanagedType.ByValArray érték használatával megadhatja a rendezőnek, hogy helyben helyezze el a tömböt. Ha ezt a rendezést használja, a tömb elemeinek számához is meg kell adnia egy értéket MarshalAsAttribute.SizeConst a mezőnek, hogy a futtatókörnyezet megfelelően lefoglalhassa a térközt a szerkezet számára.
public struct InPlaceArray
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] values;
}
struct InPlaceArray
{
int values[4];
};
Feljegyzés
A .NET nem támogatja a változó hosszúságú tömbmezők C99 rugalmas tömbtagként való beállítását.
Sztringmező-rendezés testreszabása
A .NET a sztringmezők beállításához is számos testreszabást biztosít.
Alapértelmezés szerint a .NET egy sztringet egy null értékű sztringre mutató mutatóként hoz létre. A kódolás a mező értékétől függ a StructLayoutAttribute.CharSetSystem.Runtime.InteropServices.StructLayoutAttribute. Ha nincs megadva attribútum, a kódolás alapértelmezés szerint ANSI-kódolást használ.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DefaultString
{
public string str;
}
struct DefaultString
{
char* str;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DefaultString
{
public string str;
}
struct DefaultString
{
char16_t* str; // Could also be wchar_t* on Windows.
};
Ha különböző kódolásokat kell használnia a különböző mezőkhöz, vagy inkább explicitebb a szerkezetdefinícióban, használhatja az UnmanagedType.LPStr attribútum értékeit vagy UnmanagedType.LPWStr értékeit System.Runtime.InteropServices.MarshalAsAttribute .
public struct AnsiString
{
[MarshalAs(UnmanagedType.LPStr)]
public string str;
}
struct AnsiString
{
char* str;
};
public struct UnicodeString
{
[MarshalAs(UnmanagedType.LPWStr)]
public string str;
}
struct UnicodeString
{
char16_t* str; // Could also be wchar_t* on Windows.
};
Ha az UTF-8 kódolással szeretné feloldani a sztringeket, használhatja a UnmanagedType.LPUTF8Str benne lévő MarshalAsAttributeértéket.
public struct UTF8String
{
[MarshalAs(UnmanagedType.LPUTF8Str)]
public string str;
}
struct UTF8String
{
char* str;
};
Feljegyzés
A használathoz UnmanagedType.LPUTF8Str .NET-keretrendszer 4.7-.NET-keretrendszer (vagy újabb verziók) vagy .NET Core 1.1 (vagy újabb verziók) szükséges. A .NET Standard 2.0-ban nem érhető el.
Ha COM API-kkal dolgozik, előfordulhat, hogy sztringet BSTR
kell létrehoznia. Az UnmanagedType.BStr érték használatával egy sztringet BSTR
létrehozhat.
public struct BString
{
[MarshalAs(UnmanagedType.BStr)]
public string str;
}
struct BString
{
BSTR str;
};
WinRT-alapú API használata esetén előfordulhat, hogy sztringet HSTRING
kell létrehoznia. Az UnmanagedType.HString érték használatával egy sztringet HSTRING
létrehozhat. HSTRING
a rendezés csak beépített WinRT-támogatással rendelkező futtatókörnyezetekben támogatott. A WinRT-támogatás el lett távolítva a .NET 5-ben, így HSTRING
a rendezés nem támogatott a .NET 5-ben vagy újabb verzióban.
public struct HString
{
[MarshalAs(UnmanagedType.HString)]
public string str;
}
struct BString
{
HSTRING str;
};
Ha az API megköveteli, hogy helyben adja át a sztringet a struktúrában, használhatja az UnmanagedType.ByValTStr értéket. Vegye figyelembe, hogy a sztringek kódolását ByValTStr
az CharSet
attribútum határozza meg. Emellett a mezőnek sztringhosszt kell megadnia MarshalAsAttribute.SizeConst .
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DefaultString
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string str;
}
struct DefaultString
{
char str[4];
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DefaultString
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string str;
}
struct DefaultString
{
char16_t str[4]; // Could also be wchar_t[4] on Windows.
};
Decimális mezőrendezés testreszabása
Ha Windowson dolgozik, előfordulhat, hogy a natív CY
vagy CURRENCY
a struktúrát használó API-k is megjelennek. Alapértelmezés szerint a .NET decimal
típusú marshalok a natív DECIMAL
struktúrára épülnek. Az érték használatával UnmanagedType.Currency azonban utasíthatja MarshalAsAttribute a rendezőt, hogy alakítson át egy decimal
értéket natív CY
értékké.
public struct Currency
{
[MarshalAs(UnmanagedType.Currency)]
public decimal dec;
}
struct Currency
{
CY dec;
};
Szakszervezetek
Az egyesítő adatok olyan adattípusok, amelyek különböző típusú adatokat tartalmazhatnak ugyanazon a memórián. Ez a C nyelven gyakran használt adatforma. Az egyesítők .NET-ben kifejezhetők a .NET használatával LayoutKind.Explicit
. A .NET-beli egyesítések definiálásakor ajánlott a szerkezetek használata. Az osztályok használata elrendezési problémákat okozhat, és kiszámíthatatlan viselkedést eredményezhet.
struct device1_config
{
void* a;
void* b;
void* c;
};
struct device2_config
{
int32_t a;
int32_t b;
};
struct config
{
int32_t type;
union
{
device1_config dev1;
device2_config dev2;
};
};
public unsafe struct Device1Config
{
void* a;
void* b;
void* c;
}
public struct Device2Config
{
int a;
int b;
}
public struct Config
{
public int Type;
public _Union Anonymous;
[StructLayout(LayoutKind.Explicit)]
public struct _Union
{
[FieldOffset(0)]
public Device1Config Dev1;
[FieldOffset(0)]
public Device2Config Dev2;
}
}
Marsall System.Object
Windows rendszeren a - object
typed mezőket natív kódba helyezheti. Ezeket a mezőket három típus egyikére helyezheti el:
Alapértelmezés szerint egy object
-typed mező lesz rendezve egy IUnknown*
olyanhoz, amely körbefuttatja az objektumot.
public struct ObjectDefault
{
public object obj;
}
struct ObjectDefault
{
IUnknown* obj;
};
Ha objektummezőt szeretne egy objektummezőbe IDispatch*
helyezni, adjon hozzá egy MarshalAsAttributeUnmanagedType.IDispatch értéket.
public struct ObjectDispatch
{
[MarshalAs(UnmanagedType.IDispatch)]
public object obj;
}
struct ObjectDispatch
{
IDispatch* obj;
};
Ha meg szeretné adni VARIANT
, adjon hozzá egy MarshalAsAttributeUnmanagedType.Struct értéket.
public struct ObjectVariant
{
[MarshalAs(UnmanagedType.Struct)]
public object obj;
}
struct ObjectVariant
{
VARIANT obj;
};
Az alábbi táblázat azt ismerteti, hogy a obj
mező különböző futásidejű típusai hogyan képezik le a különböző, a következőben tárolt típusokat VARIANT
:
.NET-típus | VARIANT típus |
---|---|
byte |
VT_UI1 |
sbyte |
VT_I1 |
short |
VT_I2 |
ushort |
VT_UI2 |
int |
VT_I4 |
uint |
VT_UI4 |
long |
VT_I8 |
ulong |
VT_UI8 |
float |
VT_R4 |
double |
VT_R8 |
char |
VT_UI2 |
string |
VT_BSTR |
System.Runtime.InteropServices.BStrWrapper |
VT_BSTR |
object |
VT_DISPATCH |
System.Runtime.InteropServices.UnknownWrapper |
VT_UNKNOWN |
System.Runtime.InteropServices.DispatchWrapper |
VT_DISPATCH |
System.Reflection.Missing |
VT_ERROR |
(object)null |
VT_EMPTY |
bool |
VT_BOOL |
System.DateTime |
VT_DATE |
decimal |
VT_DECIMAL |
System.Runtime.InteropServices.CurrencyWrapper |
VT_CURRENCY |
System.DBNull |
VT_NULL |
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: