Bagikan melalui


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

Lihat juga