Quellgenerierung für benutzerdefiniertes Marshalling
.NET 7 führt einen neuen Mechanismus für die Anpassung der Art des Marshallings eines Typs ein, wenn von der Quelle generiertes Interop verwendet wird. Der Quellgenerator für P/Invokes erkennt MarshalUsingAttribute und NativeMarshallingAttribute als Indikatoren für das benutzerdefinierte Marshalling eines Typs.
NativeMarshallingAttribute kann auf einen Typ angewendet werden, um das standardmäßige benutzerdefinierte Marshalling für diesen Typ anzugeben. Das MarshalUsingAttribute kann auf einen Parameter oder Rückgabewert angewendet werden, um das benutzerdefinierte Marshalling für diese bestimmte Verwendung des Typs anzugeben, und hat dabei Vorrang vor jedem NativeMarshallingAttribute, das sich möglicherweise auf dem Typ selbst befindet. Beide Attribute erwarten einen Type, den Einstiegspunkt-Marshallertyp, der mit einem oder mehreren CustomMarshallerAttribute-Attributen gekennzeichnet ist. Jedes CustomMarshallerAttribute gibt an, welche Marshallerimplementierung verwendet werden sollte, um den angegebenen verwalteten Typ für den angegebenen MarshalMode zu marshallen.
Marshallerimplementierung
Marshallerimplementierungen können entweder zustandslos oder zustandsbehaftet sein. Wenn der Marshallertyp eine static
Klasse ist, wird er als zustandslos betrachtet. Wenn es sich um einen Werttyp handelt, gilt er als zustandsbehaftet, und eine Instanz dieses Marshallers wird verwendet, um einen bestimmten Parameter oder Rückgabewert zu marshallen. Unterschiedliche Formen für die Marshallerimplementierung werden erwartet, je nachdem, ob ein Marshaller zustandslos oder zustandsbehaftet ist und ob er das Marshalling von verwaltet zu nicht verwaltet, nicht verwaltet zu verwaltet oder beides unterstützt. Das .NET SDK enthält Analysetools und Codekorrekturen, die die Implementierung von Marshallern unterstützen, die den erforderlichen Formen entsprechen.
MarshalMode
Der in einem CustomMarshallerAttribute angegebene MarshalMode bestimmt die erwartete Marshallingunterstützung und Form für die Marshallerimplementierung. Alle Modi unterstützen zustandslose Marshallerimplementierungen. Elementmarshallingmodi unterstützen keine zustandsbehafteten Marshallerimplementierungen.
MarshalMode |
Erwartete Unterstützung | Kann zustandsbehaftet sein |
---|---|---|
ManagedToUnmanagedIn | Verwaltet zu nicht verwaltet | Ja |
ManagedToUnmanagedRef | Verwaltet zu nicht verwaltet und nicht verwaltet zu verwaltet | Ja |
ManagedToUnmanagedOut | Nicht verwaltet zu verwaltet | Ja |
UnmanagedToManagedIn | Nicht verwaltet zu verwaltet | Ja |
UnmanagedToManagedRef | Verwaltet zu nicht verwaltet und nicht verwaltet zu verwaltet | Ja |
UnmanagedToManagedOut | Verwaltet zu nicht verwaltet | Ja |
ElementIn | Verwaltet zu nicht verwaltet | Nein |
ElementRef | Verwaltet zu nicht verwaltet und nicht verwaltet zu verwaltet | Nein |
ElementOut | Nicht verwaltet zu verwaltet | Nein |
MarshalMode.Default gibt an, dass die Marshallerimplementierung für jeden unterstützten Modus verwendet werden sollte. Wenn auch eine Marshallerimplementierung für einen spezifischeren MarshalMode
angegeben ist, hat er Vorrang vor MarshalMode.Default
.
Grundlegende Verwendung
Wir können NativeMarshallingAttribute für einen Typ angeben, auf einen Einstiegspunkt-Marshallertyp verweisend, der entweder eine static
-Klasse oder ein struct
ist.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
Der Einstiegspunkt-Marshallertyp ExampleMarshaller
ist mit CustomMarshallerAttribute gekennzeichnet, auf einen Marshallerimplementierungstyp verweisend. In diesem Beispiel ist ExampleMarshaller
sowohl der Einstiegspunkt als auch die Implementierung. Dies entspricht Marshallerformen, die für das benutzerdefinierte Marshalling eines Werts erwartet werden.
[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;
}
}
Der ExampleMarshaller
im Beispiel ist ein zustandsloser Marshaller, der Unterstützung für das Marshalling von verwaltet zu nicht verwaltet und von nicht verwaltet zu verwaltet implementiert. Die Marshallinglogik wird vollständig von Ihrer Marshallerimplementierung gesteuert. Das Markieren von Feldern in einer Struktur mit MarshalAsAttribute hat keine Auswirkung auf den generierten Code.
Der Example
-Typ kann dann in der P/Invoke-Quellgenerierung verwendet werden. Im folgenden P/Invoke-Beispiel wird ExampleMarshaller
verwendet, um den Parameter von verwaltet zu nicht verwaltet zu marshallen. Er wird auch verwendet, um den Rückgabewert von nicht verwaltet zu verwaltet zu marshallen.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Um einen anderen Marshaller für eine bestimmte Verwendung des Example
-Typs zu verwenden, geben Sie MarshalUsingAttribute am Verwendungsstandort an. Im folgenden P/Invoke-Beispiel wird ExampleMarshaller
verwendet, um den Parameter von verwaltet zu nicht verwaltet zu marshallen. OtherExampleMarshaller
wird verwendet, um den Rückgabewert von nicht verwaltet zu verwaltet zu marshallen.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Sammlungen
Wenden Sie das ContiguousCollectionMarshallerAttribute auf einen Marshallereinstiegspunkttyp an, um anzugeben, dass es sich um zusammenhängende Sammlungen handelt. Der Typ muss einen Typparameter mehr als der zugeordnete verwaltete Typ aufweisen. Der letzte Typparameter ist ein Platzhalter und wird vom Quellgenerator mit dem nicht verwalteten Typ für den Elementtyp der Sammlung ausgefüllt.
Sie können z. B. benutzerdefiniertes Marshalling für eine List<T> angeben. Im folgenden Code ist ListMarshaller
sowohl der Einstiegspunkt als auch die Implementierung. Er entspricht Marshallerformen, die für das benutzerdefinierte Marshalling einer Sammlung erwartet werden.
[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();
}
Der ListMarshaller
im Beispiel ist ein zustandsloser Sammlungsmarshaller, der Unterstützung für das Marshalling von verwaltet zu nicht verwaltet und von nicht verwaltet zu verwaltet für eine List<T> implementiert. Im folgenden P/Invoke-Beispiel wird ListMarshaller
verwendet, um den Parameter von verwaltet zu nicht verwaltet zu marshallen und den Rückgabewert von nicht verwaltet zu verwaltet zu marshallen. CountElementName gibt an, dass der numValues
-Parameter beim Marshalling des Rückgabewerts von nicht verwaltet zu verwaltet als Elementanzahl verwendet werden soll.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);