Partager via


Interface System.Runtime.InteropServices.ICustomMarshaler

Cet article vous offre des remarques complémentaires à la documentation de référence pour cette API.

L’interface ICustomMarshaler fournit des wrappers personnalisés pour la gestion des appels de méthode.

Un marshaller fournit un pont entre les fonctionnalités des anciennes et nouvelles interfaces. Le marshaling personnalisé offre les avantages suivants :

  • Il permet aux applications clientes conçues de fonctionner avec une ancienne interface pour travailler également avec des serveurs qui implémentent une nouvelle interface.
  • Il permet aux applications clientes créées d’utiliser une nouvelle interface pour travailler avec des serveurs qui implémentent une ancienne interface.

Si vous disposez d’une interface qui introduit un comportement de marshaling différent ou qui est exposé au modèle objet de composant (COM) d’une manière différente, vous pouvez concevoir un marshalleur personnalisé au lieu d’utiliser le marshaleur d’interopérabilité. En utilisant un marshaller personnalisé, vous pouvez réduire la distinction entre les nouveaux composants .NET Framework et les composants COM existants.

Supposons, par exemple, que vous développez une interface managée appelée INew. Lorsque cette interface est exposée à COM par le biais d’un wrapper com callable standard (CCW), elle a les mêmes méthodes que l’interface managée et utilise les règles de marshaling intégrées au marshaleur d’interopérabilité. Supposons maintenant qu’une interface COM connue appelée IOld fournit déjà les mêmes fonctionnalités que l’interface INew . En concevant un marshaller personnalisé, vous pouvez fournir une implémentation non managée de IOld ce qui délègue simplement les appels à l’implémentation managée de l’interface INew . Par conséquent, le marshaller personnalisé agit comme un pont entre les interfaces managées et non managées.

Remarque

Les marshallers personnalisés ne sont pas appelés lors de l’appel à partir du code managé vers du code non managé sur une interface dispatch uniquement.

Définir le type de marshaling

Avant de pouvoir générer un marshaleur personnalisé, vous devez définir les interfaces managées et non managées qui seront marshalées. Ces interfaces effectuent généralement la même fonction, mais sont exposées différemment aux objets managés et non managés.

Un compilateur managé produit une interface managée à partir de métadonnées, et l’interface résultante ressemble à n’importe quelle autre interface managée. L’exemple suivant montre une interface classique.

public interface INew
{
    void NewMethod();
}
Public Interface INew
    Sub NewMethod()
End Interface

Vous définissez le type non managé dans IDL (Interface Definition Language) et compilez-le avec le compilateur MIDL (Microsoft Interface Definition Language). Vous définissez l’interface dans une instruction de bibliothèque et affectez-la à un ID d’interface avec l’attribut UUID (Universal Unique Identifier), comme l’illustre l’exemple suivant.

 [uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
     [uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
     interface IOld : IUnknown
         HRESULT OldMethod();
}

Le compilateur MIDL produit plusieurs fichiers de sortie. Si l’interface est définie dans Old.idl, le fichier de sortie Old_i.c définit une const variable avec l’identificateur d’interface (IID) de l’interface, comme l’illustre l’exemple suivant.

const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};

Le fichier Old.h est également produit par MIDL. Il contient une définition C++ de l’interface qui peut être incluse dans votre code source C++.

Implémenter l’interface ICustomMarshaler

Votre marshaller personnalisé doit implémenter l’interface ICustomMarshaler pour fournir les wrappers appropriés au runtime.

Le code C# suivant affiche l’interface de base qui doit être implémentée par tous les marshallers personnalisés.

public interface ICustomMarshaler
{
    Object MarshalNativeToManaged(IntPtr pNativeData);
    IntPtr MarshalManagedToNative(Object ManagedObj);
    void CleanUpNativeData(IntPtr pNativeData);
    void CleanUpManagedData(Object ManagedObj);
    int GetNativeDataSize();
}
Public Interface ICustomMarshaler
     Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
     Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
     Sub CleanUpNativeData( pNativeData As IntPtr )
     Sub CleanUpManagedData( ManagedObj As Object )
     Function GetNativeDataSize() As Integer
