Bagikan melalui


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.CustomMarshalerenumerasi .

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").