Guide pratique pour marshaler des tableaux à l’aide de P/Invoke
Vous pouvez appeler des fonctions natives qui acceptent des chaînes de style C à l’aide du type String de chaîne CLR lors de l’utilisation de la prise en charge de l’appel de plateforme .NET Framework (P/Invoke). Nous vous encourageons à utiliser les fonctionnalités d’interopérabilité C++ au lieu de P/Invoke lorsque cela est possible. P/Invoke fournit peu de rapports d’erreurs au moment de la compilation, n’est pas sécurisé par type et peut être fastidieux à implémenter. Si l’API non managée est empaquetée en tant que DLL et que le code source n’est pas disponible, P/Invoke est la seule option. Sinon, consultez Utilisation de l’interopérabilité C++ (P/Invoke implicite)).
Exemple
Étant donné que les tableaux natifs et managés sont disposés différemment en mémoire, leur passage avec succès sur la limite managée/non managée nécessite une conversion ou un marshaling. Cet article montre comment un tableau d’éléments simples (blitables) peut être transmis à des fonctions natives à partir du code managé.
Comme c’est le cas du marshaling de données managé/non managé en général, l’attribut DllImportAttribute est utilisé pour créer un point d’entrée managé pour chaque fonction native utilisée. Dans les fonctions qui prennent des tableaux en tant qu’arguments, l’attribut MarshalAsAttribute doit être utilisé pour spécifier comment marshaler les données. Dans l’exemple suivant, l’énumération UnmanagedType est utilisée pour indiquer que le tableau managé est marshalé en tant que tableau de style C.
Le code suivant se compose d’un module non managé et managé. Le module non managé est une DLL qui définit une fonction qui accepte un tableau d’entiers. Le deuxième module est une application en ligne de commande managée qui importe cette fonction, mais la définit en termes de tableau managé. Il utilise l’attribut MarshalAsAttribute pour spécifier que le tableau doit être converti en tableau natif lorsqu’il est appelé.
// TraditionalDll4.cpp
// compile with: /LD /EHsc
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
extern "C" {
TRADITIONALDLL_API void TakesAnArray(int len, int[]);
}
void TakesAnArray(int len, int a[]) {
printf_s("[unmanaged]\n");
for (int i=0; i<len; i++)
printf("%d = %d\n", i, a[i]);
}
Le module managé est compilé à l’aide /clr
de .
// MarshalBlitArray.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
value struct TraditionalDLL {
[DllImport("TraditionalDLL4.dll")]
static public void TakesAnArray(
int len,[MarshalAs(UnmanagedType::LPArray)]array<int>^);
};
int main() {
array<int>^ b = gcnew array<int>(3);
b[0] = 11;
b[1] = 33;
b[2] = 55;
TraditionalDLL::TakesAnArray(3, b);
Console::WriteLine("[managed]");
for (int i=0; i<3; i++)
Console::WriteLine("{0} = {1}", i, b[i]);
}
Aucune partie de la DLL n’est exposée au code managé par le biais de la directive traditionnelle #include
. En fait, étant donné que la DLL est accessible au moment de l’exécution uniquement, les problèmes dans les fonctions importées par l’utilisation DllImportAttribute ne peuvent pas être détectés au moment de la compilation.
Voir aussi
Utilisation de P/Invoke explicite en C++ (DllImport
attribut)