Výchozí zařazování objektů

Parametry a pole napsaná tak, jak System.Object je možné zpřístupnit nespravovanému kódu jako jeden z následujících typů:

  • Varianta, pokud je objekt parametrem.

  • Rozhraní, pokud je objekt pole struktury.

Pouze zprostředkovatele komunikace modelu COM podporuje zařazování pro typy objektů. Výchozí chování je zařazování objektů do variant modelu COM. Tato pravidla platí pouze pro typ Object a nevztahují se na objekty silného typu, které jsou odvozeny z třídy Object .

Možnosti zařazování

V následující tabulce jsou uvedeny možnosti zařazování datového typu Objekt . Atribut MarshalAsAttribute poskytuje několik UnmanagedType hodnot výčtu pro zařazování objektů.

Typ výčtu Popis nespravovaného formátu
UnmanagedType.Struct

(výchozí hodnota pro parametry)
Varianta ve stylu modelu COM.
UnmanagedType.Interface IDispatch rozhraní, pokud je to možné; jinak IUnknown rozhraní.
Nespravovaný typ.IUnknown

(výchozí pro pole)
Rozhraní IUnknown .
Nespravovaný typ.IDispatch Rozhraní IDispatch .

Následující příklad ukazuje definici spravovaného rozhraní pro 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();
}

Následující kód exportuje MarshalObject rozhraní do knihovny typů.

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)
}

Poznámka:

Zařazovač vzájemné spolupráce automaticky uvolní všechny přidělené objekty uvnitř varianty po volání.

Následující příklad ukazuje formátovaný typ hodnoty.

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;
}

Následující kód exportuje formátovaný typ do knihovny typů.

struct ObjectHolder {
   VARIANT o1;
   IDispatch *o2;
}

Zařazování objektu do rozhraní

Když je objekt vystaven modelu COM jako rozhraní, toto rozhraní je rozhraní třídy pro spravovaný typ Object ( rozhraní _Object ). Toto rozhraní je zadáno jako IDispatch (UnmanagedType) nebo IUnknown (UnmanagedType.IUnknown) ve výsledné knihovně typů. Klienti modelu COM mohou dynamicky vyvolat členy spravované třídy nebo jakékoli členy implementované jeho odvozené třídy prostřednictvím rozhraní _Object . Klient může také volat QueryInterface k získání jakéhokoli jiného rozhraní explicitně implementovaného spravovaným typem.

Zařazování objektu na variantu

Pokud je objekt zařazován na variantu, je interní typ varianty určen za běhu na základě následujících pravidel:

  • Pokud je odkaz na objekt null (Nothing v jazyce Visual Basic), objekt se zařadí do varianty typu VT_EMPTY.

  • Pokud je objekt instance libovolného typu uvedeného v následující tabulce, výsledný typ varianty je určen pravidly integrovanými do marshalleru a zobrazeným v tabulce.

  • Další objekty, které potřebují explicitně řídit chování zařazování, mohou implementovat IConvertible rozhraní. V takovém případě je typ varianty určen kódem typu vráceným z IConvertible.GetTypeCode metody. V opačném případě se objekt zařadí jako varianta typu VT_UNKNOWN.

Zařazování typů systému na variantu

V následující tabulce jsou uvedeny typy spravovaných objektů a jejich odpovídající typy variant modelu COM. Tyto typy jsou převedeny pouze v případech, kdy podpis volané metody je typu System.Object.

Object type Typ varianty modelu COM
Odkaz na objekt null (Nothing v jazyce Visual Basic) VT_EMPTY
System.DBNull VT_NULL
System.Runtime.InteropServices.ErrorWrapper VT_ERROR
System.Reflection.Missing VT_ERROR s 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

MarshalObject Pomocí rozhraní definovaného v předchozím příkladu ukazuje následující příklad kódu, jak předat různé typy variant serveru 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 modelu COM, které nemají odpovídající spravované typy, lze zařaďovat pomocí tříd obálky, jako ErrorWrapperjsou , DispatchWrapper, UnknownWrappera CurrencyWrapper. Následující příklad kódu ukazuje, jak tyto obálky použít k předání různých typů variant serveru 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)));

Třídy obálky jsou definovány v System.Runtime.InteropServices oboru názvů.

Marshalling the IConvertible Interface to Variant

Jiné typy než typy uvedené v předchozí části můžou řídit, jak jsou zařazovány implementací IConvertible rozhraní. Pokud objekt implementuje IConvertible rozhraní, typ varianty MODELU COM je určen za běhu hodnotou výčtu TypeCode vráceného IConvertible.GetTypeCode z metody.

Následující tabulka uvádí možné hodnoty pro výčet TypeCode a odpovídající typ varianty modelu COM pro každou hodnotu.

Typecode Typ varianty 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
Nepodporováno VT_INT
Nepodporováno VT_UINT
Nepodporováno VT_ARRAY
Nepodporováno VT_RECORD
Nepodporováno VT_CY
Nepodporováno VT_VARIANT

Hodnota varianty modelu COM je určena voláním rozhraní IConvertible.ToType, kde ToType je konverzní rutina, která odpovídá typu, který byl vrácen z IConvertible.GetTypeCode. Například objekt, který vrací TypeCode.Double z IConvertible.GetTypeCode je zařazován jako varianta COM typu VT_R8. Hodnotu varianty (uloženou v poli dblVal varianty modelu COM) můžete získat přetypováním do rozhraní IConvertible a voláním ToDouble metody.

Marshalling Variant to Object

Při zařazování varianty k objektu určuje typ a někdy hodnota zařazované varianty typ vytvořeného objektu. Následující tabulka identifikuje každý typ varianty a odpovídající typ objektu, který m marshallerreates, když je varianta předána z modelu COM do rozhraní .NET Framework.

