Dela via


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:

Diagram som visar varianten som skickats på stacken. 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.

Se även