Freigeben über


Standardmäßiges Marshalling für Objekte

Aktualisiert: November 2007

Parameter und als System.Object typisierte Felder können als einer der folgenden Typen für nicht verwalteten Code verfügbar gemacht werden:

  • Wenn das Objekt ein Parameter ist, als Variante.

  • Wenn das Objekt ein Strukturfeld ist, als Schnittstelle.

Das Marshallen von Objekttypen wird nur von COM-Interop unterstützt. Standardmäßig werden Objekte an COM-Varianten gemarshallt. Diese Regeln gelten nur für den Typ Object. Sie gelten nicht für Objekte mit starkem Typ, die von der Object-Klasse abgeleitet werden.

In diesem Thema erhalten Sie folgende zusätzliche Informationen zum Marshallen von Objekttypen:

  • Marshallingoptionen

  • Marshalling eines Objekts an eine Schnittstelle

  • Marshalling eines Objekts an eine Variante

  • Marshalling einer Variante an ein Objekt

  • Marshalling von ByRef-Varianten

Marshallingoptionen

In der folgenden Tabelle finden Sie die Optionen für das Marshalling des Datentyps Object. Das MarshalAsAttribute-Attribut stellt mehrere UnmanagedType-Enumerationswerte zum Marshallen von Objekten zur Verfügung.

Enumerationstyp

Beschreibung des nicht verwalteten Formats

UnmanagedType.Struct

(Standard für Parameter)

Variante im COM-Format.

UnmanagedType.Interface

Wenn möglich, eine IDispatch-Schnittstelle; anderenfalls eine IUnknown-Schnittstelle.

UnmanagedType.IUnknown

(Standard für Felder)

Eine IUnknown-Schnittstelle.

UnmanagedType.IDispatch

Eine IDispatch-Schnittstelle.

Das folgende Beispiel demonstriert die verwaltete Schnittstellendefinition für 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();
}

Der folgende Code exportiert die MarshalObject-Schnittstelle in eine Typbibliothek.

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

Der Interop-Marshaller gibt nach dem Aufruf innerhalb der Variante reservierte Objekte automatisch frei.

Im folgenden Beispiel ist ein formatierter Werttyp dargestellt.

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

Der folgende Code exportiert den formatierten Typ in eine Typbibliothek.

struct ObjectHolder {
   VARIANT o1;
   IDispatch *o2;
}

Marshalling eines Objekts an eine Schnittstelle

Wenn ein Objekt für COM als Schnittstelle verfügbar gemacht wird, so ist diese Schnittstelle die Klassenschnittstelle für den verwalteten Typ Object (die _Object-Schnittstelle). Die Schnittstelle wird in der resultierenden Typbibliothek als IDispatch-Schnittstelle (UnmanagedType.IDispatch) oder als IUnknown-Schnittstelle (UnmanagedType.IUnknown) typisiert. COM-Clients können über die _Object-Schnittstelle die Member der verwalteten Klasse oder beliebige der durch seine abgeleitete Klasse implementierten Member dynamisch aufrufen. Der Client kann QueryInterface auch aufrufen, um eine beliebige andere Schnittstelle zu erhalten, die durch den verwalteten Typ explizit implementiert wurde.

Marshalling eines Objekts an eine Variante

Wenn ein Objekt an eine Variante gemarshallt wird, so wird der interne Varianttyp zur Laufzeit anhand der folgenden Regeln bestimmt:

  • Wenn der Objektverweis NULL (Nothing in Visual Basic) ist, wird das Objekt an eine Variante des Typs VT_EMPTY gemarshallt.

  • Wenn es sich bei dem Objekt um eine Instanz eines in der folgenden Tabelle aufgelisteten Typs handelt, wird der resultierende Varianttyp durch die im Marshaller integrierten und in der Tabelle dargestellten Regeln bestimmt.

  • Andere Objekte, die das Verhalten beim Marshalling explizit steuern müssen, können die IConvertible-Schnittstelle implementieren. In diesem Fall wird der Varianttyp durch den Typcode bestimmt, der von der IConvertible.GetTypeCode-Methode zurückgegeben wird. Anderenfalls wird das Objekt als Variante des Typs VT_UNKNOWN gemarshallt.