End Interface

L’interface ICustomMarshaler inclut des méthodes qui fournissent la prise en charge de la conversion, propre up prise en charge et des informations sur les données à marshaler.

Type d’opération ICustomMarshaler, méthode Description
Conversion (de native en code managé) MarshalNativeToManaged Marshale un pointeur vers des données natives dans un objet managé. Cette méthode retourne un wrapper pouvant être appelé au runtime personnalisé (RCW) qui peut marshaler l’interface non managée qui est passée en tant qu’argument. Le marshaller doit retourner une instance du RCW personnalisé pour ce type.
Conversion (du code managé au code natif) MarshalManagedToNative Marshale un objet managé en pointeur vers des données natives. Cette méthode retourne un wrapper COM appelé personnalisé (CCW) qui peut marshaler l’interface managée qui est passée en tant qu’argument. Le marshaller doit retourner une instance du CCW personnalisé pour ce type.
Nettoyage (du code natif) CleanUpNativeData Permet au marshaller de propre les données natives (CCW) retournées par la MarshalManagedToNative méthode.
Nettoyage (du code managé) CleanUpManagedData Permet au marshaller de propre les données managées (RCW) retournées par la MarshalNativeToManaged méthode.
Informations (à propos du code natif) GetNativeDataSize Retourne la taille des données non managées à marshaler.

Conversion

ICustomMarshaler.MarshalNativeToManaged

Marshale un pointeur vers des données natives dans un objet managé. Cette méthode retourne un wrapper pouvant être appelé au runtime personnalisé (RCW) qui peut marshaler l’interface non managée qui est passée en tant qu’argument. Le marshaller doit retourner une instance du RCW personnalisé pour ce type.

ICustomMarshaler.MarshalManagedToNative

Marshale un objet managé en pointeur vers des données natives. Cette méthode retourne un wrapper COM appelé personnalisé (CCW) qui peut marshaler l’interface managée qui est passée en tant qu’argument. Le marshaller doit retourner une instance du CCW personnalisé pour ce type.

Nettoyage

ICustomMarshaler.CleanUpNativeData

Permet au marshaller de propre les données natives (CCW) retournées par la MarshalManagedToNative méthode.

ICustomMarshaler.CleanUpManagedData

Permet au marshaller de propre les données managées (RCW) retournées par la MarshalNativeToManaged méthode.

Informations de taille

ICustomMarshaler.GetNativeDataSize

Retourne la taille des données non managées à marshaler.

Remarque

Si un marshaleur personnalisé appelle des méthodes qui définissent la dernière erreur P/Invoke lors du marshaling de native à managé ou lorsque propre up, la valeur retournée par Marshal.GetLastWin32Error() et Marshal.GetLastPInvokeError() représente l’appel dans les appels de marshaling ou de propre up. Cela peut entraîner l’absence d’erreurs lors de l’utilisation de marshallers personnalisés avec P/Invokes avec DllImportAttribute.SetLastError défini sur true. Pour conserver la dernière erreur P/Invoke, utilisez les méthodes et Marshal.SetLastPInvokeError(Int32) les Marshal.GetLastPInvokeError() méthodes de l’implémentationICustomMarshaler.

Implémenter la méthode GetInstance

Outre l’implémentation de l’interfaceICustomMarshaler, les marshalleurs personnalisés doivent implémenter une static méthode appelée GetInstance qui accepte un String paramètre et a un type de retour .ICustomMarshaler Cette static méthode est appelée par la couche COM Interop du Common Language Runtime pour instancier une instance du marshaller personnalisé. La chaîne transmise GetInstance est un cookie que la méthode peut utiliser pour personnaliser le marshaller personnalisé retourné. L’exemple suivant montre une implémentation minimale, mais complète ICustomMarshaler .

