Källgenerering för anpassad marshalling
.NET 7 introducerar en ny mekanism för anpassning av hur en typ är ordnad när du använder källgenererad interop. Källgeneratorn för P/Invokes identifierar MarshalUsingAttribute och NativeMarshallingAttribute som indikatorer för anpassad sortering av en typ.
NativeMarshallingAttribute kan tillämpas på en typ för att ange standard anpassad marshalling för den typen. MarshalUsingAttribute Kan tillämpas på en parameter eller ett returvärde för att ange anpassad sortering för den specifika användningen av typen, med företräde framför alla NativeMarshallingAttribute som kan finnas på själva typen. Båda dessa attribut förväntar sig en Type– startpunkts marshallertypen – som är markerad med ett eller flera CustomMarshallerAttribute attribut. Var CustomMarshallerAttribute och en anger vilken marshaller-implementering som ska användas för att konvertera den angivna hanterade typen för den angivna MarshalMode.
Marshaller-implementering
Marshaller-implementeringar kan antingen vara tillståndslösa eller tillståndskänsliga. Om marshallertypen är en static
klass anses den vara tillståndslös. Om det är en värdetyp anses den vara tillståndskänslig och en instans av den marshallern används för att konvertera en specifik parameter eller ett returvärde. Olika former för marshaller-implementeringen förväntas baserat på om en marshaller är tillståndslös eller tillståndskänslig och om den stöder marshalling från hanterad till ohanterad, ohanterad till hanterad eller både och. .NET SDK innehåller analysverktyg och kodkorrigeringar som hjälper dig att implementera marshallers som överensstämmer med de former som krävs.
MarshalMode
Angivet MarshalMode i en CustomMarshallerAttribute avgör det förväntade marshalling-stödet och formen för marshaller-implementeringen. Alla lägen stöder tillståndslösa marshallerimplementeringar. Element marshalling-lägen stöder inte tillståndskänsliga marshallerimplementeringar.
MarshalMode |
Förväntat stöd | Kan vara tillståndskänslig |
---|---|---|
ManagedToUnmanagedIn | Hanterad till ohanterad | Ja |
ManagedToUnmanagedRef | Hanterad till ohanterad och ohanterad till hanterad | Ja |
ManagedToUnmanagedOut | Ohanterad till hanterad | Ja |
UnmanagedToManagedIn | Ohanterad till hanterad | Ja |
UnmanagedToManagedRef | Hanterad till ohanterad och ohanterad till hanterad | Ja |
UnmanagedToManagedOut | Hanterad till ohanterad | Ja |
ElementIn | Hanterad till ohanterad | Nej |
ElementRef | Hanterad till ohanterad och ohanterad till hanterad | Nej |
ElementOut | Ohanterad till hanterad | Nej |
MarshalMode.Default anger att marshaller-implementeringen ska användas för alla lägen som den stöder. Om en marshaller-implementering för en mer specifik MarshalMode
anges har den företräde framför MarshalMode.Default
.
Grundläggande användning
Vi kan ange NativeMarshallingAttribute på en typ som pekar på en startpunkts marshallertyp som antingen är en static
klass eller en struct
.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
, är startpunktens marshallertyp markerad med CustomMarshallerAttribute, som pekar på en marshaller-implementeringstyp . I det här exemplet ExampleMarshaller
är både startpunkten och implementeringen. Den överensstämmer med marshallerformer som förväntas för anpassad marshalling av ett värde.
[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;
}
}
I ExampleMarshaller
exemplet finns en tillståndslös marshaller som implementerar stöd för marshalling från hanterad till ohanterad och från ohanterad till hanterad. Marshalling-logiken styrs helt av din marshaller-implementering. Att markera fält på en struct med MarshalAsAttribute har ingen effekt på den genererade koden.
Typen Example
kan sedan användas i P/Invoke-källgenerering. I följande P/Invoke-exempel ExampleMarshaller
används för att konvertera parametern från hanterad till ohanterad. Det används också för att konvertera returvärdet från ohanterat till hanterat.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Om du vill använda en annan marshaller för en specifik användning av Example
typen anger du MarshalUsingAttribute på användningsplatsen. I följande P/Invoke-exempel ExampleMarshaller
används för att konvertera parametern från hanterad till ohanterad. OtherExampleMarshaller
används för att konvertera returvärdet från ohanterat till hanterat.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Samlingar
ContiguousCollectionMarshallerAttribute Använd på en marshaller-startpunktstyp för att ange att den är för sammanhängande samlingar. Typen måste ha en fler typparameter än den associerade hanterade typen. Den sista typparametern är en platshållare och fylls i av källgeneratorn med den ohanterade typen för samlingens elementtyp.
Du kan till exempel ange anpassad marshalling för en List<T>. I följande kod ListMarshaller
är både startpunkten och implementeringen. Den överensstämmer med marshallerformer som förväntas för anpassad marshalling av en samling.
[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();
}
I ListMarshaller
exemplet finns en tillståndslös samlings marshaller som implementerar stöd för marshalling från hanterad till ohanterad och från ohanterad till hanterad för en List<T>. I följande P/Invoke-exempel ListMarshaller
används för att konvertera parametern från hanterad till ohanterad och för att konvertera returvärdet från ohanterat till hanterat. CountElementName anger att parametern numValues
ska användas som elementantal när du samlar returvärdet från ohanterat till hanterat.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);