Partager via


Modifications de marshaling

Les sections suivantes fournissent un ensemble sélectionné de modifications que vous pouvez apporter à un assembly d'interopérabilité pour résoudre certains problèmes spécifiques liés aux résultats du processus d'importation :

  • Tableaux conformes au style C

  • Tableaux de style C entrant/sortant

  • Tableaux de style C multidimensionnels

  • SAFEARRAY non limité par zéro

  • Préservation de la signature

  • Passer null au lieu d'une référence à un type valeur

Ces sections ne couvrent pas tous les cas d'édition d'un assembly d'interopérabilité. Par exemple, vous pouvez également éditer un assembly d'interopérabilité pour améliorer la simplicité de son utilisation. La seule manière de déterminer les types de personnalisation nécessaires est d'écrire du code à l'aide de l'assembly d'interopérabilité. Pour obtenir des instructions sur la modification des assemblys d'interopérabilité, consultez Comment : modifier des assemblys d'interopérabilité.

Le marshaling est affecté lorsque le client et le serveur résident dans des apartments incompatibles. Dans les exemples suivants, la plupart des paramètres marshalés ne sont pas compatibles avec Automation et nécessitent l'une des actions suivantes :

  • Confirmez que le client et le serveur résident tous deux dans des apartments compatibles (et qu'il n'y a pas par conséquent de marshaling COM).

  • Inscrivez le proxy et le stub générés à partir du langage de définition d'interface (IDL). Dans de tels cas, inscrire la bibliothèque de types ne résout pas le problème car l'essentiel des informations nécessaires au marshaling n'est pas propagé de l'IDL vers la bibliothèque de types.

Tableaux conformes au style C

La déclaration IDL suivante illustre un tableau de style C.

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

Comme ce type n'est pas compatible avec Automation, les informations sur la taille du tableau, telles que le lien entre le premier et le deuxième paramètre, ne peuvent pas être exprimées dans la bibliothèque de types. L'importateur de bibliothèques de types (Type Library Importer (Tlbimp.exe)) importe le deuxième paramètre comme référence à l'entier et non pas comme tableau managé. Vous pouvez ajuster le paramètre en éditant le MSIL.

Dans MSIL, recherchez

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

Remplacer par

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall

Pour appeler à partir d'un code managé

int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );

Tableaux de style C entrant/sortant

La déclaration IDL suivante illustre un tableau de style C entrant/sortant.

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)] 
int** ppInOut);

Dans ce cas, le tableau peut être redimensionné et la nouvelle taille peut être passée de nouveau. Comme ce type n'est pas compatible avec Automation, les informations sur la taille du tableau, telles que le lien entre le premier et le deuxième paramètre, ne peuvent pas être exprimées dans la bibliothèque de types. Tlbimp.exe importe le deuxième paramètre comme type IntPtr. Bien qu'il vous soit possible d'appeler cette méthode à partir du code managé, afin de redimensionner le tableau, vous devez éditer le MSIL et utiliser les méthodes de la classe Marshal pour traiter manuellement l'allocation et la désallocation de la mémoire.

Dans MSIL, recherchez

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

Remplacer par

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

Pour appeler à partir d'un code managé

int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}

Tableaux de style C multidimensionnels

La déclaration IDL suivante illustre un tableau de style C à deux dimensions.

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

Comme ce type n'est pas compatible avec Automation, les informations sur la taille et le nombre de dimensions du tableau, telles que le lien entre le premier et le deuxième paramètre, ne peuvent pas être exprimées dans la bibliothèque de types. Tlbimp.exe importe le deuxième paramètre comme type IntPtr et non pas comme tableau multidimensionnel managé. Vous pouvez ajuster le paramètre en éditant le MSIL.

Dans MSIL, recherchez

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

Remplacer par

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall

Pour appeler à partir d'un code managé

int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );

SAFEARRAY non limité par zéro

La déclaration IDL suivante illustre un paramètre SAFEARRAY.

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

Imaginons que SAFEARRAY ne soit pas limité par zéro. Dans du code managé, ces tableaux sont représentés par le type System.Array. Cependant, par défaut, l'importateur convertit tous les paramètres SAFEARRAY sous forme de références à des tableaux managés. Pour modifier le comportement par défaut, deux options s'offrent à vous :

  • Importer tous les tableaux dans une bibliothèque de types comme types System.Array à l'aide de Tlbimp.exe et du commutateur /sysarray.

  • Importer quelques paramètres comme types System.Array en éditant manuellement le MSIL comme le montre l'exemple suivant :

    Dans MSIL, recherchez

    .method public hidebysig newslot virtual 
    instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall
    

    Remplacer par

    .method public hidebysig newslot virtual 
    instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) 
    ppsa) runtime managed internalcall
    

    Pour appeler à partir d'un code managé

    int[] lengthsArray = new int[1] { 3 };   
    int[] boundsArray = new int[1] { -1 };
    Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray );
    for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ )
    param2.SetValue( i * 10, i ); 
    sum = tstArrays.InSArray( ref param2 );
    

Préservation de la signature

La déclaration IDL suivante illustre une signature de méthode COM.

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Tlbimp.exe change les signatures des méthodes COM. Les paramètres marqués avec [out, retval] dans IDL deviennent des valeurs de retour des méthodes managées. Toutes les valeurs HRESULT qui indiquent un échec sont transformées en exceptions managées. Il est parfois nécessaire de préserver la signature de méthode COM d'origine. Par exemple, lorsque la méthode retourne des valeurs autres que des HRESULT de succès. La représentation managée suivante illustre un exemple de signature que vous pouvez modifier.

Représentation managée dans MSIL

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{

Remplacer par

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig

Pour afficher le type de valeur HRESULT retournée

int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );

Passer null au lieu d'une référence à un type valeur

La déclaration IDL suivante illustre un pointeur IDL vers une structure.

HRESULT TestPassingNull([in, unique] Point* refParam);

Tlbimp.exe importe le paramètre comme référence au type valeur Point. En C# et Visual Basic 2005, une référence null (Nothing en Visual Basic) ne peut pas être passée en tant que paramètre lorsqu'une référence à un type valeur est attendue. Si la fonction COM nécessite un paramètre null (Nothing), vous pouvez modifier la signature en éditant le MSIL.

Dans MSIL, recherchez

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

Remplacer par

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

La signature modifiée vous permet de passer une valeur null. Cependant, lorsque vous avez besoin de passer de vraies valeurs, vous devez utiliser les méthodes de la classe Marshal, comme l'illustre l'exemple suivant.

tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );

Voir aussi

Tâches

Comment : modifier des assemblys d'interopérabilité

Comment : créer manuellement des wrappers

Référence

Tlbimp.exe (Type Library Importer)

Concepts

personnaliser des wrappers pouvant être appelés par le runtime

Types de données COM

personnaliser des wrappers pouvant être appelés par COM