Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
.NET 7 zavádí nový mechanismus přizpůsobení způsobu řazení typu při použití zprostředkovatele generovaného zdrojem. Zdrojový generátor pro volání nespravovaného kódu rozpozná MarshalUsingAttribute a NativeMarshallingAttribute jako indikátory pro vlastní zpracování typu.
NativeMarshallingAttribute lze použít u typu, který označuje výchozí vlastní zařazování pro daný typ. MarshalUsingAttribute lze použít u parametru nebo návratové hodnoty, aby bylo možné určit vlastní marshaling pro konkrétní použití typu, přičemž má přednost před jakýmkoli NativeMarshallingAttribute, který může být na samotném typu. Oba tyto atributy očekávají Type typ marshalleru vstupního bodu, který je označený jedním nebo více CustomMarshallerAttribute atributy. Každá CustomMarshallerAttribute označuje, která implementace marshalleru by měla být použita k zařazování zadaného spravovaného typu pro zadané MarshalMode.
Implementace Marshalleru
Vlastní implementace marshalleru můžou být bezstavové nebo stavové. Pokud je typ marshaller třídy static , považuje se za bezstavový a metody implementace by neměly sledovat stav napříč voláními. Pokud se jedná o typ hodnoty, považuje se za stavový a jedna instance tohoto marshalleru se použije k zařazování konkrétního parametru nebo návratové hodnoty. Použití unikátní instance umožňuje zachování stavu v rámci procesu serializace a deserializace.
Obrazce Marshalleru
Sada metod, které generátor marshalování očekává od vlastního typu marshalleru, se označuje jako rozhraní marshalleru. Pokud chcete podporovat bezstavové, statické vlastní typy marshallerů v rozhraní .NET Standard 2.0 (které nepodporují metody statického rozhraní) a zlepšují výkon, typy rozhraní se nepoužívají k definování a implementaci obrazců marshalleru. Místo toho jsou obrazce zdokumentovány v článku Vlastní obrazce spravované marshallerem. Očekávané metody (nebo tvar) závisí na tom, zda je marshaller bezstavový nebo stavový a zda podporuje zpracovávání ze spravovaného do nespravovaného, nespravovaného na spravovaný, nebo obou (deklarovaných pomocí CustomMarshallerAttribute.MarshalMode). Sada .NET SDK obsahuje analyzátory a opravy kódu, které pomáhají s implementací maršálovačů, které odpovídají požadovaným strukturám.
MarshalMode
Zadaný MarshalMode v sadě CustomMarshallerAttribute určuje očekávanou podporu a tvar zařazování pro implementaci marshalleru. Všechny režimy podporují bezstavové implementace maršálování. Režimy zařazování prvků nepodporují implementace stavových marshallerů.
MarshalMode |
Očekávaná podpora | Může být stavový |
|---|---|---|
| ManagedToUnmanagedIn | Přechod ze spravovaného na nespravovaný | Ano |
| ManagedToUnmanagedRef | Přechod ze spravovaného do nespravovaného a naopak | Ano |
| ManagedToUnmanagedOut | Z nespravovaného na spravované | Ano |
| UnmanagedToManagedIn | Z nespravovaného na spravované | Ano |
| UnmanagedToManagedRef | Přechod ze spravovaného do nespravovaného a naopak | Ano |
| UnmanagedToManagedOut | Přechod ze spravovaného na nespravovaný | Ano |
| ElementIn | Přechod ze spravovaného na nespravovaný | Ne |
| ElementRef | Přechod ze spravovaného do nespravovaného a naopak | Ne |
| ElementOut | Z nespravovaného na spravované | Ne |
Použijte MarshalMode.Default k označení, že implementace marshalleru se vztahuje na kterýkoli podporovaný režim podle metod, které implementuje. Pokud zadáte marshaller pro konkrétnější MarshalMode, má tento marshaller přednost před jedním označeným jako Default.
Základní použití
Zařazování jedné hodnoty
Pokud chcete vytvořit vlastní marshaller pro typ, musíte definovat typ marshalleru hlavního vstupního bodu, který implementuje požadované metody zpracování. Typ zařazovače vstupního static bodu může být třída nebo struct a musí být označen značkou CustomMarshallerAttribute.
Představte si například jednoduchý typ, který chcete přesunout mezi spravovaným a nespravovaným kódem.
public struct Example
{
public string Message;
public int Flags;
}
Definování typu marshalleru
Můžete vytvořit typ ExampleMarshaller, který je označený jako CustomMarshallerAttribute, čímž se označuje, že se jedná o typ marshalleru vstupního bodu, který poskytuje vlastní informace o zařazování pro daný typ Example. Prvním argumentem CustomMarshallerAttribute je spravovaný typ, na který se zaměřuje marshaller. Druhým argumentem je MarshalMode, který marshaller podporuje. Třetím argumentem je samotný typ marshalleru, tj. typ, který implementuje metody v očekávaném tvaru.
[CustomMarshaller(typeof(Example), MarshalMode.Default, typeof(ExampleMarshaller))]
internal static unsafe class ExampleMarshaller
{
public static ExampleUnmanaged ConvertToUnmanaged(Example managed)
{
return new ExampleUnmanaged()
{
Message = (IntPtr)Utf8StringMarshaller.ConvertToUnmanaged(managed.Message),
Flags = managed.Flags
};
}
public static Example ConvertToManaged(ExampleUnmanaged unmanaged)
{
return new Example()
{
Message = Utf8StringMarshaller.ConvertToManaged((byte*)unmanaged.Message),
Flags = unmanaged.Flags
};
}
public static void Free(ExampleUnmanaged unmanaged)
{
Utf8StringMarshaller.Free((byte*)unmanaged.Message);
}
internal struct ExampleUnmanaged
{
public IntPtr Message;
public int Flags;
}
}
Zde ExampleMarshaller uvedený příklad implementuje bezstavové zařazování ze spravovaného Example typu do blittable reprezentace ve formátu, který nativní kód očekává (ExampleUnmanaged) a zpět. Metoda Free se používá k uvolnění všech nespravovaných prostředků přidělených během procesu marshalingu. Logika zařazování je zcela řízena implementací marshalleru. Označení polí ve struktuře MarshalAsAttribute nemá žádný vliv na vygenerovaný kód.
ExampleMarshaller Tady je typ vstupního bodu i typ implementace. V případě potřeby však můžete přizpůsobit marshalling pro různé režimy tím, že vytvoříte samostatné typy marshallerů pro každý režim. Přidejte nový CustomMarshallerAttribute pro každý režim, například v následující třídě. Obvykle je to nezbytné pouze pro stavové marshallery, kde typ marshalleru je struct, který udržuje stav při voláních. Podle konvence jsou implementační typy vnořeny v marshalleru vstupního bodu.
[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedIn, typeof(ExampleMarshaller.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedOut, typeof(ExampleMarshaller.UnmanagedToManagedOut))]
internal static class ExampleMarshaller
{
internal struct ManagedToUnmanagedIn
{
public void FromManaged(TManaged managed) => throw new NotImplementedException();
public TNative ToUnmanaged() => throw new NotImplementedException();
public void Free() => throw new NotImplementedException()
}
internal struct UnmanagedToManagedOut
{
public void FromUnmanaged(TNative unmanaged) => throw new NotImplementedException();
public TManaged ToManaged() => throw new NotImplementedException();
public void Free() => throw new NotImplementedException();
}
}
Deklarujte použití marshalleru
Jakmile vytvoříte typ marshalleru, můžete v podpisu metody interop použít MarshalUsingAttribute, abyste označili, že chcete použít tento marshaller pro konkrétní parametr nebo návratovou hodnotu.
MarshalUsingAttribute bere typ zařazovače vstupního bodu jako argument, v tomto případě ExampleMarshaller.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ExampleMarshaller))]
internal static partial Example ConvertExample(
[MarshalUsing(typeof(ExampleMarshaller))] Example example);
Abyste nemuseli zadávat typ marshalleru pro každé použití Example typu, můžete ho použít NativeMarshallingAttribute i u samotného Example typu. To znamená, že zadaný marshaller by měl být použit ve výchozím nastavení pro všechny případy použití typu Example při generování zdrojů pro zprostředkování komunikace.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
Typ Example lze pak použít ve zdrojových generovaných metodách P/Invoke bez zadání typu marshaller. V následujícím příkladu P/Invoke bude ExampleMarshaller použito k zařazování parametru ze spravovaného kódu na nespravovaný. Použije se také k přenosu návratové hodnoty z nespravovaného do spravovaného prostředí.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Chcete-li použít jiný marshaller pro konkrétní parametr nebo návratovou hodnotu daného typu, specifikujte MarshalUsingAttribute na místě použití. V následujícím příkladu P/Invoke bude ExampleMarshaller použito k zařazování parametru ze spravovaného kódu na nespravovaný.
OtherExampleMarshaller se použije k přenosu návratové hodnoty z nespravované na spravovanou.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Úprava kolekcí
Ne generické kolekce
Pro kolekce, které nejsou obecné vzhledem k typu elementu, byste měli vytvořit jednoduchý typ převodníku, jak bylo ukázáno dříve.
Obecné kolekce
Pokud chcete vytvořit vlastní marshaller pro obecný typ kolekce, můžete použít ContiguousCollectionMarshallerAttribute atribut. Tento atribut označuje, že marshaller je pro souvislé kolekce, jako jsou pole nebo seznamy, a poskytuje sadu metod, které musí marshaller implementovat pro podporu zařazování prvků kolekce. Typ prvku v kolekci, který je zasílán, musí mít také definovaného marshaller podle dříve popsaných metod.
Použijte ContiguousCollectionMarshallerAttribute na typ vstupního bodu marshalleru, abyste naznačili, že je určen pro souvislé kolekce. Typ vstupního bodu marshalleru musí mít o jeden více parametrů typu než přidružený spravovaný typ. Poslední parametr typu je zástupný symbol a bude vyplněn generátorem zdrojového kódu nespravovaným typem pro typ prvku kolekce.
Můžete například zadat vlastní sériové zpracování pro List<T>. V následujícím kódu ListMarshaller je vstupním bodem i implementací. Odpovídá jednomu z typů marshalleru očekávaných pro vlastní zprostředkování kolekce. (Všimněte si, že se jedná o neúplný příklad.)
[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(List<>), MarshalMode.Default, typeof(ListMarshaller<,>.DefaultMarshaller))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
public static class DefaultMarshaller
{
public static byte* AllocateContainerForUnmanagedElements(List<T> managed, out int numElements)
{
numElements = managed.Count;
nuint collectionSizeInBytes = managed.Count * /* size of T */;
return (byte*)NativeMemory.Alloc(collectionSizeInBytes);
}
public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> new Span<TUnmanagedElement>((TUnmanagedElement*)unmanaged, numElements);
public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> new List<T>(length);
public static Span<T> GetManagedValuesDestination(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>((TUnmanagedElement*)nativeValue, numElements);
public static void Free(byte* unmanaged)
=> NativeMemory.Free(unmanaged);
}
}
V příkladu ListMarshaller je bezstavový zařazovač kolekcí, který implementuje podporu pro přenášení ze spravovaného prostředí do nespravovaného a z nespravovaného do spravovaného pro List<T>. Ve následujícím příkladu P/Invoke se ListMarshaller použije k převodu kontejneru kolekce pro parametr ze spravovaného do nespravovaného prostředí a k převodu kontejneru kolekce pro návratovou hodnotu z nespravovaného do spravovaného prostředí. Zdrojový generátor vygeneruje kód pro zkopírování prvků z parametru list do kontejneru poskytnutého marshallerem. Vzhledem k tomu, že int je blitovatelný, není potřeba samotné prvky přenášet.
CountElementName označuje, že numValues parametr by měl být použit jako počet prvků při zařazování návratové hodnoty z nespravované do spravované.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(numValues))]
internal static partial List<int> ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);
Pokud je typ elementu kolekce vlastní typ, můžete určit zařaďovač elementu pro tento prvek pomocí dalšího MarshalUsingAttribute s ElementIndirectionDepth = 1.
ListMarshaller zpracuje kontejner kolekce a ExampleMarshaller přenese každý prvek z nespravovaného prostředí na spravované a naopak. Označení ElementIndirectionDepth indikuje, že marshaller by se měl použít na prvky kolekce, které jsou o jednu úroveň hlubší než samotná kolekce.
[LibraryImport("nativelib")]
[MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(numValues))]
[MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))]
[MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
List<Example> list,
out int numValues);