Marshalling von Systemtypen an Varianten

In der folgenden Tabelle sind verwaltete Objekttypen und die entsprechenden COM-Varianttypen dargestellt. Diese Typen werden nur umgewandelt, wenn die Signatur der aufgerufenen Methode vom Typ System.Object ist.

Objekttyp

COM-Varianttyp

NULL-Objektverweis (Nothing in Visual Basic).

VT_EMPTY

System.DBNull

VT_NULL

System.Runtime.InteropServices.ErrorWrapper

VT_ERROR

System.Reflection.Missing

VT_ERROR mit 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

Unter Verwendung der im vorherigen Beispiel definierten MarshalObject-Schnittstelle demonstriert das folgende Codebeispiel, wie unterschiedliche Varianttypen an einen COM-Server übergeben werden.

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.

COM-Typen, die keine ihnen entsprechenden verwalteten Typen haben, können mit Wrapperklassen wie ErrorWrapper, DispatchWrapper, UnknownWrapper und CurrencyWrapper gemarshallt werden Im folgenden Codebeispiel wird demonstriert, wie diese Wrapper verwendet werden, um unterschiedliche Varianttypen an einen COM-Server zu übergeben.

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

Die Wrapperklassen werden im System.Runtime.InteropServices-Namespace definiert.

Marshalling der IConvertible-Schnittstelle an eine Variante

Mit Ausnahme der im vorherigen Abschnitt aufgelisteten Typen können alle Typen durch Implementierung der IConvertible-Schnittstelle steuern, wie sie gemarshallt werden. Wenn das Objekt die IConvertible-Schnittstelle implementiert, wird der COM-Varianttyp zur Laufzeit anhand des Werts der durch die IConvertible.GetTypeCode-Methode zurückgegebenen TypeCode-Enumeration bestimmt.

In der folgenden Tabelle sind mögliche Werte der TypeCode-Enumeration und die den einzelnen Werten entsprechenden COM-Varianttypen aufgelistet.

TypeCode

COM-Varianttyp

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

Nicht unterstützt.

VT_INT

Nicht unterstützt.

VT_UINT

Nicht unterstützt.

VT_ARRAY

Nicht unterstützt.

VT_RECORD

Nicht unterstützt.

VT_CY

Nicht unterstützt.

VT_VARIANT

Der Wert der COM-Variante wird durch Aufrufen der IConvertible.ToType-Schnittstelle bestimmt; ToType ist hier die Umwandlungsroutine, die dem Typ entspricht, der von IConvertible.GetTypeCode zurückgegeben wurde. Ein Objekt, das TypeCode.Double aus IConvertible.GetTypeCode zurückgibt, wird beispielsweise als COM-Variante des Typs VT_R8 gemarshallt. Den (im dblVal-Feld der COM-Variante gespeicherten) Wert der Variante können Sie erhalten, indem Sie eine Umwandlung in die IConvertible-Schnittstelle durchführen und die ToDouble-Methode aufrufen.

Marshalling einer Variante an ein Objekt

Wenn Varianten an Objekte gemarshallt werden, wird der Typ des erstellten Objekts durch den Typ und in einigen Fällen auch durch den Wert der gemarshallten Variante bestimmt. In der folgenden Tabelle sind die einzelnen Varianttypen und die entsprechenden Objekttypen aufgelistet, die der Marshaller erstellt, wenn eine Variante aus COM an .NET Framework übergeben wird.

COM-Varianttyp

Objekttyp

VT_EMPTY

NULL-Objektverweis (Nothing in Visual Basic).

VT_NULL

System.DBNull

VT_DISPATCH

System.__ComObject, oder NULL, wenn (pdispVal == null)

