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 jenis IConvertible.To, di mana Jenis Ke adalah 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:
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 |
---|---|---|
Varian v | Objek o | Tidak pernah |
Objek o | Varian v | Tidak pernah |
Varian * pv | Objek Ref o | Selalu |
Objek ref o | Varian * pv | Selalu |
Varian v (VT_ VT_BYREF | *) | Objek o | Tidak pernah |
Varian v (VT_ VT_BYREF | ) | Objek Ref o | Hanya jika jenisnya tidak berubah. |