Penyusunan Default untuk Objek

Parameter dan bidang yang diketik sebagai System.Object dapat diperlihatkan ke kode yang tidak dikelola sebagai salah satu dari jenis berikut:

  • Varian ketika objek adalah parameter.

  • Antarmuka ketika objek adalah bidang struktur.

Hanya interop COM yang mendukung marshalling untuk jenis objek. Perilaku default adalah memarshall objek ke varian COM. Aturan ini hanya berlaku untuk jenis Objek dan tidak berlaku untuk objek yang bertipe kuat yang berasal dari kelas Objek.

Opsi Marshalling

Tabel berikut menunjukkan opsi marshalling untuk jenis data Objek. Atribut MarshalAsAttribute menyediakan beberapa nilai enumerasi UnmanagedType ke objek susunan.

Jenis Enumerasi Deskripsi format yang tidak terkelola
UnmanagedType.Struct

(default untuk parameter)
Varian gaya COM.
UnmanagedType.Interface Antarmuka IDispatch, jika memungkinkan; jika tidak, antarmuka IUnknown.
UnmanagedType.IUnknown

(default untuk bidang)
Antarmuka IUnknown.
UnmanagedType.IDispatch Antarmuka IDispatch.

Contoh berikut menunjukkan definisi antarmuka terkelola untuk MarshalObject.

Interface MarshalObject
   Sub SetVariant(o As Object)
   Sub SetVariantRef(ByRef o As Object)
   Function GetVariant() As Object

   Sub SetIDispatch( <MarshalAs(UnmanagedType.IDispatch)> o As Object)
   Sub SetIDispatchRef(ByRef <MarshalAs(UnmanagedType.IDispatch)> o _
      As Object)
   Function GetIDispatch() As <MarshalAs(UnmanagedType.IDispatch)> Object
   Sub SetIUnknown( <MarshalAs(UnmanagedType.IUnknown)> o As Object)
   Sub SetIUnknownRef(ByRef <MarshalAs(UnmanagedType.IUnknown)> o _
      As Object)
   Function GetIUnknown() As <MarshalAs(UnmanagedType.IUnknown)> Object
End Interface
interface MarshalObject {
   void SetVariant(Object o);
   void SetVariantRef(ref Object o);
   Object GetVariant();

   void SetIDispatch ([MarshalAs(UnmanagedType.IDispatch)]Object o);
   void SetIDispatchRef([MarshalAs(UnmanagedType.IDispatch)]ref Object o);
   [MarshalAs(UnmanagedType.IDispatch)] Object GetIDispatch();
   void SetIUnknown ([MarshalAs(UnmanagedType.IUnknown)]Object o);
   void SetIUnknownRef([MarshalAs(UnmanagedType.IUnknown)]ref Object o);
   [MarshalAs(UnmanagedType.IUnknown)] Object GetIUnknown();
}

Kode berikut mengekspor antarmuka MarshalObject ke pustaka jenis.

interface MarshalObject {
   HRESULT SetVariant([in] VARIANT o);
   HRESULT SetVariantRef([in,out] VARIANT *o);
   HRESULT GetVariant([out,retval] VARIANT *o)
   HRESULT SetIDispatch([in] IDispatch *o);
   HRESULT SetIDispatchRef([in,out] IDispatch **o);
   HRESULT GetIDispatch([out,retval] IDispatch **o)
   HRESULT SetIUnknown([in] IUnknown *o);
   HRESULT SetIUnknownRef([in,out] IUnknown **o);
   HRESULT GetIUnknown([out,retval] IUnknown **o)
}

Catatan

Marshaller interop secara otomatis membebaskan objek yang dialokasikan di dalam varian setelah panggilan.

Contoh berikut menunjukkan jenis nilai yang diformat.

Public Structure ObjectHolder
   Dim o1 As Object
   <MarshalAs(UnmanagedType.IDispatch)> Public o2 As Object
End Structure
public struct ObjectHolder {
   Object o1;
   [MarshalAs(UnmanagedType.IDispatch)]public Object o2;
}

Kode berikut mengekspor tipe yang diformat ke pustaka tipe.

struct ObjectHolder {
   VARIANT o1;
   IDispatch *o2;
}

Marshalling Objek ke Antarmuka

Ketika sebuah objek diekspos ke COM sebagai antarmuka, antarmuka tersebut adalah antarmuka kelas untuk tipe terkelola Object (antarmuka _Object). Antarmuka ini diketik sebagai IDispatch (UnmanagedType) atau IUnknown (UnmanagedType.IUnknown) di pustaka jenis yang dihasilkan. Klien COM dapat secara dinamis memanggil anggota kelas terkelola atau anggota apa pun yang diterapkan oleh kelas turunannya melalui antarmuka _Object. Klien juga dapat memanggil QueryInterface untuk mendapatkan antarmuka lain yang secara eksplisit diimplementasikan oleh jenis terkelola.

