Objektumok alapértelmezett rendezése

A nem felügyelt kód számára elérhető paraméterek és mezők a következő típusok egyikeként vannak begépelve System.Object :

  • Egy variáns, ha az objektum paraméter.

  • Felület, ha az objektum egy struktúramező.

Csak a COM-interop támogatja az objektumtípusok rendezését. Az alapértelmezett viselkedés az objektumok COM-variánsokra való beállítása. Ezek a szabályok csak az Objektum típusra vonatkoznak, és nem vonatkoznak az objektumosztályból származó, erősen gépelt objektumokra.

Rendezési beállítások

Az alábbi táblázat az objektum adattípusának rendezési beállításait mutatja be. Az MarshalAsAttribute attribútum számos UnmanagedType enumerálási értéket biztosít a marsall objektumok számára.

Számbavétel típusa A nem felügyelt formátum leírása
UnmanagedType.Struct

(a paraméterek alapértelmezett beállítása)
COM-stílusú változat.
UnmanagedType.Interface IDispatch-felület, ha lehetséges; ellenkező esetben egy IUnknown felület.
UnmanagedType.IUnknown

(a mezők alapértelmezett beállítása)
Egy IUnknown felület.
UnmanagedType.IDispatch IDispatch interfész.

Az alábbi példa a felügyelt felület definícióját mutatja be a következőhöz 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();
}

Az alábbi kód egy típustárba exportálja a MarshalObject felületet.

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

Feljegyzés

Az interop marshaller automatikusan felszabadít minden lefoglalt objektumot a változaton belül a hívás után.

Az alábbi példa egy formázott értéktípust mutat be.

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

A következő kód exportálja a formázott típust egy típustárba.

struct ObjectHolder {
   VARIANT o1;
   IDispatch *o2;
}

Objektum és felület rendezése

Amikor egy objektum felületként jelenik meg a COM-nak, az a felügyelt típus Object osztályfelülete (a _Object felület). Ez a felület IDispatch () vagy IUnknown (UnmanagedType.IUnknown) néven van begépelve az eredményül kapott típustárba.UnmanagedType A COM-ügyfelek dinamikusan meghívhatják a felügyelt osztály tagjait vagy a származtatott osztályok által implementált tagokat a _Object felületen keresztül. Az ügyfél meghívhatja a QueryInterface-t is, hogy beszerezze a felügyelt típus által explicit módon implementált bármely más felületet.

Objektum rendezése Variant-ra

Ha egy objektumot egy variánshoz rendeznek, a belső változattípus futásidőben lesz meghatározva a következő szabályok alapján:

  • Ha az objektumhivatkozás null (a Visual Basicben semmi), az objektum egy VT_EMPTY típusú változatra lesz rendezve.

  • Ha az objektum az alábbi táblázatban felsorolt bármilyen típusú példány, az eredményül kapott változattípust a rendezőbe beépített és a táblázatban látható szabályok határozzák meg.

  • Más objektumok, amelyeknek explicit módon kell szabályozni a rendezési viselkedést, implementálhatják az interfészt IConvertible . Ebben az esetben a variáns típusát a metódusból IConvertible.GetTypeCode visszaadott típuskód határozza meg. Ellenkező esetben az objektum VT_UNKNOWN típusú változatként van rendezve.

Rendszertípusok rendezése Variant-ra

Az alábbi táblázat a felügyelt objektumtípusokat és a hozzájuk tartozó COM-változattípusokat mutatja be. Ezek a típusok csak akkor lesznek konvertálva, ha a meghívott metódus aláírása típus System.Object.

Objektumtípus COM-változat típusa
Null objektumhivatkozás (a Visual Basicben semmi). VT_EMPTY
System.DBNull VT_NULL
System.Runtime.InteropServices.ErrorWrapper VT_ERROR
System.Reflection.Missing VT_ERROR 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 Az előző példában definiált felület használatával az alábbi kódpéldából megtudhatja, hogyan adhat át különböző típusú változatokat egy COM-kiszolgálónak.

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.

Azok a COM-típusok, amelyek nem rendelkeznek megfelelő felügyelt típusokkal, burkolóosztályokkal, például ErrorWrapper, DispatchWrapper, UnknownWrapperés CurrencyWrapper. Az alábbi példakód bemutatja, hogyan használhatja ezeket a burkolókat különböző típusú változatok com-kiszolgálónak való átadására.

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

