Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
.NET 7 memperkenalkan mekanisme baru untuk penyesuaian tentang bagaimana jenis di-marshalled saat menggunakan interop yang dihasilkan sumber. Generator sumber untuk P/Invokes mengenali MarshalUsingAttribute dan NativeMarshallingAttribute sebagai indikator untuk marshalling kustom tipe.
NativeMarshallingAttribute dapat diterapkan ke tipe untuk menunjukkan marshalling kustom default tipe tersebut. MarshalUsingAttribute dapat diterapkan ke parameter atau nilai pengembalian untuk menunjukkan marshalling kustom untuk penggunaan jenis tertentu itu, dengan mengabaikan NativeMarshallingAttribute yang mungkin ada pada jenis itu sendiri. Kedua atribut ini mengharapkan jenis marshaller titik masuk Type—yang ditandai dengan satu atau lebih atribut CustomMarshallerAttribute. Masing-masing CustomMarshallerAttribute menunjukkan implementasi marshaller mana yang harus digunakan untuk memproses jenis terkelola yang ditentukan pada MarshalMode yang ditunjuk.
Implementasi Marshaller
Implementasi marshaller kustom dapat berupa baik stateless maupun stateful. Jika jenis marshaller adalah static kelas, itu dianggap tanpa status, dan metode implementasi tidak boleh melacak status di seluruh panggilan. Jika ini adalah jenis nilai, itu dianggap stateful, dan satu instans marshaller tersebut akan digunakan untuk marshal parameter tertentu atau mengembalikan nilai. Menggunakan instance unik memungkinkan status dipertahankan selama proses marshalling dan unmarshalling.
Bentuk marshaller
Sekumpulan metode yang diharapkan generator marshalling dari jenis marshaller kustom disebut sebagai struktur marshaller. Untuk mendukung jenis marshaller kustom statis tanpa status di .NET Standard 2.0 (yang tidak mendukung metode antarmuka statis), dan meningkatkan performa, jenis antarmuka tidak digunakan untuk menentukan dan mengimplementasikan bentuk marshaller. Sebaliknya, bentuk tersebut terdokumentasi dalam artikel Custom marshaller shapes. Metode yang diharapkan (atau bentuknya) tergantung pada apakah marshaller stateless atau berstatus, dan apakah mendukung marshalling dari terkelola ke tidak terkelola, dari tidak terkelola ke terkelola, atau keduanya (dinyatakan dengan CustomMarshallerAttribute.MarshalMode). .NET SDK mencakup penganalisis dan pemecah kode untuk membantu mengimplementasikan marshaller yang sesuai dengan bentuk yang diperlukan.
MarshalMode
MarshalMode yang ditentukan dalam CustomMarshallerAttribute menentukan dukungan marshalling dan konfigurasi yang diantisipasi untuk implementasi marshaller. Semua mode ini mendukung implementasi stateless marshaller. Mode marshalling elemen tidak mendukung implementasi marshaller yang berstatus.
MarshalMode |
Dukungan yang diharapkan | Berlaku pada | Bisa berstatus |
|---|---|---|---|
| ManagedToUnmanagedIn | Dari dikelola menjadi tidak dikelola | Parameter nilai-demi-nilai dan in dalam P/Invoke |
Ya |
| ManagedToUnmanagedRef | Berubah dari terkelola menjadi tidak terkelola dan dari tidak terkelola menjadi terkelola |
ref parameter dalam P/Invoke |
Ya |
| ManagedToUnmanagedOut | Dari tidak terkelola menjadi terkelola |
out parameter dan nilai pengembalian dalam P/Invoke |
Ya |
| UnmanagedToManagedIn | Dari tidak terkelola menjadi terkelola | Parameter by-value dan in dalam Reverse P/Invoke |
Ya |
| UnmanagedToManagedRef | Berubah dari terkelola menjadi tidak terkelola dan dari tidak terkelola menjadi terkelola |
ref parameter dalam Reverse P/Invoke |
Ya |
| UnmanagedToManagedOut | Dari dikelola menjadi tidak dikelola |
out parameter dan nilai kembalian dalam Reverse P/Invoke |
Ya |
| ElementIn | Dari dikelola menjadi tidak dikelola | Elemen koleksi yang diteruskan dengan in atau menurut nilai |
Tidak. |
| ElementRef | Berubah dari terkelola menjadi tidak terkelola dan dari tidak terkelola menjadi terkelola | Elemen dari koleksi diteruskan dengan ref |
Tidak. |
| ElementOut | Dari tidak terkelola menjadi terkelola | Elemen koleksi diteruskan dengan out |
Tidak. |
Nota
Nama anggota mengikuti pola {CallDirection}{DataFlow}:
-
Arah panggilan (
ManagedToUnmanagedatauUnmanagedToManaged) menunjukkan sisi mana yang memulai panggilan.ManagedToUnmanagedberarti kode terkelola memanggil kode tidak terkelola (P/Invoke).UnmanagedToManagedberarti kode yang tidak dikelola memanggil kode terkelola (Reverse P/Invoke, COM). -
Aliran data (
In,Out, atauRef) menunjukkan bagaimana data bergerak relatif terhadap panggilan.Inberarti aliran data dari penelepon ke penerima panggilan.Outberarti aliran data dari penelepon kembali ke pemanggil—ini mencakupoutparameter dan nilai yang dikembalikan.Refberarti aliran data di kedua arah.
Misalnya, ManagedToUnmanagedOut digunakan untuk out parameter dan mengembalikan nilai saat kode terkelola memanggil kode yang tidak dikelola. Meskipun namanya dimulai dengan "ManagedToUnmanaged", marshaller untuk mode ini mengonversi dari representasi yang tidak dikelola ke representasi terkelola, karena data mengalir kembali ke pemanggil terkelola.
Gunakan MarshalMode.Default untuk menunjukkan bahwa implementasi marshaller berlaku untuk mode yang didukung, berdasarkan metode yang diterapkannya. Jika Anda menentukan marshaller untuk yang lebih khusus MarshalMode, marshaller itu lebih diutamakan dibandingkan dengan yang ditandai sebagai Default.
Penggunaan dasar
Penyusunan nilai tunggal
Untuk membuat marshaller kustom untuk suatu tipe, Anda perlu menentukan tipe marshaller titik awal yang mengimplementasikan metode marshalling yang diperlukan. Jenis marshaller entry-point dapat berupa kelas static atau struct, dan harus diberi tanda CustomMarshallerAttribute.
Misalnya, pertimbangkan jenis sederhana yang ingin Anda marshal antara kode terkelola dan tidak terkelola:
public struct Example
{
public string Message;
public int Flags;
}
Tetapkan tipe marshaller
Anda dapat membuat jenis objek bernama ExampleMarshaller yang ditandai dengan CustomMarshallerAttribute untuk menunjukkan bahwa itu adalah tipe marshaller titik masuk yang menyediakan informasi marshalling khusus untuk jenis Example. Argumen pertama dari CustomMarshallerAttribute adalah jenis terkelola yang dituju marshaller. Argumen kedua adalah MarshalMode yang didukung oleh marshaller. Argumen ketiga adalah jenis marshaller itu sendiri, yaitu jenis yang mengimplementasikan metode dalam bentuk yang diharapkan.
[CustomMarshaller(typeof(Example), MarshalMode.Default, typeof(ExampleMarshaller))]
internal static unsafe class ExampleMarshaller
{
public static ExampleUnmanaged ConvertToUnmanaged(Example managed)
{
return new ExampleUnmanaged()
{
Message = (IntPtr)Utf8StringMarshaller.ConvertToUnmanaged(managed.Message),
Flags = managed.Flags
};
}
public static Example ConvertToManaged(ExampleUnmanaged unmanaged)
{
return new Example()
{
Message = Utf8StringMarshaller.ConvertToManaged((byte*)unmanaged.Message),
Flags = unmanaged.Flags
};
}
public static void Free(ExampleUnmanaged unmanaged)
{
Utf8StringMarshaller.Free((byte*)unmanaged.Message);
}
internal struct ExampleUnmanaged
{
public IntPtr Message;
public int Flags;
}
}
ExampleMarshaller yang ditunjukkan di sini mengimplementasikan marshalling stateless dari jenis terkelola Example ke representasi yang dapat di-blittable dalam format yang diharapkan oleh kode asli (ExampleUnmanaged) dan kembali. Metode Free ini digunakan untuk merilis sumber daya yang tidak dikelola yang dialokasikan selama proses marshalling. Logika marshalling sepenuhnya dikendalikan oleh implementasi marshaller. Menandai bidang pada struct dengan MarshalAsAttribute tidak berpengaruh pada kode yang dihasilkan.
Di sini, ExampleMarshaller adalah jenis titik masuk dan jenis implementasi. Namun, jika perlu, Anda dapat menyesuaikan marshalling untuk berbagai mode dengan membuat tipe marshaller yang terpisah untuk setiap mode. Tambahkan sebuah CustomMarshallerAttribute untuk setiap mode seperti pada kelas berikut. Biasanya, ini hanya diperlukan untuk marshaller berstatus, di mana jenis marshaller adalah struct yang mempertahankan keadaan di seluruh panggilan. Menurut konvensi, jenis implementasi disarangkan di dalam jenis entry-point marshaller.
[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedIn, typeof(ExampleMarshaller.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedOut, typeof(ExampleMarshaller.UnmanagedToManagedOut))]
internal static class ExampleMarshaller
{
internal struct ManagedToUnmanagedIn
{
public void FromManaged(TManaged managed) => throw new NotImplementedException();
public TNative ToUnmanaged() => throw new NotImplementedException();
public void Free() => throw new NotImplementedException()
}
internal struct UnmanagedToManagedOut
{
public void FromUnmanaged(TNative unmanaged) => throw new NotImplementedException();
public TManaged ToManaged() => throw new NotImplementedException();
public void Free() => throw new NotImplementedException();
}
}
Deklarasikan marshaller mana yang akan digunakan
Setelah membuat jenis marshaller, Anda dapat menggunakan MarshalUsingAttribute pada tanda tangan metode interop untuk menunjukkan bahwa Anda ingin menggunakan marshaller ini untuk parameter tertentu atau mengembalikan nilai.
MarshalUsingAttribute mengambil jenis marshaller sebagai argumen pada titik masuk, dalam hal ini ExampleMarshaller.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ExampleMarshaller))]
internal static partial Example ConvertExample(
[MarshalUsing(typeof(ExampleMarshaller))] Example example);
Untuk menghindari harus menentukan jenis marshaller untuk setiap penggunaan jenis Example, Anda juga dapat menerapkan NativeMarshallingAttribute ke jenis Example itu sendiri. Ini menunjukkan bahwa marshaller yang ditentukan harus digunakan secara default untuk semua penggunaan jenis Example dalam pembuatan kode sumber interop.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
Jenis Example dapat digunakan dalam metode P/Invoke yang dihasilkan dari sumber tanpa perlu menentukan jenis marshaller. Dalam contoh P/Invoke berikut, ExampleMarshaller akan digunakan untuk mengonversi parameter dari lingkungan terkelola ke lingkungan tak terkelola. Ini juga akan digunakan untuk mengatur nilai pengembalian dari lingkungan tidak dikelola ke lingkungan dikelola.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Untuk menggunakan marshaller yang berbeda untuk parameter tertentu atau mengembalikan nilai jenis Example , tentukan MarshalUsingAttribute di situs penggunaan. Dalam contoh P/Invoke berikut, ExampleMarshaller akan digunakan untuk mengonversi parameter dari lingkungan terkelola ke lingkungan tak terkelola.
OtherExampleMarshaller akan digunakan untuk mentransfer nilai pengembalian dari lingkungan tidak dikelola ke lingkungan yang dikelola.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Pengaturan koleksi
Koleksi non-generik
Untuk koleksi yang tidak generik terhadap tipe elemen, Anda harus membuat tipe marshaller sederhana seperti yang ditunjukkan sebelumnya.
Koleksi generik
Untuk membuat marshaller kustom untuk jenis koleksi generik, Anda dapat menggunakan atribut ContiguousCollectionMarshallerAttribute. Atribut ini menunjukkan bahwa pengirim adalah untuk koleksi yang bersambungan, seperti array atau daftar, dan menyediakan serangkaian metode yang harus diterapkan pengirim untuk mendukung proses pengiriman elemen koleksi. Jenis elemen koleksi yang dikemas juga harus memiliki marshaller yang didefinisikan untuk itu menggunakan metode yang dijelaskan sebelumnya.
Terapkan ContiguousCollectionMarshallerAttribute pada jenis titik masuk marshaller untuk menunjukkan bahwa itu untuk koleksi yang berdekatan secara berurutan. Jenis entry-point marshaller harus memiliki satu parameter tipe lebih banyak daripada tipe yang dikelola yang terkait. Parameter tipe terakhir adalah placeholder dan akan diisi oleh generator sumber dengan tipe unmanaged untuk elemen dalam koleksi.
Misalnya, Anda dapat menentukan marshalling kustom untuk List<T>. Dalam kode berikut, ListMarshaller adalah titik masuk dan implementasinya. Ini sesuai dengan salah satu bentuk marshaller yang diharapkan untuk pengolahan kustom kumpulan. (Perhatikan bahwa ini adalah contoh yang tidak lengkap.)
[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(List<>), MarshalMode.Default, typeof(ListMarshaller<,>.DefaultMarshaller))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
public static class DefaultMarshaller
{
public static byte* AllocateContainerForUnmanagedElements(List<T> managed, out int numElements)
{
numElements = managed.Count;
nuint collectionSizeInBytes = managed.Count * /* size of T */;
return (byte*)NativeMemory.Alloc(collectionSizeInBytes);
}
public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> new Span<TUnmanagedElement>((TUnmanagedElement*)unmanaged, numElements);
public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> new List<T>(length);
public static Span<T> GetManagedValuesDestination(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>((TUnmanagedElement*)nativeValue, numElements);
public static void Free(byte* unmanaged)
=> NativeMemory.Free(unmanaged);
}
}
Dalam contoh, ListMarshaller adalah marshaller koleksi stateless yang mengimplementasikan dukungan untuk marshalling dari dikelola ke tidak terkelola dan dari yang tidak dikelola ke yang dikelola untuk List<T>. Dalam contoh P/Invoke berikut, ListMarshaller akan digunakan untuk melakukan marshaling kontainer koleksi bagi parameter dari yang dikelola ke yang tidak terkelola dan untuk melakukan marshaling kontainer koleksi bagi nilai pengembalian dari yang tidak dikelola ke yang dikelola. Generator sumber akan menghasilkan kode untuk menyalin elemen dari parameter list ke kontainer yang disediakan oleh marshaller. Karena int bisa dipetakan langsung, elemen-elemen itu sendiri tidak perlu dimarshall.
CountElementName menunjukkan bahwa parameter numValues harus digunakan sebagai penghitung elemen saat mengubah nilai pengembalian dari lingkungan tidak dikelola ke lingkungan yang dikelola.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(numValues))]
internal static partial List<int> ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);
Ketika jenis elemen koleksi adalah jenis kustom, Anda dapat menentukan marshaller elemen untuk itu menggunakan tambahan MarshalUsingAttribute dengan ElementIndirectionDepth = 1.
ListMarshaller akan menangani kontainer pengumpulan dan ExampleMarshaller akan mengatur setiap elemen dari lingkungan tidak terkelola ke lingkungan terkelola dan sebaliknya. mengindikasikan ElementIndirectionDepth bahwa marshaller harus diterapkan ke elemen dalam koleksi, yang satu tingkat lebih dalam dari koleksi itu sendiri.
[LibraryImport("nativelib")]
[MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(numValues))]
[MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))]
[MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
List<Example> list,
out int numValues);