Marshalling Object ke Varian

Saat objek disusun ke varian, jenis varian internal ditentukan pada waktu proses, berdasarkan aturan berikut:

  • Jika referensi objek adalah null (Tidak ada dalam Visual Basic), objek akan dirangkai menjadi varian tipe VT_EMPTY.

  • Jika objek adalah instans dari jenis apa pun yang tercantum dalam tabel berikut, jenis varian yang dihasilkan ditentukan oleh aturan yang dibangun ke dalam marshaller dan ditampilkan dalam tabel.

  • Objek lain yang perlu secara eksplisit mengontrol perilaku penyusunan dapat mengimplementasikan antarmuka IConvertible. Dalam hal ini, jenis varian ditentukan oleh kode jenis yang dikembalikan dari metode IConvertible.GetTypeCode. Jika tidak, objek akan disusun sebagai varian dari jenis VT_UNKNOWN.

Jenis Sistem Marshalling ke Varian

Tabel berikut menunjukkan jenis objek terkelola dan jenis varian COM yang sesuai. Jenis ini dikonversi hanya jika tanda tangan dari metode yang dipanggil berjenis System.Object.

Tipe objek Jenis varian COM
Referensi objek null (Tidak ada dalam Visual Basic). VT_EMPTY
System.DBNull VT_NULL
System.Runtime.InteropServices.ErrorWrapper VT_ERROR
System.Reflection.Missing VT_ERROR with E_PARAMNOTFOUND
System.Runtime.InteropServices.DispatchWrapper VT_DISPATCH
System.Runtime.InteropServices.UnknownWrapper VT_UNKNOWN
System.Runtime.InteropServices.CurrencyWrapper VT_CY
System.Boolean VT_BOOL
System.SByte VT_I1
System.Byte VT_UI1
System.Int16 VT_I2
System.UInt16 VT_UI2
System.Int32 VT_I4
System.UInt32 VT_UI4
System.Int64 VT_I8
System.UInt64 VT_UI8
System.Single VT_R4
System.Double VT_R8
System.Decimal VT_DECIMAL
System.DateTime VT_DATE
System.String VT_BSTR
System.IntPtr VT_INT
System.UIntPtr VT_UINT
System.Array VT_ARRAY

Menggunakan antarmuka MarshalObject yang ditentukan dalam contoh sebelumnya, contoh kode berikut menunjukkan cara meneruskan berbagai jenis varian ke server COM.

Dim mo As New MarshalObject()
mo.SetVariant(Nothing)         ' Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value) ' Marshal as variant of type VT_NULL.
mo.SetVariant(CInt(27))        ' Marshal as variant of type VT_I2.
mo.SetVariant(CLng(27))        ' Marshal as variant of type VT_I4.
mo.SetVariant(CSng(27.0))      ' Marshal as variant of type VT_R4.
mo.SetVariant(CDbl(27.0))      ' Marshal as variant of type VT_R8.
MarshalObject mo = new MarshalObject();
mo.SetVariant(null);            // Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value); // Marshal as variant of type VT_NULL.
mo.SetVariant((int)27);          // Marshal as variant of type VT_I2.
mo.SetVariant((long)27);          // Marshal as variant of type VT_I4.
mo.SetVariant((single)27.0);   // Marshal as variant of type VT_R4.
mo.SetVariant((double)27.0);   // Marshal as variant of type VT_R8.

Jenis COM yang tidak memiliki jenis terkelola yang sesuai dapat disusun menggunakan kelas pembungkus seperti ErrorWrapper, DispatchWrapper, UnknownWrapper, dan CurrencyWrapper. Contoh kode berikut menunjukkan cara menggunakan pembungkus ini untuk meneruskan berbagai jenis varian ke server COM.

Imports System.Runtime.InteropServices
' Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(New UnknownWrapper(inew))
' Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(New DispatchWrapper(inew))
' Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(New ErrorWrapper(&H80054002))
' Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(New CurrencyWrapper(New Decimal(5.25)))
using System.Runtime.InteropServices;
// Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(new UnknownWrapper(inew));
// Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(new DispatchWrapper(inew));
// Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(new ErrorWrapper(0x80054002));
// Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(new CurrencyWrapper(new Decimal(5.25)));

Kelas pembungkus didefinisikan dalam System.Runtime.InteropServices namespace layanan.

Marshalling Antarmuka IConvertible ke Varian

Jenis selain yang tercantum di bagian sebelumnya dapat mengontrol cara penyusunannya dengan menerapkan antarmuka IConvertible. Jika objek mengimplementasikan antarmuka IConvertible, jenis varian COM ditentukan pada waktu berjalan dengan nilai enumerasi TypeCode yang dikembalikan dari metode IConvertible.GetTypeCode.