A burkolóosztályok a System.Runtime.InteropServices névtérben vannak definiálva.

Az IConvertible interfész a Variant-hoz való rendezése

Az előző szakaszban felsoroltaktól eltérő típusok a felület implementálásával szabályozhatják a IConvertible rendezés módját. Ha az objektum megvalósítja az IConvertible felületet, a COM-variánstípust futásidőben határozza meg a TypeCode metódusból IConvertible.GetTypeCode visszaadott számbavétel értéke.

Az alábbi táblázat a TypeCode enumerálás lehetséges értékeit és az egyes értékekhez tartozó COM-variánstípust mutatja be.

TypeCode COM-változat típusa
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.Decimális VT_DECIMAL
TypeCode.DateTime VT_DATE
TypeCode.String VT_BSTR
Nem támogatott. VT_INT
Nem támogatott. VT_UINT
Nem támogatott. VT_ARRAY
Nem támogatott. VT_RECORD
Nem támogatott. VT_CY
Nem támogatott. VT_VARIANT

A COM-variáns értékét a IConvertible.ToTípus felület meghívásával határozzuk meg, ahol a Beírás az IConvertible.GetTypeCode-ból visszaadott típusnak megfelelő konverziós rutin. Például az IConvertible.GetTypeCode-ból typeCode.Double értéket visszaadó objektum a VT_R8 típusú COM-változatként van rendezve. A variáns értékét (a COM-változat dblVal mezőjében tárolva) az IConvertible felületre való öntéssel és a ToDouble metódus meghívásával szerezheti be.

Variant objektumba rendezése

Amikor egy variánst objektumhoz rendez, a rendezővariáns típusa és néha értéke határozza meg a létrehozott objektum típusát. Az alábbi táblázat azonosítja az egyes változattípusokat és a megfelelő objektumtípusokat, amelyeket az m marshallerreates ad át a COM-ból a .NET-keretrendszer.

