Delen via


Standaard marshalling voor objecten

Parameters en velden die als zodanig zijn getypt System.Object , kunnen worden blootgesteld aan onbeheerde code als een van de volgende typen:

  • Een variant wanneer het object een parameter is.

  • Een interface wanneer het object een structuurveld is.

Alleen COM-interop ondersteunt marshalling voor objecttypen. Het standaardgedrag is marshal-objecten voor COM-varianten. Deze regels zijn alleen van toepassing op het type Object en zijn niet van toepassing op sterk getypte objecten die zijn afgeleid van de objectklasse .

Marshallopties

In de volgende tabel ziet u de marshallopties voor het gegevenstype Object . Het MarshalAsAttribute kenmerk biedt verschillende UnmanagedType opsommingswaarden voor marshal-objecten.

Opsommingstype Beschrijving van niet-beheerde indeling
UnmanagedType.Struct

(standaard voor parameters)
Een COM-stijlvariant.
UnmanagedType.Interface Een IDispatch-interface , indien mogelijk; anders een IUnknown-interface .
UnmanagedType.IUnknown

(standaard voor velden)
Een IUnknown interface.
UnmanagedType.IDispatch Een IDispatch-interface .

In het volgende voorbeeld ziet u de definitie van de beheerde interface voor 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();
}

Met de volgende code wordt de MarshalObject interface geëxporteerd naar een typebibliotheek.

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

Notitie

De interop marshaller verwijdert automatisch elk toegewezen object in de variant na de aanroep.

In het volgende voorbeeld ziet u een opgemaakt waardetype.

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

Met de volgende code wordt het opgemaakte type geëxporteerd naar een typebibliotheek.

struct ObjectHolder {
   VARIANT o1;
   IDispatch *o2;
}

Marshalling Object to Interface

Wanneer een object als interface aan COM wordt blootgesteld, is die interface de klasse-interface voor het beheerde type Object (de _Object-interface ). Deze interface wordt getypt als een IDispatch (UnmanagedType) of een IUnknown (UnmanagedType.IUnknown) in de resulterende typebibliotheek. COM-clients kunnen de leden van de beheerde klasse dynamisch aanroepen of leden die zijn geïmplementeerd door de afgeleide klassen via de _Object-interface . De client kan ook QueryInterface aanroepen om een andere interface te verkrijgen die expliciet door het beheerde type is geïmplementeerd.

Marshalling-object naar variant

Wanneer een object wordt ge marshalld naar een variant, wordt het interne varianttype tijdens runtime bepaald op basis van de volgende regels:

  • Als de objectverwijzing null is (niets in Visual Basic), wordt het object ge marshalld naar een variant van het type VT_EMPTY.

  • Als het object een exemplaar is van een type dat wordt vermeld in de volgende tabel, wordt het resulterende varianttype bepaald door de regels die zijn ingebouwd in de marshaller en worden weergegeven in de tabel.

  • Andere objecten die expliciet het marshallgedrag moeten beheren, kunnen de IConvertible interface implementeren. In dat geval wordt het varianttype bepaald door de typecode die wordt geretourneerd door de IConvertible.GetTypeCode methode. Anders wordt het object ge marshalld als een variant van het type VT_UNKNOWN.

Marshalling Systeemtypen naar variant

In de volgende tabel ziet u beheerde objecttypen en de bijbehorende COM-varianttypen. Deze typen worden alleen geconverteerd wanneer de handtekening van de methode die wordt aangeroepen van het type System.Objectis.

Object type COM-varianttype
Null-objectverwijzing (niets in Visual Basic). VT_EMPTY
System.DBNull VT_NULL
System.Runtime.InteropServices.ErrorWrapper VT_ERROR
System.Reflection.Missing VT_ERROR met 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

Met behulp van de MarshalObject interface die in het vorige voorbeeld is gedefinieerd, laat het volgende codevoorbeeld zien hoe u verschillende typen varianten doorgeeft aan een COM-server.

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 geen overeenkomende beheerde typen hebben, kunnen worden ge marshalld met behulp van wrapperklassen zoals ErrorWrapper, DispatchWrapper, UnknownWrapperen CurrencyWrapper. In het volgende codevoorbeeld ziet u hoe u deze wrappers gebruikt om verschillende typen varianten door te geven aan een COM-server.

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

De wrapper-klassen worden gedefinieerd in de System.Runtime.InteropServices naamruimte.