Tabel berikut menunjukkan kemungkinan nilai untuk enumerasi TypeCode dan jenis varian COM yang sesuai untuk setiap nilai.

TypeCode Jenis varian COM
TypeCode.Empty VT_EMPTY
TypeCode.Object VT_UNKNOWN
TypeCode.DBNull VT_NULL
TypeCode.Boolean VT_BOOL
TypeCode.Char VT_UI2
TypeCode.Sbyte VT_I1
TypeCode.Byte VT_UI1
TypeCode.Int16 VT_I2
TypeCode.UInt16 VT_UI2
TypeCode.Int32 VT_I4
TypeCode.UInt32 VT_UI4
TypeCode.Int64 VT_I8
TypeCode.UInt64 VT_UI8
TypeCode.Single VT_R4
TypeCode.Double VT_R8
TypeCode.Decimal VT_DECIMAL
TypeCode.DateTime VT_DATE
TypeCode.String VT_BSTR
Tidak didukung. VT_INT
Tidak didukung. VT_UINT
Tidak didukung. VT_ARRAY
Tidak didukung. VT_RECORD
Tidak didukung. VT_CY
Tidak didukung. VT_VARIANT

Nilai varian COM ditentukan dengan memanggil antarmuka IConvertible.ToType, dengan ToType merupakan rutinitas konversi yang sesuai dengan jenis yang dikembalikan dari IConvertible.GetTypeCode. Misalnya, objek yang mengembalikan TypeCode.Double dari IConvertible.GetTypeCode disusun sebagai varian COM dari jenis VT_R8. Anda dapat memperoleh nilai varian (disimpan di bidang dblVal varian COM) dengan mentransmisikan ke antarmuka IConvertible dan memanggil metode ToDouble.

Varian Marshalling ke Objek

Saat menyusun varian ke suatu objek, jenis, dan terkadang nilainya, dari varian yang disusun menentukan jenis objek yang dihasilkan. Tabel berikut mengidentifikasi setiap jenis varian dan jenis objek terkait yang dibuat oleh m marshallerreates saat varian diteruskan dari COM ke .NET Framework.

Jenis varian COM Tipe objek
VT_EMPTY Referensi objek null (Tidak ada dalam Visual Basic).
VT_NULL System.DBNull
VT_DISPATCH System.__ComObject or null if (pdispVal == null)
VT_UNKNOWN System.__ComObject or null if (punkVal == null)
VT_ERROR System.UInt32
VT_BOOL System.Boolean
VT_I1 System.SByte
VT_UI1 System.Byte
VT_I2 System.Int16
VT_UI2 System.UInt16
VT_I4 System.Int32
VT_UI4 System.UInt32
VT_I8 System.Int64
VT_UI8 System.UInt64
VT_R4 System.Single
VT_R8 System.Double
VT_DECIMAL System.Decimal
VT_DATE System.DateTime
VT_BSTR System.String
VT_INT System.Int32
VT_UINT System.UInt32
VT_ARRAY | VT_* System.Array
VT_CY System.Decimal
VT_RECORD Jenis nilai kotak yang sesuai.
VT_VARIANT Tidak didukung.

Jenis varian yang diteruskan dari COM ke kode terkelola lalu kembali ke COM mungkin tidak mempertahankan jenis varian yang sama selama panggilan berlangsung. Pertimbangkan apa yang terjadi ketika varian tipe VT_DISPATCH diteruskan dari COM ke .NET Framework. Selama menyusun, varian diubah menjadi System.Object. Jika Objek kemudian diteruskan kembali ke COM, maka akan disusun kembali ke varian jenis VT_UNKNOWN. Tidak ada jaminan bahwa varian yang dihasilkan saat objek disusun dari kode terkelola ke COM akan memiliki tipe yang sama dengan varian yang awalnya digunakan untuk memproduksi objek.

Marshalling ByRef Varian

Meskipun varian itu sendiri dapat diteruskan dengan nilai atau referensi, tanda VT_BYREF juga dapat digunakan dengan jenis varian apa pun untuk menunjukkan bahwa konten varian diteruskan dengan referensi, bukan berdasarkan nilai. Perbedaan antara menyusun varian dengan referensi dan menyusun varian dengan kumpulan bendera VT_BYREF dapat membingungkan. Ilustrasi berikut menjelaskan perbedaannya:

Diagram that shows variant passed on the stack. Varian yang diteruskan oleh nilai dan berdasarkan referensi

