Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les paramètres et les champs de type System.Object peuvent être exposés à un code non managé sous la forme de l’un des types suivants :
Un variant quand l’objet est un paramètre.
Une interface quand l’objet est un champ de structure.
Seul COM interop prend en charge le marshaling des types d’objets. Le comportement par défaut est de marshaler des objets vers des variants COM. Ces règles s’appliquent uniquement au type Object et ne s’appliquent pas aux objets fortement typés qui dérivent de la Object classe.
Options de marshaling
Le tableau suivant présente les options de marshaling pour le Object type de données. L’attribut MarshalAsAttribute fournit plusieurs valeurs d’énumération UnmanagedType pour marshaler des objets.
| Type d'énumération | Description du format non managé |
|---|---|
|
UnmanagedType.Struct (valeur par défaut des paramètres) |
Variant de style COM. |
| UnmanagedType.Interface | Une IDispatch interface, si possible ; sinon, une IUnknown interface. |
|
UnmanagedType.IUnknown (valeur par défaut des champs) |
Interface IUnknown. |
| UnmanagedType.IDispatch | Interface IDispatch. |
L’exemple suivant montre la définition d’interface managée de 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();
}
Le code suivant exporte l’interface MarshalObject vers une bibliothèque de types.
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)
}
Notes
Le marshaleur d’interopérabilité libère automatiquement tout objet alloué à l’intérieur du variant après l’appel.
L’exemple suivant illustre un type valeur mis en forme.
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;
}
Le code suivant exporte le type mis en forme vers une bibliothèque de types.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Marshaling d’un objet vers une interface
Lorsqu’un objet est exposé à COM en tant qu’interface, cette interface est l’interface de classe pour le type Object managé (l’interface _Object ). Cette interface est typée sous la forme d’un IDispatch (UnmanagedType) ou d’un IUnknown (UnmanagedType.IUnknown) dans la bibliothèque de types résultante. Les clients COM peuvent appeler dynamiquement les membres de la classe managée ou tous les membres implémentés par ses classes dérivées via l’interface _Object . Le client peut également appeler QueryInterface pour obtenir toute autre interface explicitement implémentée par le type managé.
Marshaling d’un objet vers un variant
Lorsqu’un objet est marshalé en variante, le type de variante interne est déterminé au moment de l’exécution, en fonction des règles suivantes :
Si la référence d’objet est null (Nothing en Visual Basic), l’objet est marshalé en variant de type VT_EMPTY.
Si l’objet est une instance d’un type énuméré dans le tableau suivant, le type variant résultant est déterminé par les règles du marshaleur et illustré dans le tableau.
Les autres objets qui nécessitent de contrôler explicitement le comportement de marshaling peuvent implémenter l’interface IConvertible. Dans ce cas, le type variant est déterminé par le code de type retourné à partir de la méthode IConvertible.GetTypeCode. Sinon, l’objet est marshalé en tant que variant de type VT_UNKNOWN.
Marshaling de types système vers des variants
Le tableau suivant montre les types d’objets managés et leurs types variant COM correspondants. Ces types sont convertis uniquement quand la signature de la méthode appelée est de type System.Object.
| Type d’objet | Type variant COM |
|---|---|
| Référence d’objet null (Nothing en Visual Basic). | VT_EMPTY |
| System.DBNull | VT_NULL |
| System.Runtime.InteropServices.ErrorWrapper | VT_ERROR |
| System.Reflection.Missing | VT_ERROR avec 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 |
En utilisant l’interface MarshalObject définie dans l’exemple précédent, l’exemple de code suivant démontre comment passer plusieurs types de variants à un serveur COM.
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.
Les types COM qui n’ont pas de types managés correspondants peuvent être marshalés à l’aide des classes wrapper comme ErrorWrapper, DispatchWrapper, UnknownWrapper et CurrencyWrapper. L’exemple de code suivant démontre comment utiliser ces wrappers pour passer différents types de variants à un serveur COM.
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)));
Les classes wrapper sont définies dans l’espace de noms System.Runtime.InteropServices.
Marshaling de l’interface IConvertible vers un variant
D’autres types que ceux répertoriés dans la section précédente peuvent contrôler la manière dont ils sont marshalés en implémentant l’interface IConvertible. Si l’objet implémente l’interface IConvertible , le type de variante COM est déterminé au moment de l’exécution par la valeur de l’énumération TypeCode retournée par la IConvertible.GetTypeCode méthode.
Le tableau suivant présente les valeurs possibles pour l’énumération TypeCode et le type de variante COM correspondant pour chaque valeur.
| TypeCode | Type variant COM |
|---|---|
| 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 |
| Non pris en charge. | VT_INT |
| Non pris en charge. | VT_UINT |
| Non pris en charge. | VT_ARRAY |
| Non pris en charge. | VT_RECORD |
| Non pris en charge. | VT_CY |
| Non pris en charge. | VT_VARIANT |
La valeur de la variante COM est déterminée en appelant l’interface dblVal champ de la variante COM) en effectuant un transtypage vers l’interface IConvertible et en appelant la méthode ToDouble.
Marshaling d’un variant vers un objet
Lors du marshaling d’un variant vers un objet, le type, et parfois la valeur, du variant marshalé détermine le type d’objet produit. Le tableau suivant identifie chaque type variant et le type d’objet correspondant que le marshaleur crée quand un variant est transféré de COM à .NET Framework.
| Type variant COM | Type d’objet |
|---|---|
| VT_EMPTY | Référence d’objet null (Nothing en Visual Basic). |
| VT_NULL | System.DBNull |
| VT_DISPATCH | System.__ComObject ou null si (pdispVal == null) |
| VT_UNKNOWN | System.__ComObject ou null si (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 | Type valeur boxed correspondant. |
| VT_VARIANT | Non pris en charge. |
Les types variant passés à partir de COM vers le code managé, puis repassés à COM peuvent ne pas conserver le même type variant durant la durée de l’appel. Tenez compte de ce qui se passe lorsqu’une variante de type VT_DISPATCH est passée de COM au .NET Framework. Lors du marshaling, le variant est converti en System.Object. Si la Object valeur est ensuite renvoyée à COM, elle est marshalée vers une variante de type VT_UNKNOWN. Rien ne garantit que le variant produit quand un objet est marshalé à partir de code managé vers COM sera du même type que le variant utilisé à l’origine pour produire l’objet.
Marshaling des variants ByRef
Bien que les variantes elles-mêmes puissent être passées par valeur ou par référence, l’indicateur VT_BYREF peut également être utilisé avec n’importe quel type de variante pour indiquer que le contenu de la variante est passé par référence plutôt que par valeur. La différence entre les variantes de marshaling par référence et le marshaling d’une variante avec le VT_BYREF jeu d’indicateurs peut être déroutante. L’illustration suivante clarifie ces différences :
Variants passés par valeur et par référence
Comportement par défaut de marshaling d’objets et de variants par valeur
Lors du passage d’objets à partir de code managé vers COM, le contenu de l’objet est copié dans un nouveau variant créé par le marshaleur à l’aide des règles définies dans Marshaling d’un objet vers un variant. Les changements apportés au variant côté non managé ne sont pas retournés vers l’objet d’origine au retour de l’appel.
Lors du passage de variants à partir de COM vers du code managé, le contenu du variant est copié dans un objet nouvellement créé à l’aide des règles définies dans Marshaling d’un variant vers un objet. Les changements apportés à l’objet côté managé ne sont pas retournés vers le variant d’origine au retour de l’appel.
Comportement par défaut de marshaling d’objets et de variants par valeur
Pour retourner des changements à l’appelant, les paramètres doivent être passés par référence. Par exemple, vous pouvez utiliser le ref mot clé en C# (ou ByRef dans le code managé Visual Basic) pour passer des paramètres par référence. Dans COM, les paramètres de référence sont passés à l’aide d’un pointeur tel que variant *.
Lors du passage d’un objet à COM par référence, le marshaleur crée un variant et copie le contenu de la référence d’objet dans le variant avant que l’appel ne soit effectué. Le variant est passé à la fonction non managée où l’utilisateur est libre de changer le contenu du variant. Au retour de l’appel, tout changement apporté au variant côté non managé est retourné vers l’objet d’origine. Si le type de variant est différent du type du variant passé à l’appel, les changements sont retournés à un objet d’un type différent. Cela signifie que le type de l’objet passé dans l’appel peut être différent du type de l’objet retourné à partir de l’appel.
Lors du passage d’un variant au code managé par référence, le marshaleur crée un objet et copie le contenu du variant dans l’objet avant que l’appel ne soit effectué. Une référence à l’objet est passée à la fonction managée, où l’utilisateur est libre de changer l’objet. Au retour de l’appel, tout changement apporté à l’objet référencé est retourné vers le variant d’origine. Si le type de l’objet est différent du type de l’objet passé dans l’appel, le type du variant d’origine est changé et la valeur est retournée dans le variant. Là encore, le type du variant passé dans l’appel peut être différent du type du variant retourné à partir de l’appel.
Comportement par défaut de marshaling d’un variant dont l’indicateur VT_BYREF est défini
Une variante transmise au code managé par valeur peut avoir l’indicateur
VT_BYREFdéfini pour indiquer que la variante contient une référence au lieu d’une valeur. Dans ce cas, le variant est toujours marshalé vers un objet, car le variant est passé par valeur. Le marshaleur déréférence automatiquement le contenu du variant et le copie dans un objet nouvellement créé avant d’effectuer l’appel. L’objet est ensuite passé à la fonction managée ; cependant, au retour de l’appel, l’objet n’est pas retourné dans le variant d’origine. Les changements apportés à l’objet managé sont perdus.Attention
Il n’existe aucun moyen de modifier la valeur d’une variante transmise par valeur, même si la variante a l’indicateur
VT_BYREFdéfini.Une variante transmise au code managé par référence peut également avoir l’indicateur
VT_BYREFdéfini pour indiquer que la variante contient une autre référence. Si c’est le cas, la variante est convertie enrefobjet, car la variante est passée par référence. Le marshaleur déréférence automatiquement le contenu du variant et le copie dans un objet nouvellement créé avant d’effectuer l’appel. Au retour de l’appel, la valeur de l’objet est retournée vers la référence située dans le variant d’origine uniquement si l’objet est du même type que l’objet passé. Autrement dit, la propagation ne modifie pas le type d’une variante avec l’indicateurVT_BYREFdéfini. Si le type de l’objet est modifié durant l’appel, une exception InvalidCastException se produit au retour de l’appel.
Le tableau suivant résume les règles de propagation pour les variants et les objets.
| Du | À | Changements retournés |
|---|---|---|
| Variantv | Objeto | Jamais |
| Objeto | Variantv | Jamais |
| Variante*Pv | Ref, objeto | Toujours |
| Objet Refo | Variante*Pv | Toujours |
| Variantev(VT_BYREF|VT_*) | Objeto | Jamais |
| Variantev(VT_BYREF|VT_) | Ref, objeto | Uniquement si le type n’a pas changé. |