Partager via


Comment : marshaler des pointeurs fonction à l'aide de PInvoke

Mise à jour : novembre 2007

Cette rubrique explique comment les délégués managés peuvent être utilisés plutôt que des pointeurs fonction lors de l'interaction avec des fonctions non managées à l'aide des fonctionnalités P/Invoke du .NET Framework. Cependant, les programmeurs Visual C++ sont encouragés à utiliser plutôt les fonctionnalités d'interopérabilité C++ (dans la mesure du possible), car P/Invoke signale peu les erreurs de compilation, n'est pas de type sécurisé et peut être fastidieux à implémenter. Si l'API non managée se présente sous la forme d'une DLL et si le code source n'est pas disponible, P/Invoke est votre seule option. Sinon, consultez les rubriques suivantes :

Les API non managées qui prennent des pointeurs de fonction comme arguments peuvent être appelés à partir du code managé avec un délégué managé plutôt que le pointeur fonction natif. Le compilateur marshale automatiquement le délégué vers les fonctions non managées en tant que pointeur fonction et insère le code de transition managé/non managé nécessaire.

Exemple

Le code suivant est constitué d'un module non managé et d'un module managé. Le module non managé est une DLL qui définit une fonction appelée TakesCallback acceptant un pointeur fonction. Cette adresse est utilisée pour exécuter la fonction.

Le module managé définit un délégué marshalé vers le code natif en tant que pointeur fonction et utilise l'attribut DllImportAttribute pour exposer la fonction TakesCallback native au code managé. Dans la fonction principale, une instance du délégué est créée et passée à la fonction TakesCallback. La sortie du programme montre que cette fonction est exécutée par la fonction TakesCallback native.

La fonction managée supprime le garbage collection du délégué managé pour empêcher le garbage collection du .NET Framework de réadresser le délégué pendant l'exécution de la fonction native.

Le module managé est compilé avec /clr, mais /clr:pure fonctionne également.

// TraditionalDll5.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" {
   /* Declare an unmanaged function type that takes two int arguments
      Note the use of __stdcall for compatibility with managed code */
   typedef int (__stdcall *CALLBACK)(int);
   TRADITIONALDLL_API int TakesCallback(CALLBACK fp, int);
}

int TakesCallback(CALLBACK fp, int n) {
   printf_s("[unmanaged] got callback address, calling it...\n");
   return fp(n);
}

// MarshalDelegate.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

public delegate int GetTheAnswerDelegate(int);
public value struct TraditionalDLL {
   [DllImport("TraditionalDLL5.dll")]
   static public int TakesCallback(GetTheAnswerDelegate^ pfn, int n);
};

int GetNumber(int n) {
   Console::WriteLine("[managed] callback!");
   static int x = 0;
   ++x;
   return x + n;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   pin_ptr<GetTheAnswerDelegate^> pp = &fp;
   Console::WriteLine("[managed] sending delegate as callback...");

   int answer = TraditionalDLL::TakesCallback(fp, 42);
}

Notez qu'aucune partie de la DLL n'est exposée au code managé à l'aide de la directive #include traditionnelle. En réalité, l'accès à la DLL se limite au moment de l'exécution. Par conséquent, les problèmes liés aux fonctions importées à l'aide de DllImportAttribute ne sont pas détectés au moment de la compilation.

Voir aussi

Autres ressources

Utilisation d'un PInvoke explicite en C++ (attribut DllImport)