Brongeneratie voor aangepast marshallen
.NET 7 introduceert een nieuw mechanisme voor aanpassing van de manier waarop een type wordt marshalld bij het gebruik van door de bron gegenereerde interop. De brongenerator voor P/Invokes MarshalUsingAttribute herkent en NativeMarshallingAttribute als indicatoren voor aangepast marshalling van een type.
NativeMarshallingAttribute kan worden toegepast op een type om de standaard aangepaste marshalling voor dat type aan te geven. De MarshalUsingAttribute kan worden toegepast op een parameter of retourwaarde om de aangepaste marshalling voor dat specifieke gebruik van het type aan te geven, waarbij prioriteit wordt gegeven aan een NativeMarshallingAttribute waarde die op het type zelf kan staan. Beide kenmerken verwachten een Type(het invoerpunt marshallertype) dat is gemarkeerd met een of meer CustomMarshallerAttribute kenmerken. Elk CustomMarshallerAttribute geeft aan welke marshaller-implementatie moet worden gebruikt om het opgegeven beheerde type voor de opgegeven te marshalen MarshalMode.
Marshaller-implementatie
Marshaller-implementaties kunnen staatloos of stateful zijn. Als het marshallertype een static
klasse is, wordt het beschouwd als staatloos. Als het een waardetype is, wordt het beschouwd als stateful en wordt één exemplaar van die marshaller gebruikt om een specifieke parameter of retourwaarde te marshalen. Verschillende vormen voor de marshaller-implementatie worden verwacht op basis van of een marshaller staatloos of stateful is en of het marshalling ondersteunt van beheerd naar onbeheerd, onbeheerd tot beheerd, of beide. De .NET SDK bevat analyses en codefixers om te helpen bij het implementeren van marshallers die voldoen aan de vereiste shapes.
MarshalMode
De MarshalMode opgegeven in een CustomMarshallerAttribute bepaalt de verwachte marshallondersteuning en vorm voor de marshaller-implementatie. Alle modi ondersteunen stateless marshaller-implementaties. Element marshallingmodi bieden geen ondersteuning voor stateful marshaller-implementaties.
MarshalMode |
Verwachte ondersteuning | Kan stateful zijn |
---|---|---|
ManagedToUnmanagedIn | Beheerd aan onbeheerde | Ja |
ManagedToUnmanagedRef | Beheerd naar onbeheerde en onbeheerde naar beheerde | Ja |
ManagedToUnmanagedOut | Onbeheerd om te worden beheerd | Ja |
UnmanagedToManagedIn | Onbeheerd om te worden beheerd | Ja |
UnmanagedToManagedRef | Beheerd naar onbeheerde en onbeheerde naar beheerde | Ja |
UnmanagedToManagedOut | Beheerd aan onbeheerde | Ja |
ElementIn | Beheerd aan onbeheerde | Nee |
ElementRef | Beheerd naar onbeheerde en onbeheerde naar beheerde | Nee |
ElementOut | Onbeheerd om te worden beheerd | Nee |
MarshalMode.Default geeft aan dat de marshaller-implementatie moet worden gebruikt voor elke modus die wordt ondersteund. Als ook een marshaller-implementatie voor een specifieker MarshalMode
is opgegeven, heeft deze voorrang op MarshalMode.Default
.
Basaal gebruik
We kunnen opgeven NativeMarshallingAttribute voor een type, wijzend op een invoerpunt marshallertype dat een static
klasse of een struct
.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
, het invoerpunt marshallertype, is gemarkeerd met CustomMarshallerAttribute, wijzend op een marshaller-implementatietype . In dit voorbeeld ExampleMarshaller
zijn zowel het toegangspunt als de implementatie. Het voldoet aan de verwachte marshallersshapes voor aangepaste marshalling van een waarde.
[CustomMarshaller(typeof(Example), MarshalMode.Default, typeof(ExampleMarshaller))]
internal static class ExampleMarshaller
{
public static ExampleUnmanaged ConvertToUnmanaged(Example managed)
=> throw new NotImplementedException();
public static Example ConvertToManaged(ExampleUnmanaged unmanaged)
=> throw new NotImplementedException();
public static void Free(ExampleUnmanaged unmanaged)
=> throw new NotImplementedException();
internal struct ExampleUnmanaged
{
public IntPtr Message;
public int Flags;
}
}
Het ExampleMarshaller
in het voorbeeld is een staatloze marshaller die ondersteuning implementeert voor marshalling van beheerd naar onbeheerd en van onbeheerd tot beheerd. De marshallinglogica wordt volledig beheerd door uw marshaller-implementatie. Het markeren van velden in een struct met MarshalAsAttribute geen effect op de gegenereerde code.
Het Example
type kan vervolgens worden gebruikt bij het genereren van de bron P/Invoke. In het volgende P/Invoke-voorbeeld ExampleMarshaller
wordt gebruikt om de parameter van beheerd naar onbeheerd te marshalen. Het wordt ook gebruikt om de retourwaarde van onbeheerd tot beheerd te marshalen.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Als u een andere marshaller wilt gebruiken voor een specifiek gebruik van het Example
type, geeft u MarshalUsingAttribute op de gebruikssite op. In het volgende P/Invoke-voorbeeld ExampleMarshaller
wordt gebruikt om de parameter van beheerd naar onbeheerd te marshalen. OtherExampleMarshaller
wordt gebruikt om de retourwaarde van onbeheerd te beheren.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Verzamelingen
Pas het ContiguousCollectionMarshallerAttribute invoerpunttype van een marshaller toe om aan te geven dat het voor aaneengesloten verzamelingen is. Het type moet een meer typeparameter hebben dan het bijbehorende beheerde type. De laatste typeparameter is een tijdelijke aanduiding en wordt ingevuld door de brongenerator met het niet-beheerde type voor het elementtype van de verzameling.
U kunt bijvoorbeeld aangepast marshalling opgeven voor een List<T>. In de volgende code ListMarshaller
is zowel het toegangspunt als de implementatie. Het voldoet aan de marshallershapes die worden verwacht voor aangepaste marshalling van een verzameling.
[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(List<>), MarshalMode.Default, typeof(ListMarshaller<,>))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
public static byte* AllocateContainerForUnmanagedElements(List<T> managed, out int numElements)
=> throw new NotImplementedException();
public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
=> throw new NotImplementedException();
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> throw new NotImplementedException();
public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> throw new NotImplementedException();
public static Span<T> GetManagedValuesDestination(List<T> managed)
=> throw new NotImplementedException();
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> throw new NotImplementedException();
public static void Free(byte* unmanaged)
=> throw new NotImplementedException();
}
Het ListMarshaller
in het voorbeeld is een staatloze verzamelings marshaller die ondersteuning implementeert voor marshalling van beheerd naar onbeheerd en van onbeheerd tot beheerd voor een List<T>. In het volgende P/Invoke-voorbeeld ListMarshaller
wordt gebruikt om de parameter van beheerd naar onbeheerd te marshalen om de retourwaarde van onbeheerd naar beheerd te marshalen. CountElementName geeft aan dat de numValues
parameter moet worden gebruikt als het aantal elementen bij het marshallen van de retourwaarde van niet-beheerd naar beheerd.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial List<int> ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);