Perilaku default untuk menyusun objek dan varian berdasarkan nilai

  • Saat meneruskan objek dari kode terkelola ke COM, konten objek disalin ke varian baru yang dibuat oleh marshaller, menggunakan aturan yang ditentukan dalam Marshalling Object to Variant. Perubahan yang dilakukan pada varian di sisi yang tidak terkelola tidak disebarkan kembali ke objek asli saat kembali dari panggilan.

  • Saat meneruskan varian dari COM ke kode terkelola, konten varian disalin ke objek yang baru dibuat, menggunakan aturan yang ditentukan dalam Membuat Varian ke Objek. Perubahan yang dibuat pada objek di sisi terkelola tidak disebarkan kembali ke varian asli saat kembali dari panggilan.

Perilaku default untuk menyusun objek dan varian berdasarkan referensi

Untuk menyebarkan perubahan kembali ke pemanggil, parameter harus diteruskan dengan referensi. Misalnya, Anda dapat menggunakan kata kunci ref dalam C# (atau ByRef dalam kode terkelola Visual Basic) untuk meneruskan parameter dengan referensi. Di COM, parameter referensi diteruskan menggunakan pointer seperti varian *.

  • Saat meneruskan objek ke COM dengan referensi, marshaller membuat varian baru dan menyalin konten referensi objek ke varian sebelum panggilan dilakukan. Varian diteruskan ke fungsi yang tidak dikelola tempat pengguna bebas untuk mengubah isi varian. Saat kembali dari panggilan, setiap perubahan yang dilakukan pada varian di sisi yang tidak dikelola disebarkan kembali ke objek asli. Jika jenis varian berbeda dari jenis varian yang diteruskan ke panggilan, perubahan akan disebarkan kembali ke objek dari jenis yang berbeda. Artinya, jenis objek yang diteruskan ke panggilan dapat berbeda dari jenis objek yang dikembalikan dari panggilan.

  • Saat meneruskan varian ke kode terkelola dengan referensi, marshaller membuat objek baru dan menyalin konten varian ke objek sebelum melakukan panggilan. Referensi ke objek diteruskan ke fungsi terkelola. Di sini, pengguna bebas untuk mengubah objek. Saat kembali dari panggilan, setiap perubahan yang dibuat pada objek yang direferensikan akan disebarkan kembali ke varian aslinya. Jika jenis objek berbeda dari jenis objek yang diteruskan ke panggilan, jenis varian asli diubah dan nilainya disebarkan kembali ke varian. Sekali lagi, jenis varian yang diteruskan ke panggilan dapat berbeda dari jenis varian yang dikembalikan dari panggilan.

Perilaku default untuk marshalling varian dengan menetapkan bendera VT_BYREF

  • Varian yang diteruskan ke kode terkelola berdasarkan nilai dapat memiliki tanda VT_BYREF yang diatur untuk menunjukkan bahwa varian berisi referensi, bukan nilai. Dalam hal ini, varian masih dirangkai ke suatu objek karena varian tersebut diteruskan oleh nilai. Marshaller secara otomatis melakukan dereferensi isi varian dan menyalinnya ke objek yang baru dibuat sebelum melakukan panggilan. Objek tersebut kemudian diteruskan ke fungsi yang dikelola; namun, saat kembali dari panggilan, objek tidak disebarkan kembali ke varian aslinya. Perubahan yang dibuat pada objek terkelola akan hilang.

    Perhatian

    Tidak ada cara untuk mengubah nilai varian yang diteruskan oleh nilai, meskipun varian telah menetapkan bendera VT_BYREF.

  • Varian yang diteruskan ke kode terkelola menurut referensi dapat menetapkan bendera VT_BYREF untuk menunjukkan bahwa varian berisi referensi, bukan referensi. Jika ya, varian akan dirangkai ke objek ref karena varian diteruskan dengan referensi. Marshaller secara otomatis melakukan dereferensi isi varian dan menyalinnya ke objek yang baru dibuat sebelum melakukan panggilan. Saat kembali dari panggilan, nilai objek disebarkan kembali ke referensi dalam varian asli hanya jika objek berjenis sama dengan objek yang diteruskan. Artinya, penyebaran tidak mengubah jenis varian dengan penetapan bendera VT_BYREF. Jika jenis objek diubah selama panggilan, InvalidCastException terjadi saat kembali dari panggilan.

Tabel berikut merangkum aturan penyebaran untuk varian dan objek.

Dari Untuk Perubahan disebarkan kembali
Variantv Objecto Tidak pernah
Objecto Variantv Tidak pernah
Variant*pv Ref Objecto Selalu
Ref objecto Variant*pv Selalu
Variantv(VT_BYREF|VT_*) Objecto Tidak pernah
Variantv(VT_BYREF|VT_) Ref Objecto Hanya jika jenisnya tidak berubah.

Lihat juga