Marshalling the IConvertible Interface to Variant

Andere typen dan de typen die in de vorige sectie worden vermeld, kunnen bepalen hoe ze worden marshalled door de IConvertible interface te implementeren. Als het object de IConvertible-interface implementeert, wordt het COM-varianttype tijdens runtime bepaald door de waarde van de TypeCode opsomming die is geretourneerd door de IConvertible.GetTypeCode methode.

In de volgende tabel ziet u de mogelijke waarden voor de opsomming TypeCode en het bijbehorende COM-varianttype voor elke waarde.

TypeCode COM-varianttype
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
Wordt niet ondersteund. VT_INT
Wordt niet ondersteund. VT_UINT
Wordt niet ondersteund. VT_ARRAY
Wordt niet ondersteund. VT_RECORD
Wordt niet ondersteund. VT_CY
Wordt niet ondersteund. VT_VARIANT

De waarde van de COM-variant wordt bepaald door het aanroepen van de interface IConvertible.To Type, waarbij To Type de conversieroutine is die overeenkomt met het type dat is geretourneerd door IConvertible.GetTypeCode. Een object dat Bijvoorbeeld TypeCode.Double van IConvertible.GetTypeCode retourneert, is marshalled als com-variant van het type VT_R8. U kunt de waarde van de variant verkrijgen (opgeslagen in het veld dblVal van de COM-variant) door naar de IConvertible-interface te gieten en de methode aan ToDouble te roepen.

Marshalling Variant naar Object

Bij het marshallen van een variant op een object bepaalt het type, en soms de waarde, van de marshallvariant het type geproduceerd object. De volgende tabel identificeert elk varianttype en het bijbehorende objecttype dat de m marshallerreates wanneer een variant wordt doorgegeven van COM aan .NET Framework.