public class NewOldMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string pstrCookie)
        => new NewOldMarshaler();

    public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
    public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
    public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
    public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
    public int GetNativeDataSize() => throw new NotImplementedException();
}

Appliquer MarshalAsAttribute

Pour utiliser un marshaleur personnalisé, vous devez appliquer l’attribut MarshalAsAttribute au paramètre ou au champ en cours de marshalage.

Vous devez également transmettre la valeur d’énumération UnmanagedType.CustomMarshaler au MarshalAsAttribute constructeur. En outre, vous devez spécifier le MarshalType champ avec l’un des paramètres nommés suivants :

  • MarshalType (obligatoire) : nom qualifié par l’assembly du marshaller personnalisé. Le nom doit inclure l’espace de noms et la classe du marshaller personnalisé. Si le marshaller personnalisé n’est pas défini dans l’assembly dans lequel il est utilisé, vous devez spécifier le nom de l’assembly dans lequel il est défini.

    Remarque

    Vous pouvez utiliser le MarshalTypeRef champ au lieu du MarshalType champ. MarshalTypeRef prend un type plus facile à spécifier.

  • MarshalCookie (facultatif) : cookie transmis au marshaller personnalisé. Vous pouvez utiliser le cookie pour fournir des informations supplémentaires au marshaller. Par exemple, si le même marshaller est utilisé pour fournir un certain nombre de wrappers, le cookie identifie un wrapper spécifique. Le cookie est passé à la GetInstance méthode du marshaller.

L’attribut MarshalAsAttribute identifie le marshaller personnalisé afin qu’il puisse activer le wrapper approprié. Le service d’interopérabilité du Common Language Runtime examine ensuite l’attribut et crée le marshaleur personnalisé la première fois que l’argument (paramètre ou champ) doit être marshalé.

Le runtime appelle ensuite les méthodes et MarshalManagedToNative les MarshalNativeToManaged méthodes sur le marshaller personnalisé pour activer le wrapper approprié pour gérer l’appel.

Utiliser un marshaller personnalisé

Une fois le marshaller personnalisé terminé, vous pouvez l’utiliser comme wrapper personnalisé pour un type particulier. L’exemple suivant montre la définition de l’interface IUserData managée :

interface IUserData
{
    void DoSomeStuff(INew pINew);
}
Public Interface IUserData
    Sub DoSomeStuff(pINew As INew)
End Interface

Dans l’exemple suivant, l’interface IUserData utilise le NewOldMarshaler marshaller personnalisé pour permettre aux applications clientes non managées de passer une IOld interface à la DoSomeStuff méthode. La description gérée de la DoSomeStuff méthode prend une INew interface, comme illustré dans l’exemple précédent, tandis que la version non managée de DoSomeStuff prend un pointeur d’interface IOld , comme illustré dans l’exemple suivant.

[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
     [uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
     interface IUserData : IUnknown
         HRESULT DoSomeStuff(IUnknown* pIOld);
}

La bibliothèque de types générée par l’exportation de la définition managée génère IUserData la définition non managée indiquée dans cet exemple au lieu de la définition standard. L’attribut MarshalAsAttribute appliqué à l’argument INew dans la définition managée de la DoSomeStuff méthode indique que l’argument utilise un marshalleur personnalisé, comme l’illustre l’exemple suivant.

using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
    void DoSomeStuff(
        [MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType="NewOldMarshaler")]
    INew pINew
    );
}
Public Interface IUserData
    Sub DoSomeStuff( _
        <MarshalAs(UnmanagedType.CustomMarshaler, _
        MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface

Dans les exemples précédents, le premier paramètre fourni à l’attribut MarshalAsAttribute est la UnmanagedType.CustomMarshaler valeur UnmanagedType.CustomMarshalerd’énumération .

Le deuxième paramètre est le MarshalType champ, qui fournit le nom qualifié d’assembly du marshaller personnalisé. Ce nom se compose de l’espace de noms et de la classe du marshaller personnalisé (MarshalType="MyCompany.NewOldMarshaler").