Typ varianty modelu COM Object type
VT_EMPTY Odkaz na objekt null (Nothing v jazyce Visual Basic)
VT_NULL System.DBNull
VT_DISPATCH System.__ComObject nebo null, pokud (pdispVal == null)
VT_UNKNOWN System.__ComObject nebo null, pokud (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 Odpovídající typ hodnoty v poli
VT_VARIANT Nepodporováno

Variantní typy předávané z modelu COM do spravovaného kódu a potom zpět do modelu COM nemusí uchovávat stejný typ varianty po dobu trvání volání. Zvažte, co se stane, když je varianta typu VT_DISPATCH předána z modelu COM do rozhraní .NET Framework. Během zařazování je varianta převedena na System.Object. Pokud je objekt předán zpět modelu COM, je zařazován zpět do varianty typu VT_UNKNOWN. Neexistuje žádná záruka, že varianta vytvořená v případě, že je objekt zařašován ze spravovaného kódu na com, bude stejný typ jako varianta původně použitá k vytvoření objektu.

Marshalling ByRef Variants

I když samotné varianty mohou být předány podle hodnoty nebo odkazu, příznak VT_BYREF lze použít také s libovolným typem varianty k označení, že obsah varianty se předává odkazem místo hodnotou. Rozdíl mezi variantami zařazování pomocí odkazu a zařazování varianty se sadou příznaků VT_BYREF může být matoucí. Následující obrázek vysvětluje rozdíly:

Diagram that shows variant passed on the stack. Varianty předané podle hodnoty a odkazem

Výchozí chování pro zařazování objektů a variant podle hodnoty

  • Při předávání objektů ze spravovaného kódu do modelu COM se obsah objektu zkopíruje do nové varianty vytvořené marshallerem pomocí pravidel definovaných v Marshalling Object to Variant. Změny provedené u varianty na nespravované straně se nešíří zpět do původního objektu při návratu z volání.

  • Při předávání variant z modelu COM do spravovaného kódu se obsah varianty zkopíruje do nově vytvořeného objektu pomocí pravidel definovaných v marshalling Variant to Object. Změny provedené u objektu na spravované straně se nešířily zpět do původní varianty při návratu z volání.

Výchozí chování pro zařazování objektů a variant podle odkazu

Chcete-li rozšířit změny zpět do volajícího, musí být parametry předány odkazem. Pomocí klíčového slova ref v jazyce C# (nebo ByRef ve spravovaném kódu jazyka Visual Basic) můžete například předat parametry odkazem. V modelu COM se referenční parametry předávají pomocí ukazatele, například varianty *.

  • Při předávání objektu com odkazem, marshaller vytvoří novou variantu a zkopíruje obsah odkazu na objekt do varianty před voláním. Varianta se předává nespravované funkci, kde uživatel může změnit obsah varianty. Při návratu z volání se všechny změny provedené u varianty na nespravované straně rozšíří zpět do původního objektu. Pokud se typ varianty liší od typu varianty předané volání, změny se rozšíří zpět do objektu jiného typu. To znamená, že typ objektu předaného do volání se může lišit od typu objektu vráceného z volání.

  • Při předávání varianty spravovaného kódu odkazem vytvoří marshaller nový objekt a zkopíruje obsah varianty do objektu před voláním. Odkaz na objekt se předá spravované funkci, kde uživatel může objekt změnit. Při návratu z volání se všechny změny provedené v odkazovaném objektu rozšíří zpět do původní varianty. Pokud se typ objektu liší od typu objektu předaného volání, změní se typ původní varianty a hodnota se rozšíří zpět do varianty. Typ varianty předávané do volání se může opět lišit od typu varianty vrácené voláním.

Výchozí chování pro zařazování varianty se sadou příznaků VT_BYREF

  • Varianta předávaná spravovanému kódu podle hodnoty může obsahovat příznak VT_BYREF , který označuje, že varianta obsahuje odkaz místo hodnoty. V tomto případě je varianta stále zařazována do objektu, protože varianta je předána hodnotou. Marshaller automaticky dereference obsah varianty a zkopíruje ho do nově vytvořeného objektu před voláním. Objekt se pak předá do spravované funkce; nicméně při návratu z volání objekt není rozšířen zpět do původní varianty. Změny provedené u spravovaného objektu se ztratí.

    Upozornění

    Neexistuje způsob, jak změnit hodnotu varianty předané hodnotou, i když má varianta nastavenou VT_BYREF příznakem.

  • Varianta předávaná spravovanému kódu odkazem může mít také nastavenou VT_BYREF příznak, který označuje, že varianta obsahuje jiný odkaz. Pokud ano, varianta je zařazována do objektu ref , protože varianta je předána odkazem. Marshaller automaticky dereference obsah varianty a zkopíruje ho do nově vytvořeného objektu před voláním. Při návratu z volání se hodnota objektu rozšíří zpět na odkaz v rámci původní varianty pouze v případě, že objekt je stejného typu jako objekt předaný. To znamená, že šíření nezmění typ varianty se sadou příznaků VT_BYREF . Pokud se typ objektu během volání změní, InvalidCastException dojde k návratu z volání.

Následující tabulka shrnuje pravidla šíření pro varianty a objekty.

Z Záměr Změny se rozšířily zpět
Variantav Objekto Nikdy
Objekto Variantav Nikdy
Variantní*pv Ref – objekto Always
Ref – objekto Variantní*pv Always
Variantav(VT_BYREF VT_|*) Objekto Nikdy
Variantav(VT_BYREF VT_|) Ref – objekto Pouze pokud se typ nezměnil.

Viz také