Antarmuka System.Runtime.InteropServices.ICustomMarshaler
Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.
Antarmuka ICustomMarshaler menyediakan pembungkus kustom untuk menangani panggilan metode.
Marshaller menyediakan jembatan antara fungsionalitas antarmuka lama dan baru. Marshaling kustom memberikan manfaat berikut:
- Ini memungkinkan aplikasi klien yang dirancang untuk bekerja dengan antarmuka lama untuk juga bekerja dengan server yang menerapkan antarmuka baru.
- Ini memungkinkan aplikasi klien yang dibangun untuk bekerja dengan antarmuka baru untuk bekerja dengan server yang mengimplementasikan antarmuka lama.
Jika Anda memiliki antarmuka yang memperkenalkan perilaku marshaling yang berbeda atau yang diekspos ke Model Objek Komponen (COM) dengan cara yang berbeda, Anda dapat merancang marshaller kustom alih-alih menggunakan marshaller interop. Dengan menggunakan marshaller kustom, Anda dapat meminimalkan perbedaan antara komponen .NET Framework baru dan komponen COM yang ada.
Misalnya, Anda mengembangkan antarmuka terkelola yang disebut INew
. Ketika antarmuka ini diekspos ke COM melalui pembungkus panggilan COM standar (CCW), antarmuka ini memiliki metode yang sama dengan antarmuka terkelola dan menggunakan aturan marshaling yang dibangun ke dalam marshaller interop. Sekarang misalkan antarmuka COM terkenal yang disebut IOld
sudah menyediakan fungsionalitas yang sama dengan INew
antarmuka. Dengan merancang marshaller kustom, Anda dapat memberikan implementasi yang tidak terkelola IOld
yang hanya mendelegasikan panggilan ke implementasi antarmuka terkelola INew
. Oleh karena itu, marshaller kustom bertindak sebagai jembatan antara antarmuka yang dikelola dan tidak dikelola.
Catatan
Marshaller kustom tidak dipanggil saat memanggil dari kode terkelola ke kode yang tidak dikelola pada antarmuka khusus pengiriman.
Tentukan jenis marshaling
Sebelum Anda dapat membangun marshaller kustom, Anda harus menentukan antarmuka terkelola dan tidak terkelola yang akan di-marshal. Antarmuka ini biasanya melakukan fungsi yang sama tetapi diekspos secara berbeda dengan objek yang dikelola dan tidak dikelola.
Kompiler terkelola menghasilkan antarmuka terkelola dari metadata, dan antarmuka yang dihasilkan terlihat seperti antarmuka terkelola lainnya. Contoh berikut menunjukkan antarmuka umum.
public interface INew
{
void NewMethod();
}
Public Interface INew
Sub NewMethod()
End Interface
Anda menentukan jenis yang tidak dikelola dalam Bahasa Definisi Antarmuka (IDL) dan mengkompilasinya dengan pengkompilasi Microsoft Interface Definition Language (MIDL). Anda menentukan antarmuka dalam pernyataan pustaka dan menetapkanNYA ID antarmuka dengan atribut pengidentifikasi unik universal (UUID), seperti yang ditunjukkan contoh berikut.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
[uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
interface IOld : IUnknown
HRESULT OldMethod();
}
Pengkompilasi MIDL menghasilkan beberapa file output. Jika antarmuka didefinisikan dalam Old.idl, file output Old_i.c menentukan const
variabel dengan pengidentifikasi antarmuka (IID) antarmuka, seperti yang ditunjukkan contoh berikut.
const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};
File Old.h juga diproduksi oleh MIDL. Ini berisi definisi C++ antarmuka yang dapat disertakan dalam kode sumber C++Anda.
Menerapkan antarmuka ICustomMarshaler
Marshaller kustom Anda harus mengimplementasikan ICustomMarshaler antarmuka untuk menyediakan pembungkus yang sesuai ke runtime.
Kode C# berikut menampilkan antarmuka dasar yang harus diimplementasikan oleh semua marshaller kustom.
public interface ICustomMarshaler
{
Object MarshalNativeToManaged(IntPtr pNativeData);
IntPtr MarshalManagedToNative(Object ManagedObj);
void CleanUpNativeData(IntPtr pNativeData);
void CleanUpManagedData(Object ManagedObj);
int GetNativeDataSize();
}
Public Interface ICustomMarshaler
Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
Sub CleanUpNativeData( pNativeData As IntPtr )
Sub CleanUpManagedData( ManagedObj As Object )
Function GetNativeDataSize() As Integer
End Interface
Antarmuka ICustomMarshaler mencakup metode yang memberikan dukungan konversi, dukungan pembersihan, dan informasi tentang data yang akan dinamai.
Jenis operasi | Metode ICustomMarshaler | Deskripsi |
---|---|---|
Konversi (dari asli ke kode terkelola) | MarshalNativeToManaged | Marshals pointer ke data asli ke dalam objek terkelola. Metode ini mengembalikan pembungkus yang dapat dipanggil runtime kustom (RCW) yang dapat melakukan marshal antarmuka yang tidak dikelola yang diteruskan sebagai argumen. Marshaller harus mengembalikan instans RCW kustom untuk jenis tersebut. |
Konversi (dari terkelola ke kode asli) | MarshalManagedToNative | Marsekal objek terkelola ke dalam penunjuk ke data asli. Metode ini mengembalikan pembungkus yang dapat dipanggil COM kustom (CCW) yang dapat melakukan marshal antarmuka terkelola yang diteruskan sebagai argumen. Marshaller harus mengembalikan instans CCW kustom untuk jenis tersebut. |
Pembersihan (kode asli) | CleanUpNativeData | Memungkinkan marshaller membersihkan data asli (CCW) yang dikembalikan oleh MarshalManagedToNative metode . |
Pembersihan (kode terkelola) | CleanUpManagedData | Memungkinkan marshaller membersihkan data terkelola (RCW) yang dikembalikan oleh MarshalNativeToManaged metode . |
Informasi (tentang kode asli) | GetNativeDataSize | Mengembalikan ukuran data yang tidak dikelola untuk di-marshal. |
Konversi
ICustomMarshaler.MarshalNativeToManaged
Marshals pointer ke data asli ke dalam objek terkelola. Metode ini mengembalikan pembungkus yang dapat dipanggil runtime kustom (RCW) yang dapat melakukan marshal antarmuka yang tidak dikelola yang diteruskan sebagai argumen. Marshaller harus mengembalikan instans RCW kustom untuk jenis tersebut.
ICustomMarshaler.MarshalManagedToNative
Marsekal objek terkelola ke dalam penunjuk ke data asli. Metode ini mengembalikan pembungkus yang dapat dipanggil COM kustom (CCW) yang dapat melakukan marshal antarmuka terkelola yang diteruskan sebagai argumen. Marshaller harus mengembalikan instans CCW kustom untuk jenis tersebut.
Pembersihan
ICustomMarshaler.CleanUpNativeData
Memungkinkan marshaller membersihkan data asli (CCW) yang dikembalikan oleh MarshalManagedToNative metode .
ICustomMarshaler.CleanUpManagedData
Memungkinkan marshaller membersihkan data terkelola (RCW) yang dikembalikan oleh MarshalNativeToManaged metode .
Informasi ukuran
ICustomMarshaler.GetNativeDataSize
Mengembalikan ukuran data yang tidak dikelola untuk di-marshal.
Catatan
Jika marshaller kustom memanggil metode apa pun yang mengatur kesalahan P/Invoke terakhir saat melakukan marshaling dari asli ke terkelola atau saat membersihkan, nilai yang dikembalikan oleh Marshal.GetLastWin32Error() dan Marshal.GetLastPInvokeError() akan mewakili panggilan dalam panggilan marshaling atau pembersihan. Ini dapat menyebabkan kesalahan terlewatkan saat menggunakan marshaller kustom dengan P/Invokes dengan DllImportAttribute.SetLastError diatur ke true
. Untuk mempertahankan kesalahan P/Invoke terakhir, gunakan Marshal.GetLastPInvokeError() metode dan Marshal.SetLastPInvokeError(Int32) dalam ICustomMarshaler implementasi.
Menerapkan metode GetInstance
Selain menerapkan ICustomMarshaler antarmuka, marshaller kustom harus menerapkan static
metode yang disebut GetInstance
yang menerima String sebagai parameter dan memiliki jenis ICustomMarshalerpengembalian . Metode ini static
dipanggil oleh lapisan interop COM runtime bahasa umum untuk membuat instans marshaller kustom. String yang diteruskan ke GetInstance
adalah cookie yang dapat digunakan metode untuk menyesuaikan marshaller kustom yang dikembalikan. Contoh berikut menunjukkan implementasi minimal, tetapi lengkap ICustomMarshaler .
public class NewOldMarshaler : ICustomMarshaler
{
public static ICustomMarshaler GetInstance(string pstrCookie)
=> new NewOldMarshaler();
public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
public int GetNativeDataSize() => throw new NotImplementedException();
}
Terapkan MarshalAsAttribute
Untuk menggunakan marshaller kustom, Anda harus menerapkan MarshalAsAttribute atribut ke parameter atau bidang yang sedang di-marshal.
Anda juga harus meneruskan UnmanagedType.CustomMarshaler nilai enumerasi ke MarshalAsAttribute konstruktor. Selain itu, Anda harus menentukan MarshalType bidang dengan salah satu parameter bernama berikut:
MarshalType (wajib): Nama marshaller kustom yang memenuhi syarat rakitan. Nama harus menyertakan namespace layanan dan kelas marshaller kustom. Jika marshaller kustom tidak didefinisikan dalam rakitan tempatnya digunakan, Anda harus menentukan nama rakitan tempat assembly didefinisikan.
Catatan
Anda dapat menggunakan MarshalTypeRef bidang alih-alih MarshalType bidang . MarshalTypeRef mengambil jenis yang lebih mudah ditentukan.
MarshalCookie (opsional): Cookie yang diteruskan ke marshaller kustom. Anda dapat menggunakan cookie untuk memberikan informasi tambahan kepada marshaller. Misalnya, jika marshaller yang sama digunakan untuk menyediakan sejumlah pembungkus, cookie mengidentifikasi pembungkus tertentu. Cookie diteruskan ke
GetInstance
metode marshaller.
Atribut MarshalAsAttribute mengidentifikasi marshaller kustom sehingga dapat mengaktifkan pembungkus yang sesuai. Layanan interop runtime bahasa umum kemudian memeriksa atribut dan membuat marshaller kustom saat pertama kali argumen (parameter atau bidang) perlu di-marshal.
Runtime kemudian memanggil MarshalNativeToManaged metode dan MarshalManagedToNative pada marshaller kustom untuk mengaktifkan pembungkus yang benar untuk menangani panggilan.
Menggunakan marshaller kustom
Ketika marshaller kustom selesai, Anda dapat menggunakannya sebagai pembungkus kustom untuk jenis tertentu. Contoh berikut menunjukkan definisi antarmuka terkelola IUserData
:
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
Dalam contoh berikut, IUserData
antarmuka menggunakan marshaller NewOldMarshaler
kustom untuk memungkinkan aplikasi klien yang IOld
tidak dikelola untuk meneruskan antarmuka ke DoSomeStuff
metode . Deskripsi terkelola dari DoSomeStuff
metode ini mengambil antarmuka, seperti yang INew
ditunjukkan pada contoh sebelumnya, sedangkan versi DoSomeStuff
yang tidak dikelola mengambil IOld
penunjuk antarmuka, seperti yang ditunjukkan dalam contoh berikut.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
[uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
interface IUserData : IUnknown
HRESULT DoSomeStuff(IUnknown* pIOld);
}
Pustaka jenis yang dihasilkan dengan mengekspor definisi IUserData
terkelola menghasilkan definisi yang tidak dikelola yang ditunjukkan dalam contoh ini alih-alih definisi standar. Atribut MarshalAsAttribute yang diterapkan ke INew
argumen dalam definisi terkelola metode DoSomeStuff
menunjukkan bahwa argumen menggunakan marshaller kustom, seperti yang ditunjukkan contoh berikut.
using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
void DoSomeStuff(
[MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType="NewOldMarshaler")]
INew pINew
);
}
Public Interface IUserData
Sub DoSomeStuff( _
<MarshalAs(UnmanagedType.CustomMarshaler, _
MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface
Dalam contoh sebelumnya, parameter pertama yang disediakan untuk MarshalAsAttribute atribut adalah UnmanagedType.CustomMarshaler nilai UnmanagedType.CustomMarshaler
enumerasi .
Parameter kedua adalah MarshalType bidang , yang menyediakan nama marshaller kustom yang memenuhi syarat rakitan. Nama ini terdiri dari namespace layanan dan kelas marshaller kustom (MarshalType="MyCompany.NewOldMarshaler"
).