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 typu IConvertible.To, gdzie ToType 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:

Diagram that shows variant passed on the stack. 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
Wariantv Obiekto Nigdy
Obiekto Wariantv Nigdy
Wariant*pv Obiekt ref o Zawsze
Obiekt ref o Wariant*pv Zawsze
Variantv(VT_BYREF VT_|*) Obiekto Nigdy
Variantv(VT_BYREF VT_|) Obiekt ref o Tylko wtedy, gdy typ nie uległ zmianie.

Zobacz też