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:
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. |