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.CustomMarshaler
d’é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"
).