COM-változat típusa Objektumtípus
VT_EMPTY Null objektumhivatkozás (a Visual Basicben semmi).
VT_NULL System.DBNull
VT_DISPATCH System.__ComObject vagy null, ha (pdispVal == null)
VT_UNKNOWN System.__ComObject vagy null ha (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 Megfelelő dobozos értéktípus.
VT_VARIANT Nem támogatott.

Előfordulhat, hogy a COM-ból a felügyelt kódba, majd a COM-ba visszakerülő változattípusok a hívás időtartama alatt nem őrzik meg ugyanazt a változattípust. Gondolja át, mi történik, ha egy VT_DISPATCH típusú változatot ad át a COM-ból a .NET-keretrendszer. A rendezés során a rendszer átalakítja a variánst .System.Object Ha az objektumot ezután visszaküldi a COM-nak, a VT_UNKNOWN típusú változatra kerül vissza. Nincs garancia arra, hogy az objektum felügyelt kódból COM-ba való beállításakor előállított változat ugyanaz a típus lesz, mint az objektum előállításához eredetileg használt változat.

ByRef-változatok rendezése

Bár maguk a variánsok átadhatók érték vagy hivatkozás alapján, a VT_BYREF jelző bármilyen változattípussal is használható annak jelzésére, hogy a változat tartalma hivatkozással, nem pedig érték alapján van átadva. A változatok hivatkozással történő rendezése és a változatok VT_BYREF jelölőkészlettel való rendezése közötti különbség zavaró lehet. Az alábbi ábra tisztázza a különbségeket:

Diagram that shows variant passed on the stack. Érték és hivatkozás szerint átadott variánsok

Objektumok és változatok érték szerinti rendezési viselkedése

  • Ha objektumokat ad át a felügyelt kódból a COM-nak, az objektum tartalma a rendező által létrehozott új változatba lesz másolva a Marshalling Object to Variant parancsban meghatározott szabályok használatával. A nem felügyelt oldalon végrehajtott módosításokat a rendszer nem propagálja vissza az eredeti objektumra a hívásból való visszatéréskor.

  • Ha a com-ról felügyelt kódra továbbítja a variánsokat, a rendszer egy újonnan létrehozott objektumba másolja a változat tartalmát a Marshalling Variant objektumba definiált szabályokkal. A felügyelt oldalon az objektum módosításai nem lesznek visszaterjesztve az eredeti változatra a hívásból való visszatéréskor.

Objektumok és változatok beállításának alapértelmezett viselkedése hivatkozás alapján

Ha a módosításokat vissza szeretné propagálja a hívónak, a paramétereket hivatkozással kell átadni. Használhatja például a ref kulcsszót A C# (vagy ByRef a Visual Basic felügyelt kódjában) használatával paraméterek hivatkozás alapján történő átadásához. A COM-ban a referenciaparaméterek egy mutató, például egy *változat használatával lesznek átadva.

  • Amikor egy objektumot hivatkozással ad át a COM-nak, a rendező létrehoz egy új változatot, és a hívás előtt átmásolja az objektumhivatkozás tartalmát a változatba. A variáns a nem felügyelt függvénynek lesz átadva, ahol a felhasználó szabadon módosíthatja a változat tartalmát. A hívásból való visszatéréskor a nem felügyelt oldalon végrehajtott módosításokat a rendszer visszaterjeszti az eredeti objektumra. Ha a változat típusa eltér a hívásnak átadott változat típusától, a rendszer a módosításokat egy másik típusú objektumra propagálja. Vagyis a hívásba átadott objektum típusa eltérhet a hívásból visszaadott objektum típusától.

  • Ha egy változatot referencia alapján ad át a felügyelt kódnak, a rendező létrehoz egy új objektumot, és a hívás előtt átmásolja a változat tartalmát az objektumba. Az objektumra mutató hivatkozást a rendszer átadja a felügyelt függvénynek, ahol a felhasználó szabadon módosíthatja az objektumot. A hívásból való visszatéréskor a hivatkozott objektumon végrehajtott módosítások az eredeti változatra lesznek propagálva. Ha az objektum típusa eltér a hívásnak átadott objektum típusától, az eredeti változat típusa megváltozik, és az érték visszakerül a változatba. A hívásba átadott változat típusa is eltérhet a hívásból visszaadott változat típusától.

A VT_BYREF jelzőkészlettel rendelkező változatok beállításának alapértelmezett viselkedése

  • A felügyelt kódnak érték szerint átadott változatok VT_BYREF jelzővel jelezhetik, hogy a változat egy érték helyett egy hivatkozást tartalmaz. Ebben az esetben a variáns továbbra is egy objektumhoz van rendezve, mert a variánst érték adja át. A rendező automatikusan elhalasztja a változat tartalmát, és egy újonnan létrehozott objektumba másolja a hívást. Az objektum ezután bekerül a felügyelt függvénybe; azonban a hívásból való visszatéréskor az objektum nem lesz újra propagálva az eredeti változatba. A felügyelt objektum módosításai elvesznek.

    Figyelemfelhívás

    Az érték által átadott változatok értékét nem lehet módosítani, még akkor sem, ha a változat VT_BYREF jelölőkészlettel rendelkezik.

  • A felügyelt kódnak hivatkozással átadott változatok VT_BYREF jelzővel is jelezhetik, hogy a változat egy másik hivatkozást tartalmaz. Ha igen, akkor a variáns egy ref objektumhoz lesz rendezve, mert a variánst hivatkozással továbbítja. A rendező automatikusan elhalasztja a változat tartalmát, és egy újonnan létrehozott objektumba másolja a hívást. A hívásból való visszatéréskor az objektum értékét a rendszer csak akkor propagálja vissza az eredeti változatban lévő hivatkozásra, ha az objektum típusa megegyezik az átadott objektum típusával. Ez azt jelenti, hogy a propagálás nem módosítja a VT_BYREF jelölőkészlettel rendelkező változat típusát. Ha az objektum típusa módosul a hívás során, InvalidCastException a hívásból való visszatéréskor történik.

Az alábbi táblázat összefoglalja a variánsok és objektumok propagálási szabályait.

Forrás Művelet A módosítások újraterjesztése
Variantv Objektumo Soha
Objektumo Variantv Soha
Variant*pv Ref Objecto Mindig
Ref objektumo Variant*pv Mindig
Variantv(VT_BYREF|VT_*) Objektumo Soha
Variantv(VT_BYREF|VT_) Ref Objecto Csak akkor, ha a típus nem változott.

Lásd még