Standard-marshalling för objekt
Parametrar och fält som skrivs som System.Object kan exponeras för ohanterad kod som en av följande typer:
En variant när objektet är en parameter.
Ett gränssnitt när objektet är ett strukturfält.
Endast COM-interop stöder marshalling för objekttyper. Standardbeteendet är att konvertera objekt till COM-varianter. Dessa regler gäller endast för typen Objekt och gäller inte för starkt skrivna objekt som härleds från klassen Object .
Alternativ för marshalling
I följande tabell visas alternativ för sortering för objektdatatypen . Attributet MarshalAsAttribute innehåller flera UnmanagedType uppräkningsvärden för att konvertera objekt.
Uppräkningstyp | Beskrivning av ohanterat format |
---|---|
OhanteradType.Struct (standard för parametrar) |
En COM-formatvariant. |
UnmanagedType.Interface | Ett IDispatch-gränssnitt , om möjligt, annars ett IUnknown-gränssnitt . |
UnmanagedType.IUnknown (standard för fält) |
Ett IUnknown-gränssnitt . |
UnmanagedType.IDispatch | Ett IDispatch-gränssnitt . |
I följande exempel visas definitionen för det hanterade gränssnittet 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();
}
Följande kod exporterar MarshalObject
gränssnittet till ett typbibliotek.
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)
}
Kommentar
Interop-marshallern frigör automatiskt alla allokerade objekt i varianten efter anropet.
I följande exempel visas en formaterad värdetyp.
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;
}
Följande kod exporterar den formaterade typen till ett typbibliotek.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Rangeringsobjekt till gränssnitt
När ett objekt exponeras för COM som ett gränssnitt är det gränssnittet klassgränssnittet för den hanterade typen Object ( _Object-gränssnittet ). Det här gränssnittet skrivs som en IDispatch (UnmanagedType) eller en IUnknown (UnmanagedType.IUnknown) i det resulterande typbiblioteket. COM-klienter kan dynamiskt anropa medlemmar i den hanterade klassen eller medlemmar som implementeras av dess härledda klasser via _Object-gränssnittet . Klienten kan också anropa QueryInterface för att hämta andra gränssnitt som uttryckligen implementerats av den hanterade typen.
Rangeringsobjekt till variant
När ett objekt är uppdelade till en variant bestäms den interna varianttypen vid körning, baserat på följande regler:
Om objektreferensen är null (ingenting i Visual Basic) är objektet uppdelade till en variant av typen VT_EMPTY.
Om objektet är en instans av någon typ som anges i följande tabell bestäms den resulterande varianttypen av de regler som är inbyggda i marshallern och visas i tabellen.
Andra objekt som uttryckligen behöver styra marshallingbeteendet kan implementera IConvertible gränssnittet. I så fall bestäms varianttypen av den typkod som returneras från IConvertible.GetTypeCode metoden. Annars ordnas objektet som en variant av typen VT_UNKNOWN.
Sorteringssystemtyper till variant
I följande tabell visas hanterade objekttyper och deras motsvarande COM-varianttyper. Dessa typer konverteras endast när signaturen för metoden som anropas är av typen System.Object.
Object type | COM-varianttyp |
---|---|
Null-objektreferens (ingenting i Visual Basic). | VT_EMPTY |
System.DBNull | VT_NULL |
System.Runtime.InteropServices.ErrorWrapper | VT_ERROR |
System.Reflection.Missing | VT_ERROR med 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 |
Med hjälp av gränssnittet MarshalObject
som definierades i föregående exempel visar följande kodexempel hur du skickar olika typer av varianter till en 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-typer som inte har motsvarande hanterade typer kan ordnas med hjälp av omslutningsklasser som ErrorWrapper, DispatchWrapper, UnknownWrapperoch CurrencyWrapper. Följande kodexempel visar hur du använder dessa omslutningar för att skicka olika typer av varianter till en 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)));
Omslutningsklasserna definieras i System.Runtime.InteropServices namnområdet.
Marshalling the IConvertible Interface to Variant
Andra typer än de som anges i föregående avsnitt kan styra hur de ordnas genom att implementera IConvertible gränssnittet. Om objektet implementerar IConvertible-gränssnittet bestäms COM-varianttypen vid körning av värdet för den TypeCode uppräkning som returneras från IConvertible.GetTypeCode metoden.
I följande tabell visas möjliga värden för TypeCode-uppräkningen och motsvarande COM-varianttyp för varje värde.
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 |
Stöds ej. | VT_INT |
Stöds ej. | VT_UINT |
Stöds ej. | VT_ARRAY |
Stöds ej. | VT_RECORD |
Stöds ej. | VT_CY |
Stöds ej. | VT_VARIANT |
Värdet för COM-varianten bestäms genom att anropa gränssnittet IConvertible.To Type, där To Type är den konverteringsrutin som motsvarar den typ som returnerades från IConvertible.GetTypeCode. Ett objekt som till exempel returnerar TypeCode.Double från IConvertible.GetTypeCode är en COM-variant av typen VT_R8. Du kan hämta värdet för varianten (lagras i fältet dblVal i COM-varianten) genom att konvertera till IConvertible-gränssnittet och anropa ToDouble metoden.
Rangeringsvariant till objekt
När du sorterar en variant till ett objekt avgör typen och ibland värdet för den marshallerade varianten vilken typ av objekt som skapas. I följande tabell identifieras varje varianttyp och motsvarande objekttyp som m marshallerreates när en variant skickas från COM till .NET Framework.
COM-varianttyp | Object type |
---|---|
VT_EMPTY | Null-objektreferens (ingenting i Visual Basic). |
VT_NULL | System.DBNull |
VT_DISPATCH | System.__ComObject eller null om (pdispVal == null) |
VT_UNKNOWN | System.__ComObject eller null om (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 | Motsvarande boxade värdetyp. |
VT_VARIANT | Stöds ej. |
Varianttyper som skickas från COM till hanterad kod och sedan tillbaka till COM kanske inte behåller samma varianttyp under anropets varaktighet. Tänk på vad som händer när en variant av typen VT_DISPATCH skickas från COM till .NET Framework. Under marshalling konverteras varianten till en System.Object. Om objektet sedan skickas tillbaka till COM, omdirigeras det tillbaka till en variant av typen VT_UNKNOWN. Det finns ingen garanti för att varianten som skapas när ett objekt är marshalled från hanterad kod till COM kommer att vara av samma typ som den variant som ursprungligen användes för att producera objektet.
Marshalling ByRef-varianter
Även om varianterna i sig kan skickas med värde eller referens, kan flaggan VT_BYREF också användas med valfri varianttyp för att indikera att innehållet i varianten skickas med referens i stället för efter värde. Skillnaden mellan marshallingvarianter efter referens och marshalling av en variant med VT_BYREF-flagguppsättningen kan vara förvirrande. Följande bild förtydligar skillnaderna:
Varianter som skickas av värde och efter referens
Standardbeteende för att ordna objekt och varianter efter värde
När objekt skickas från hanterad kod till COM kopieras innehållet i objektet till en ny variant som skapats av marshallern med hjälp av de regler som definierats i Marshalling Object to Variant. Ändringar som görs i varianten på den ohanterade sidan sprids inte tillbaka till det ursprungliga objektet vid retur från anropet.
När du skickar varianter från COM till hanterad kod kopieras innehållet i varianten till ett nyligen skapat objekt med hjälp av reglerna som definieras i Marshalling Variant to Object. Ändringar som gjorts i objektet på den hanterade sidan sprids inte tillbaka till den ursprungliga varianten vid retur från anropet.
Standardbeteende för att ordna objekt och varianter efter referens
Om du vill sprida ändringar tillbaka till anroparen måste parametrarna skickas med referens. Du kan till exempel använda referensnyckelordet i C# (eller ByRef i Visual Basic-hanterad kod) för att skicka parametrar efter referens. I COM skickas referensparametrar med hjälp av en pekare, till exempel en variant *.
När ett objekt skickas till COM med referens skapar marshaller en ny variant och kopierar innehållet i objektreferensen till varianten innan anropet görs. Varianten skickas till den ohanterade funktionen där användaren kan ändra innehållet i varianten. Vid retur från anropet sprids alla ändringar som gjorts i varianten på den ohanterade sidan tillbaka till det ursprungliga objektet. Om typen av variant skiljer sig från den typ av variant som skickas till anropet sprids ändringarna tillbaka till ett objekt av en annan typ. Det vill säga att typen av objekt som skickas till anropet kan skilja sig från den typ av objekt som returneras från anropet.
När du skickar en variant till hanterad kod med referens skapar marshaller ett nytt objekt och kopierar innehållet i varianten till objektet innan anropet utförs. En referens till objektet skickas till den hanterade funktionen, där användaren kan ändra objektet. Vid retur från anropet sprids alla ändringar som gjorts i det refererade objektet tillbaka till den ursprungliga varianten. Om objektets typ skiljer sig från den typ av objekt som skickas till anropet ändras typen av den ursprungliga varianten och värdet sprids tillbaka till varianten. Återigen kan typen av variant som skickas till anropet skilja sig från vilken typ av variant som returneras från anropet.
Standardbeteende för att ordna en variant med VT_BYREF-flagguppsättningen
En variant som skickas till hanterad kod efter värde kan ha flaggan VT_BYREF inställd för att indikera att varianten innehåller en referens i stället för ett värde. I det här fallet är varianten fortfarande grupperad till ett objekt eftersom varianten skickas av ett värde. Marshaller derefererar automatiskt innehållet i varianten och kopierar det till ett nyligen skapat objekt innan anropet. Objektet skickas sedan till den hanterade funktionen. Vid retur från anropet sprids objektet dock inte tillbaka till den ursprungliga varianten. Ändringar som görs i det hanterade objektet går förlorade.
Varning
Det finns inget sätt att ändra värdet för en variant som skickas av ett värde, även om varianten har VT_BYREF flagguppsättning.
En variant som skickas till hanterad kod med referens kan också ha flaggan VT_BYREF inställd för att indikera att varianten innehåller en annan referens. Om det gör det, är varianten marshalled till ett referensobjekt eftersom varianten skickas med referens. Marshaller derefererar automatiskt innehållet i varianten och kopierar det till ett nyligen skapat objekt innan anropet. Vid retur från anropet sprids objektets värde tillbaka till referensen i den ursprungliga varianten endast om objektet är av samma typ som objektet som skickades in. Spridning ändrar alltså inte typen av variant med VT_BYREF flagguppsättning. Om typen av objekt ändras under anropet inträffar en InvalidCastException vid retur från anropet.
I följande tabell sammanfattas spridningsreglerna för varianter och objekt.
Från | Till | Ändringar som sprids tillbaka |
---|---|---|
Variant v | Objekt o | Aldrig |
Objekt o | Variant v | Aldrig |
Variant * pv | Referensobjekt o | Alltid |
Referensobjekt o | Variant * pv | Alltid |
Variant v (VT_BYREF | VT_*) | Objekt o | Aldrig |
Variant v (VT_BYREF | VT_) | Referensobjekt o | Endast om typen inte har ändrats. |