Delen via


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

Zie ook