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.To Type, kde To Type je rutina převodu, 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:
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 |
---|---|---|
Varianta v | Objekt o | Nikdy |
Objekt o | Varianta v | Nikdy |
Varianta * pv | Ref – objekt o | Always |
Ref – objekt o | Varianta * pv | Always |
Varianta v (VT_BYREF VT_ | *) | Objekt o | Nikdy |
Varianta v (VT_BYREF VT_ | ) | Ref – objekt o | Pouze pokud se typ nezměnil. |