VT_UNKNOWN

System.__ComObject, oder NULL, wenn (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

Entsprechender geschachtelter Werttyp.

VT_VARIANT

Nicht unterstützt.

Aus COM an verwalteten Code übergebene und dann an COM zurückgegebene Varianttypen behalten für die Dauer des Anrufs möglicherweise nicht denselben Varianttyp bei. Dies ist beispielsweise der Fall, wenn eine Variante des Typs VT_DISPATCH aus COM an .NET Framework übergeben wird. Während des Marshallens wird die Variante in ein System.Object umgewandelt. Wenn das Object dann an COM zurückgegeben wird, wird es an eine Variante des Typs VT_UNKNOWN zurückgemarshallt. Es gibt keine Garantie dafür, dass die Variante, die beim Marshallen eines Objekts aus verwaltetem Code an COM erstellt wird, demselben Typ angehört wie die ursprünglich zur Erstellung des Objekts verwendete Variante.

Marshalling von ByRef-Varianten

Obwohl Varianten durch einen Wert oder durch einen Verweis übergeben werden können, kann das VT_BYREF-Flag auch mit einem beliebigen Varianttyp verwendet werden, um darauf hinzuweisen, dass der Inhalt der Variante nicht durch einen Wert, sondern durch einen Verweis übergeben wird. Der Unterschied zwischen dem Marshalling von Varianten durch einen Verweis und dem Marshalling von Varianten mit eingerichtetem VT_BYREF-Flag kann verwirrend sein. Die folgende Abbildung stellt den Unterschied deutlich dar.

Durch einen Wert und durch einen Verweis übergebene Varianten

An den Stapel übergebener Variant

Standardmäßiges Verhalten beim Marshalling von Objekten und Varianten durch einen Wert

  • Wenn Objekte aus verwaltetem Code an COM übergeben werden, wird der Inhalt des Objekts in eine neue Variante kopiert, die unter Verwendung der unter Marshalling eines Objekts an eine Variante definierten Regeln durch den Marshaller erstellt wurde. Die auf der nicht verwalteten Seite an der Variante vorgenommenen Änderungen werden bei Rückgabe aus dem Aufruf nicht wieder an das ursprüngliche Objekt zurückübertragen.

  • Wenn Varianten aus COM an verwalteten Code übergeben werden, wird der Inhalt der Variante unter Verwendung der unter Marshalling einer Variante an ein Objekt definierten Regeln in ein neu erstelltes Objekt kopiert. Die auf der verwalteten Seite am Objekt vorgenommenen Änderungen werden bei Rückgabe aus dem Aufruf nicht wieder an die ursprüngliche Variante zurückübertragen.

Standardverhalten für das Marshalling von Objekten und Varianten durch einen Verweis

Um Änderungen wieder an den Aufrufer zurückübertragen zu können, müssen die Parameter durch einen Verweis übergeben werden. Das ref-Schlüsselwort kann beispielsweise in C# (bzw. ByRef im verwalteten Code von Visual Basic) zum Übergeben von Parametern durch einen Verweis verwendet werden. In COM werden Verweisparameter mithilfe eines Zeigers (z. B. variant *) übergeben.

  • Wenn ein Objekt durch einen Verweis an COM übergeben wird, erstellt der Marshaller eine neue Variante und kopiert den Inhalt des Objektverweises in die Variante, bevor der Aufruf ausgeführt wird. Die Variante wird an die nicht verwaltete Funktion übergeben; dort kann der Benutzer entscheiden, ob der Inhalt der Variante geändert werden soll. Bei Rückgabe aus dem Aufruf werden die auf der nicht verwalteten Seite an der Variante vorgenommenen Änderungen wieder an das ursprüngliche Objekt zurückübertragen. Wenn sich der Typ der Variante vom Typ der an den Aufruf übergebenen Variante unterscheidet, werden die Änderungen wieder an ein Objekt eines anderen Typs zurückübertragen. Dies bedeutet, dass sich der Typ des an den Aufruf übergebenen Objekts vom Typ des durch den Aufruf zurückgegebenen Objekts unterscheiden kann.

  • Wenn eine Variante durch einen Verweis an verwalteten Code übergeben wird, erstellt der Marshaller ein neues Objekt und kopiert den Inhalt der Variante in das Objekt, bevor der Aufruf ausgeführt wird. Dann wird ein Verweis auf das Objekt an die verwaltete Funktion übergeben. Dort kann der Benutzer entscheiden, ob das Objekt geändert werden soll. Bei Rückgabe aus dem Aufruf werden die am referenzierten Objekt vorgenommenen Änderungen wieder an das ursprüngliche Objekt zurückübertragen. Wenn sich der Typ des Objekts vom Typ des an den Aufruf übergebenen Objekts unterscheidet, wird der Typ der ursprünglichen Variante geändert, und der Wert wird wieder an die Variante zurückübertragen. Auch hier kann sich der Typ der an den Aufruf übergebenen Variante vom Typ der durch den Aufruf zurückgegebenen Variante unterscheiden.

Standardverhalten für das Marshalling von Varianten mit eingerichtetem VT_BYREF-Flag

  • Für eine durch einen Wert an verwalteten Code übergebene Variante kann das VT_BYREF-Flag eingerichtet sein, das darauf hinweist, dass die Variante anstelle eines Werts einen Verweis enthält. In diesem Fall wird die Variante trotzdem an ein Objekt gemarshallt, da sie durch einen Wert übergeben wird. Der Marshaller dereferenziert den Inhalt der Variante automatisch und kopiert sie vor der Ausführung des Aufrufs in ein neu erstelltes Objekt. Dieses Objekt wird dann an die verwaltete Funktion übergeben; bei Rückgabe aus dem Aufruf wird das Objekt jedoch nicht wieder an die ursprüngliche Variante zurückübertragen. Die am verwalteten Objekt vorgenommenen Änderungen gehen dabei verloren.

    Vorsicht:

    Der Wert einer durch einen Wert übergebenen Variante kann nicht geändert werden, auch wenn für die Variante das VT_BYREF-Flag eingerichtet wurde.

  • Für eine durch einen Verweis an verwalteten Code übergebene Variante kann das VT_BYREF-Flag ebenfalls eingerichtet sein, um anzugeben, dass die Variante einen weiteren Verweis enthält. Wenn das der Fall ist, wird die Variante an ein ref-Objekt gemarshallt, da die Variante durch einen Verweis übergeben wird. Der Marshaller dereferenziert den Inhalt der Variante automatisch und kopiert sie vor der Ausführung des Aufrufs in ein neu erstelltes Objekt. Bei Rückgabe aus dem Aufruf wird der Wert des Objekts nur dann an den Verweis in der ursprünglichen Variante zurückübertragen, wenn das Objekt demselben Typ angehört wie das übergebene Objekt. Durch die Weitergabe wird also der Typ einer Variante mit eingerichtetem VT_BYREF-Flag nicht geändert. Wenn der Objekttyp während des Aufrufs geändert wird, wird bei Rückgabe aus dem Aufruf eine InvalidCastException ausgelöst.

In der folgenden Tabelle sind die Regeln für die Weitergabe von Varianten und Objekten zusammengefasst.

Von

Ziel

Zurückübertragene Änderungen

Variant v

Object o

Nie

Object o

Variant v

Nie

Variant *pv

Ref Object o

Immer

Ref object o

Variant *pv

Immer

Variant v(VT_BYREF|VT_*)

Object o

Nie

Variant v(VT_BYREF|VT_)

Ref Object o

Nur, wenn der Typ sich nicht geändert hat.

Siehe auch

Konzepte

Blitfähige und nicht blitfähige Typen

Direktionale Attribute

Kopieren und Fixieren

Weitere Ressourcen

Standardmäßiges Marshallingverhalten