COM-varianttype Object type
VT_EMPTY Null-objectverwijzing (niets in Visual Basic).
VT_NULL System.DBNull
VT_DISPATCH System.__ComObject of null als (pdispVal == null)
VT_UNKNOWN System.__ComObject of null als (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 Corresponderend waardetype in vak.
VT_VARIANT Wordt niet ondersteund.

Varianttypen die worden doorgegeven van COM aan beheerde code en vervolgens terug naar COM behouden mogelijk niet hetzelfde varianttype voor de duur van de aanroep. Bedenk wat er gebeurt wanneer een variant van het type VT_DISPATCH wordt doorgegeven van COM aan .NET Framework. Tijdens marshalling wordt de variant omgezet in een System.Object. Als het object vervolgens wordt doorgestuurd naar COM, wordt het teruggezet naar een variant van het type VT_UNKNOWN. Er is geen garantie dat de variant die wordt geproduceerd wanneer een object van beheerde code naar COM wordt opgehaald, hetzelfde type is als de variant die in eerste instantie wordt gebruikt om het object te produceren.

Marshalling ByRef Varianten

Hoewel varianten zelf kunnen worden doorgegeven door waarde of verwijzing, kan de vlag VT_BYREF ook worden gebruikt met elk varianttype om aan te geven dat de inhoud van de variant wordt doorgegeven via een verwijzing in plaats van op waarde. Het verschil tussen marshallvarianten per referentie en marshalling van een variant met de VT_BYREF vlagset kan verwarrend zijn. In de volgende afbeelding worden de verschillen verduidelijkt:

Diagram met een variant die is doorgegeven aan de stack. Varianten doorgegeven door waarde en verwijzing

Standaardgedrag voor marshalling van objecten en varianten op waarde

  • Wanneer objecten van beheerde code worden doorgegeven aan COM, wordt de inhoud van het object gekopieerd naar een nieuwe variant die is gemaakt door de marshaller, met behulp van de regels die zijn gedefinieerd in Marshalling Object to Variant. Wijzigingen die zijn aangebracht in de variant aan de onbeheerde zijde, worden niet teruggegeven aan het oorspronkelijke object bij terugkeer vanuit de aanroep.

  • Wanneer u varianten van COM doorgeeft aan beheerde code, wordt de inhoud van de variant gekopieerd naar een nieuw gemaakt object met behulp van de regels die zijn gedefinieerd in Marshalling Variant naar Object. Wijzigingen die zijn aangebracht in het object aan de beheerde zijde, worden niet doorgegeven aan de oorspronkelijke variant bij terugkeer vanuit de aanroep.

Standaardgedrag voor marshallobjecten en varianten op referentie

Als u wijzigingen wilt doorgeven aan de aanroeper, moeten de parameters worden doorgegeven via verwijzing. U kunt bijvoorbeeld het trefwoord verw in C# (of ByRef in beheerde Visual Basic-code) gebruiken om parameters door te geven per verwijzing. In COM worden referentieparameters doorgegeven met behulp van een aanwijzer zoals een variant *.

  • Bij het doorgeven van een object aan COM per referentie, maakt de marshaller een nieuwe variant en kopieert de inhoud van de objectverwijzing naar de variant voordat de aanroep wordt gedaan. De variant wordt doorgegeven aan de onbeheerde functie waar de gebruiker de inhoud van de variant vrij kan wijzigen. Bij terugkeer van de aanroep worden alle wijzigingen die zijn aangebracht in de variant aan de onbeheerde zijde, weer doorgegeven aan het oorspronkelijke object. Als het type van de variant verschilt van het type van de variant die aan de aanroep is doorgegeven, worden de wijzigingen doorgegeven aan een object van een ander type. Dat wil gezegd: het type object dat in de aanroep wordt doorgegeven, kan verschillen van het type object dat door de aanroep wordt geretourneerd.

  • Wanneer een variant wordt doorgegeven aan beheerde code, maakt de marshaller een nieuw object en kopieert de inhoud van de variant naar het object voordat de aanroep wordt uitgevoerd. Een verwijzing naar het object wordt doorgegeven aan de beheerde functie, waarbij de gebruiker het object vrij kan wijzigen. Bij terugkeer vanuit de aanroep worden alle wijzigingen die zijn aangebracht in het object waarnaar wordt verwezen, doorgegeven aan de oorspronkelijke variant. Als het type van het object verschilt van het type van het object dat aan de aanroep is doorgegeven, wordt het type van de oorspronkelijke variant gewijzigd en wordt de waarde weer doorgegeven aan de variant. Nogmaals, het type van de variant die in de aanroep is doorgegeven, kan verschillen van het type van de variant die door de aanroep wordt geretourneerd.

Standaardgedrag voor het marshallen van een variant met de VT_BYREF vlag ingesteld

  • Een variant die wordt doorgegeven aan beheerde code op waarde, kan de VT_BYREF vlag hebben ingesteld om aan te geven dat de variant een verwijzing bevat in plaats van een waarde. In dit geval wordt de variant nog steeds ge marshalld naar een object omdat de variant wordt doorgegeven door een waarde. De marshaller deducteert automatisch de inhoud van de variant en kopieert deze naar een nieuw gemaakt object voordat de aanroep wordt uitgevoerd. Het object wordt vervolgens doorgegeven aan de beheerde functie; bij terugkeer van de aanroep wordt het object echter niet doorgegeven aan de oorspronkelijke variant. Wijzigingen in het beheerde object gaan verloren.

    Let op

    Er is geen manier om de waarde van een variant te wijzigen die wordt doorgegeven door een waarde, zelfs als de variant de VT_BYREF vlag heeft ingesteld.

  • Een variant die wordt doorgegeven aan beheerde code, kan ook worden ingesteld VT_BYREF vlag om aan te geven dat de variant een andere verwijzing bevat. Als dit het geval is, wordt de variant ge marshalld naar een ref-object omdat de variant wordt doorgegeven door verwijzing. De marshaller deducteert automatisch de inhoud van de variant en kopieert deze naar een nieuw gemaakt object voordat de aanroep wordt uitgevoerd. Bij retour van de aanroep wordt de waarde van het object alleen doorgegeven aan de verwijzing in de oorspronkelijke variant als het object hetzelfde type is als het object dat is doorgegeven. Dat wil gezegd, de doorgifte wijzigt niet het type van een variant met de VT_BYREF vlagset. Als het type van het object tijdens de aanroep wordt gewijzigd, treedt er een InvalidCastException op bij het retourneren van de aanroep.

De volgende tabel bevat een overzicht van de doorgifteregels voor varianten en objecten.

Van Tot Wijzigingen doorgevoerd
Variant v Object o Nooit
Object o Variant v Nooit
Variant * hw Ref-object o Altijd
Ref-object o Variant * hw Altijd
Variant v (VT_BYREF VT_ | *) Object o Nooit
Variant v (VT_BYREF VT_ | ) Ref-object o Alleen als het type niet is gewijzigd.

Zie ook