Domyślne marshalling dla obiektów
Parametry i pola wpisane jako System.Object mogą być widoczne dla niezarządzanego kodu jako jednego z następujących typów:
Wariant, gdy obiekt jest parametrem.
Interfejs, gdy obiekt jest polem struktury.
Tylko interop COM obsługuje marshalling dla typów obiektów. Domyślnym zachowaniem jest przeprowadzanie marshalingu obiektów do wariantów MODELU COM. Te reguły dotyczą tylko typu Object i nie mają zastosowania do silnie typiowanych obiektów, które pochodzą z klasy Object .
Opcje marshalingu
W poniższej tabeli przedstawiono opcje marshalingu dla typu danych Obiekt . Atrybut MarshalAsAttribute zawiera kilka UnmanagedType wartości wyliczenia do marshalingu obiektów.
Typ wyliczenia | Opis formatu niezarządzanego |
---|---|
UnmanagedType.Struct (wartość domyślna dla parametrów) |
Wariant stylu COM. |
UnmanagedType.Interface | Interfejs IDispatch , jeśli to możliwe, w przeciwnym razie interfejs IUnknown . |
UnmanagedType.IUnknown (wartość domyślna dla pól) |
Interfejs IUnknown . |
Niezarządzany typ.IDispatch | Interfejs IDispatch . |
W poniższym przykładzie przedstawiono definicję interfejsu zarządzanego dla elementu 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();
}
Poniższy kod eksportuje MarshalObject
interfejs do biblioteki typów.
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)
}
Uwaga
Marshaller międzyoperacyjny automatycznie zwalnia wszystkie przydzielone obiekty wewnątrz wariantu po wywołaniu.
W poniższym przykładzie przedstawiono sformatowany typ wartości.
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;
}
Poniższy kod eksportuje sformatowany typ do biblioteki typów.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Marshalling, obiekt do interfejsu
Gdy obiekt jest uwidoczniony dla modelu COM jako interfejsu, interfejs ten jest interfejsem klasy dla typu Object zarządzanego ( interfejs _Object ). Ten interfejs jest wpisywany jako IDispatch (UnmanagedType) lub IUnknown (UnmanagedType.IUnknown) w wynikowej bibliotece typów. Klienci com mogą dynamicznie wywoływać składowe klasy zarządzanej lub dowolnych składowych implementowanych przez jej klasy pochodne za pośrednictwem interfejsu _Object . Klient może również wywołać metodę QueryInterface , aby uzyskać dowolny inny interfejs jawnie zaimplementowany przez typ zarządzany.
Marshalling Object to Variant
Gdy obiekt jest skierowany do wariantu, wewnętrzny typ wariantu jest określany w czasie wykonywania na podstawie następujących reguł:
Jeśli odwołanie do obiektu ma wartość null (Nic w Visual Basic), obiekt jest przeznaczony do wariantu typu VT_EMPTY.
Jeśli obiekt jest wystąpieniem dowolnego typu wymienionego w poniższej tabeli, wynikowy typ wariantu jest określany przez reguły wbudowane w marshaller i pokazane w tabeli.
Inne obiekty, które muszą jawnie kontrolować zachowanie marshallingu, mogą implementować IConvertible interfejs. W takim przypadku typ wariantu jest określany przez kod typu zwrócony z IConvertible.GetTypeCode metody . W przeciwnym razie obiekt jest marshalled jako wariant typu VT_UNKNOWN.
Typy systemu marshalling do wariantu
W poniższej tabeli przedstawiono typy obiektów zarządzanych i odpowiadające im typy wariantów MODELU COM. Te typy są konwertowane tylko wtedy, gdy sygnatura wywoływanej metody jest typu System.Object.
Object type | Typ wariantu MODELU COM |
---|---|
Odwołanie do obiektu o wartości null (nic w Visual Basic). | VT_EMPTY |
System.DBNull | VT_NULL |
System.Runtime.InteropServices.ErrorWrapper | VT_ERROR |
System.Reflection.Missing | VT_ERROR z 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 |
Korzystając z interfejsu MarshalObject
zdefiniowanego w poprzednim przykładzie, poniższy przykład kodu pokazuje, jak przekazać różne typy wariantów do serwera 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.
Typy COM, które nie mają odpowiednich typów zarządzanych, można rozdzielić przy użyciu klas otoki, takich jak ErrorWrapper, DispatchWrapper, UnknownWrapperi CurrencyWrapper. W poniższym przykładzie kodu pokazano, jak używać tych otoek do przekazywania różnych typów wariantów do serwera 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)));
Klasy otoki są definiowane System.Runtime.InteropServices w przestrzeni nazw.
Marshalling IConvertible Interface to Variant (Marshalling interfejsU IConvertible do wariantu)
Typy inne niż wymienione w poprzedniej sekcji mogą kontrolować sposób ich działania przez zaimplementowanie interfejsu IConvertible . Jeśli obiekt implementuje interfejs IConvertible, typ wariantu COM jest określany w czasie wykonywania przez wartość TypeCode wyliczenia zwróconego IConvertible.GetTypeCode z metody .
W poniższej tabeli przedstawiono możliwe wartości dla wyliczenia TypeCode i odpowiadający typ wariantu COM dla każdej wartości.
TypeCode | Typ wariantu MODELU 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 |
Nieobsługiwane. | VT_INT |
Nieobsługiwane. | VT_UINT |
Nieobsługiwane. | VT_ARRAY |
Nieobsługiwane. | VT_RECORD |
Nieobsługiwane. | VT_CY |
Nieobsługiwane. | VT_VARIANT |
Wartość wariantu COM jest określana przez wywołanie interfejsu IConvertible.To Type, gdzie To Type jest procedurą konwersji odpowiadającą typowi zwróconego z interfejsu IConvertible.GetTypeCode. Na przykład obiekt, który zwraca typeCode.Double z IConvertible.GetTypeCode, jest marshalled jako wariant COM typu VT_R8. Możesz uzyskać wartość wariantu (przechowywanego w polu dblVal wariantu COM), rzutując do interfejsu IConvertible i wywołując metodę ToDouble .
Wariant marshalling do obiektu
Podczas określania wariantu obiektu typ, a czasami wartość wariantu marshalled określa typ produkowanego obiektu. Poniższa tabela identyfikuje każdy typ wariantu i odpowiadający mu typ obiektu, który m marshallerreates po przekazaniu wariantu z modelu COM do programu .NET Framework.
Typ wariantu MODELU COM | Object type |
---|---|
VT_EMPTY | Odwołanie do obiektu o wartości null (nic w Visual Basic). |
VT_NULL | System.DBNull |
VT_DISPATCH | System.__ComObject lub null, jeśli (pdispVal == null) |
VT_UNKNOWN | System.__ComObject lub null, jeśli (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 | Odpowiadający typ wartości pola. |
VT_VARIANT | Nieobsługiwane. |
Typy wariantów przekazywane z modelu COM do kodu zarządzanego, a następnie z powrotem do modelu COM mogą nie zachować tego samego typu wariantu przez czas trwania wywołania. Zastanów się, co się stanie, gdy wariant typu VT_DISPATCH jest przekazywany z modelu COM do programu .NET Framework. Podczas marshallingu wariant jest konwertowany na System.Object. Jeśli obiekt jest następnie przekazywany z powrotem do MODELU COM, jest on rozdzielany z powrotem do wariantu typu VT_UNKNOWN. Nie ma gwarancji, że wariant generowany, gdy obiekt jest rozdzielany z kodu zarządzanego do MODELU COM, będzie tego samego typu, co wariant początkowo używany do tworzenia obiektu.
Warianty Marshalling ByRef
Mimo że same warianty mogą być przekazywane przez wartość lub odwołanie, flaga VT_BYREF może być również używana z dowolnym typem wariantu, aby wskazać, że zawartość wariantu jest przekazywana przez odwołanie, a nie przez wartość. Różnica między wariantami marshallingu przez odwołanie i marshalling wariantu z zestawem flag VT_BYREF może być myląca. Poniższa ilustracja wyjaśnia różnice:
Warianty przekazywane według wartości i według odwołania
Domyślne zachowanie dla obiektów marshalingu i wariantów według wartości
Podczas przekazywania obiektów z kodu zarządzanego do modelu COM zawartość obiektu jest kopiowana do nowego wariantu utworzonego przez marshallera przy użyciu reguł zdefiniowanych w obiekcie Marshalling Na Wariant. Zmiany wprowadzone w wariantie po stronie niezarządzanej nie są propagowane z powrotem do oryginalnego obiektu po powrocie z wywołania.
Podczas przekazywania wariantów z modelu COM do kodu zarządzanego zawartość wariantu jest kopiowana do nowo utworzonego obiektu przy użyciu reguł zdefiniowanych w języku Marshalling Variant to Object. Zmiany wprowadzone w obiekcie po stronie zarządzanej nie są propagowane z powrotem do oryginalnego wariantu po powrocie z wywołania.
Domyślne zachowanie dla obiektów marshalingu i wariantów według odwołania
Aby propagować zmiany z powrotem do elementu wywołującego, parametry muszą zostać przekazane przez odwołanie. Na przykład możesz użyć słowa kluczowego ref w języku C# (lub ByRef w kodzie zarządzanym języka Visual Basic), aby przekazać parametry według odwołania. W modelu COM parametry odwołania są przekazywane przy użyciu wskaźnika, takiego jak wariant *.
Podczas przekazywania obiektu do modelu COM przez odwołanie marshaller tworzy nowy wariant i kopiuje zawartość odwołania do obiektu do wariantu przed wykonaniem wywołania. Wariant jest przekazywany do funkcji niezarządzanej, w której użytkownik może zmienić zawartość wariantu. Po powrocie z wywołania wszelkie zmiany wprowadzone w wariantie po stronie niezarządzanej są propagowane z powrotem do oryginalnego obiektu. Jeśli typ wariantu różni się od typu wariantu przekazanego do wywołania, zmiany są propagowane z powrotem do obiektu innego typu. Oznacza to, że typ obiektu przekazanego do wywołania może różnić się od typu obiektu zwróconego z wywołania.
Podczas przekazywania wariantu do kodu zarządzanego przez odwołanie marshaller tworzy nowy obiekt i kopiuje zawartość wariantu do obiektu przed wywołaniem. Odwołanie do obiektu jest przekazywane do funkcji zarządzanej, gdzie użytkownik może zmienić obiekt. Po powrocie z wywołania wszelkie zmiany wprowadzone w odwołanym obiekcie są propagowane z powrotem do oryginalnego wariantu. Jeśli typ obiektu różni się od typu obiektu przekazanego do wywołania, typ oryginalnego wariantu jest zmieniany, a wartość jest propagowana z powrotem do wariantu. Ponownie typ wariantu przekazanego do wywołania może różnić się od typu wariantu zwróconego z wywołania.
Domyślne zachowanie w przypadku marshalingu wariantu z ustawioną flagą VT_BYREF
Wariant przekazywany do kodu zarządzanego według wartości może mieć ustawioną flagę VT_BYREF wskazującą, że wariant zawiera odwołanie zamiast wartości. W tym przypadku wariant jest nadal przenoszony do obiektu, ponieważ wariant jest przekazywany przez wartość. Marshaller automatycznie wyłusza zawartość wariantu i kopiuje ją do nowo utworzonego obiektu przed wykonaniem wywołania. Obiekt jest następnie przekazywany do funkcji zarządzanej; jednak po powrocie z wywołania obiekt nie jest propagowany z powrotem do oryginalnego wariantu. Zmiany wprowadzone w obiekcie zarządzanym zostaną utracone.
Uwaga
Nie ma możliwości zmiany wartości wariantu przekazanego przez wartość, nawet jeśli wariant ma ustawioną flagę VT_BYREF .
Wariant przekazywany do kodu zarządzanego przez odwołanie może również mieć ustawioną flagę VT_BYREF wskazującą, że wariant zawiera inne odwołanie. Jeśli tak, wariant jest przenoszony do obiektu ref , ponieważ wariant jest przekazywany przez odwołanie. Marshaller automatycznie wyłusza zawartość wariantu i kopiuje ją do nowo utworzonego obiektu przed wykonaniem wywołania. Po powrocie z wywołania wartość obiektu jest propagowana z powrotem do odwołania w oryginalnym wariantie tylko wtedy, gdy obiekt jest tego samego typu co przekazany obiekt. Oznacza to, że propagacja nie zmienia typu wariantu z ustawionym flagą VT_BYREF . Jeśli typ obiektu zostanie zmieniony podczas wywołania, wystąpi po InvalidCastException powrocie z wywołania.
Poniższa tabela zawiera podsumowanie reguł propagacji dla wariantów i obiektów.
Źródło | Działanie | Zmiany propagowane z powrotem |
---|---|---|
Wariant v | Obiekt o | Nigdy |
Obiekt o | Wariant v | Nigdy |
Wariant * pv | Obiekt ref o | Zawsze |
Obiekt ref o | Wariant * pv | Zawsze |
Variant v (VT_BYREF VT_ | *) | Obiekt o | Nigdy |
Variant v (VT_BYREF VT_ | ) | Obiekt ref o | Tylko wtedy, gdy typ nie uległ zmianie. |