Partager via


Guide pratique pour marshaler des pointeurs de fonction à l’aide de P/Invoke

Les délégués managés peuvent être utilisés à la place des pointeurs de fonction lors de l’interopérabilité avec des fonctions non managées à l’aide des fonctionnalités .NET Framework P/Invoke. Toutefois, nous vous encourageons à utiliser les fonctionnalités d’interopérabilité C++ au lieu de cela, 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 les articles suivants :

Les API non managées qui prennent des pointeurs de fonctions en tant qu’arguments peuvent être appelés à partir du code managé à l’aide d’un délégué managé à la place du pointeur de fonction natif. Le compilateur marshale automatiquement le délégué en fonctions non managées en tant que pointeur de fonction. Il insère le code de transition managé/non managé nécessaire.

Exemple

Le code suivant se compose d’un module non managé et managé. Le module non managé est une DLL qui définit une fonction appelée TakesCallback qui accepte un pointeur de fonction. Cette adresse est utilisée pour exécuter la fonction.

// 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);
}

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

La fonction managée supprime le garbage collection pour le délégué managé afin d’empêcher le garbage collection .NET Framework de déplacer le délégué pendant l’exécution de la fonction native.

// 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);
}

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. Par conséquent, les problèmes liés aux 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)