Guide pratique pour marshaler des pointeurs incorporés à l’aide de P/Invoke
Les fonctions implémentées dans des DLL non managées peuvent être appelées à partir de code managé à l’aide de la fonctionnalité Appel de plateforme (P/Invoke). Si le code source de la DLL n’est pas disponible, P/Invoke est la seule option d’interopérabilité. Toutefois, contrairement à d’autres langages .NET, Visual C++ offre une alternative à P/Invoke. Pour plus d’informations, consultez Utilisation de l’interopérabilité C++ (P/Invoke implicite) et Guide pratique pour marshaler des pointeurs incorporés à l’aide de l’interopérabilité C++.
Exemple
Le passage de structures à du code natif nécessite qu’une structure managée équivalente en termes de disposition des données à la structure native soit créée. Toutefois, les structures qui contiennent des pointeurs nécessitent une gestion spéciale. Pour chaque pointeur incorporé dans la structure native, la version managée de la structure doit contenir une instance du IntPtr type. En outre, la mémoire de ces instances doit être explicitement allouée, initialisée et libérée à l’aide des méthodes et StructureToPtrFreeCoTaskMem des AllocCoTaskMemméthodes.
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 une structure appelée ListString
qui contient un pointeur et une fonction appelée TakesListStruct
.
// TraditionalDll6.cpp
// compile with: /EHsc /LD
#include <stdio.h>
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
#pragma pack(push, 8)
struct ListStruct {
int count;
double* item;
};
#pragma pack(pop)
extern "C" {
TRADITIONALDLL_API void TakesListStruct(ListStruct);
}
void TakesListStruct(ListStruct list) {
printf_s("[unmanaged] count = %d\n", list.count);
for (int i=0; i<list.count; i++)
printf_s("array[%d] = %f\n", i, list.item[i]);
}
Le module managé est une application de ligne de commande qui importe la TakesListStruct
fonction et définit une structure appelée MListStruct
qui équivaut au natif ListStruct
, sauf que celle-ci double*
est représentée avec une IntPtr instance. Avant d’appeler TakesListStruct
, la main
fonction alloue et initialise la mémoire référencée par ce champ.
// EmbeddedPointerMarshalling.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MListStruct {
int count;
IntPtr item;
};
value struct TraditionalDLL {
[DllImport("TraditionalDLL6.dll")]
static public void TakesListStruct(MListStruct);
};
int main() {
array<double>^ parray = gcnew array<double>(10);
Console::WriteLine("[managed] count = {0}", parray->Length);
Random^ r = gcnew Random();
for (int i=0; i<parray->Length; i++) {
parray[i] = r->NextDouble() * 100.0;
Console::WriteLine("array[{0}] = {1}", i, parray[i]);
}
int size = Marshal::SizeOf(double::typeid);
MListStruct list;
list.count = parray->Length;
list.item = Marshal::AllocCoTaskMem(size * parray->Length);
for (int i=0; i<parray->Length; i++) {
IntPtr t = IntPtr(list.item.ToInt32() + i * size);
Marshal::StructureToPtr(parray[i], t, false);
}
TraditionalDLL::TakesListStruct( list );
Marshal::FreeCoTaskMem(list.item);
}
Aucune partie de la DLL n’est exposée au code managé à l’aide de la directive traditionnelle #include
. En fait, la DLL est accessible au moment de l’exécution uniquement, de sorte que les problèmes dans les fonctions importées à l’aide de 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)