Pembuatan sumber untuk marshalling kustom
.NET 7 memperkenalkan mekanisme baru untuk penyesuaian tentang bagaimana jenis di-marshalled saat menggunakan interop yang dihasilkan sumber. Generator sumber untuk P/InvokesMarshalUsingAttribute mengenali dan NativeMarshallingAttribute sebagai indikator untuk marshalling kustom jenis.
NativeMarshallingAttribute dapat diterapkan ke jenis untuk menunjukkan marshalling kustom default untuk jenis tersebut. MarshalUsingAttribute dapat diterapkan ke parameter atau mengembalikan nilai untuk menunjukkan marshalling kustom untuk penggunaan jenis tertentu tersebut, lebih diutamakan daripada yang NativeMarshallingAttribute mungkin ada pada jenis itu sendiri. Kedua atribut ini mengharapkan — Typejenis marshaller titik masuk—yang ditandai dengan satu atau beberapa CustomMarshallerAttribute atribut. Masing-masing CustomMarshallerAttribute menunjukkan implementasi marshaller mana yang harus digunakan untuk marshal jenis terkelola yang ditentukan untuk yang ditentukan MarshalMode.
Implementasi Marshaller
Implementasi Marshaller dapat stateless atau stateful. Jika jenis marshaller adalah static
kelas, itu dianggap tanpa status. Jika ini adalah jenis nilai, itu dianggap stateful dan satu instans marshaller tersebut akan digunakan untuk marshal parameter tertentu atau nilai pengembalian. Bentuk yang berbeda untuk implementasi marshaller diharapkan berdasarkan apakah marshaller tanpa status atau stateful dan apakah mendukung marshalling dari dikelola ke tidak dikelola, tidak dikelola untuk dikelola, atau keduanya. .NET SDK mencakup penganalisis dan perbaikan kode untuk membantu mengimplementasikan marshaller yang sesuai dengan bentuk yang diperlukan.
MarshalMode
yang MarshalMode ditentukan dalam CustomMarshallerAttribute menentukan dukungan dan bentuk marshalling yang diharapkan untuk implementasi marshaller. Semua mode mendukung implementasi stateless marshaller. Mode marshalling elemen tidak mendukung implementasi stateful marshaller.
MarshalMode |
Dukungan yang diharapkan | Bisa stateful |
---|---|---|
ManagedToUnmanagedIn | Dikelola ke tidak terkelola | Ya |
ManagedToUnmanagedRef | Dikelola ke tidak dikelola dan tidak dikelola ke dikelola | Ya |
ManagedToUnmanagedOut | Tidak dikelola ke terkelola | Ya |
UnmanagedToManagedIn | Tidak dikelola ke terkelola | Ya |
UnmanagedToManagedRef | Dikelola ke tidak dikelola dan tidak dikelola ke dikelola | Ya |
UnmanagedToManagedOut | Dikelola ke tidak terkelola | Ya |
ElementIn | Dikelola ke tidak terkelola | No |
ElementRef | Dikelola ke tidak dikelola dan tidak dikelola ke dikelola | No |
ElementOut | Tidak dikelola ke terkelola | No |
MarshalMode.Default menunjukkan bahwa implementasi marshaller harus digunakan untuk mode apa pun yang didukungnya. Jika implementasi marshaller untuk yang lebih spesifik MarshalMode
juga ditentukan, itu lebih diutamakan daripada MarshalMode.Default
.
Penggunaan dasar
Kita dapat menentukan NativeMarshallingAttribute pada jenis, menunjuk pada jenis marshaller titik masuk yang merupakan static
kelas atau struct
.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
, jenis marshaller titik masuk, ditandai dengan CustomMarshallerAttribute, menunjuk pada jenis implementasi marshaller. Dalam contoh ini, ExampleMarshaller
adalah titik masuk dan implementasinya. Ini sesuai dengan bentuk marshaller yang diharapkan untuk marshalling kustom nilai.
[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;
}
}
ExampleMarshaller
Dalam contoh adalah marshaller tanpa status yang mengimplementasikan dukungan untuk marshalling dari dikelola ke tidak dikelola dan dari tidak dikelola ke dikelola. Logika marshalling sepenuhnya dikendalikan oleh implementasi marshaller Anda. Menandai bidang pada struct dengan MarshalAsAttribute tidak berpengaruh pada kode yang dihasilkan.
Jenis kemudian Example
dapat digunakan dalam pembuatan sumber P/Invoke. Dalam contoh P/Invoke berikut, ExampleMarshaller
akan digunakan untuk marshal parameter dari dikelola ke tidak dikelola. Ini juga akan digunakan untuk marshal nilai pengembalian dari tidak dikelola ke dikelola.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Untuk menggunakan marshaller yang berbeda untuk penggunaan Example
jenis tertentu, tentukan MarshalUsingAttribute di situs penggunaan. Dalam contoh P/Invoke berikut, ExampleMarshaller
akan digunakan untuk marshal parameter dari dikelola ke tidak dikelola. OtherExampleMarshaller
akan digunakan untuk melakukan marshal nilai pengembalian dari yang tidak dikelola ke dikelola.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Koleksi
Terapkan ke ContiguousCollectionMarshallerAttribute jenis titik masuk marshaller untuk menunjukkan bahwa itu untuk koleksi yang berdampingan. Jenis harus memiliki satu parameter jenis lagi daripada jenis terkelola terkait. Parameter jenis terakhir adalah tempat penampung dan akan diisi oleh generator sumber dengan jenis yang tidak dikelola untuk jenis elemen koleksi.
Misalnya, Anda dapat menentukan marshalling kustom untuk List<T>. Dalam kode berikut, ListMarshaller
adalah titik masuk dan implementasinya. Ini sesuai dengan bentuk marshaller yang diharapkan untuk marshalling kustom koleksi.
[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();
}
ListMarshaller
Dalam contoh adalah marshaller koleksi stateless yang mengimplementasikan dukungan untuk marshalling dari dikelola ke tidak terkelola dan dari yang tidak dikelola hingga dikelola untuk List<T>. Dalam contoh P/Invoke berikut, ListMarshaller
akan digunakan untuk marshal parameter dari dikelola ke tidak dikelola dan untuk marshal nilai pengembalian dari tidak dikelola ke dikelola. CountElementName menunjukkan bahwa numValues
parameter harus digunakan sebagai jumlah elemen saat marsekal nilai pengembalian dari tidak dikelola ke dikelola.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);