Compartir a través de


Cómo: Serializar punteros de función mediante P/Invoke

Los delegados administrados se pueden usar en lugar de punteros de función al interoperar con funciones no administradas mediante .NET Framework características P/Invoke. Pero se recomienda usar las características de Interoperabilidad de C++en su lugar, siempre que sea posible. P/Invoke proporciona pocos informes de errores en tiempo de compilación, no es con seguridad de tipos e implementarlo puede ser tedioso. Si la API no administrada se empaqueta como una DLL y el código fuente no está disponible, P/Invoke es la única opción. De lo contrario, consulte estos artículos:

Las API no administradas que toman punteros de funciones como argumentos se pueden llamar desde código administrado mediante un delegado administrado en lugar del puntero de función nativa. El compilador serializa de forma automática el delegado en funciones no administradas como un puntero de función. Inserta el código de transición administrado o no administrado necesario.

Ejemplo

El código siguiente consta de un módulo administrado y uno no administrado. El módulo no administrado es una DLL que define una función denominada TakesCallback que acepta un puntero de función. Esta dirección se usa para ejecutar la función.

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

El módulo administrado define un delegado que se serializa en el código nativo como un puntero de función. Usa el atributo DllImportAttribute para exponer la función nativa TakesCallback al código administrado. En la función main, se crea una instancia del delegado y se pasa a la función TakesCallback. Los resultados del programa muestran que esta función nativa la ejecuta la función nativa TakesCallback.

La función administrada suprime la recolección de elementos no utilizados para el delegado administrado para evitar que la recolección de elementos no utilizados de .NET Framework reubique el delegado mientras se ejecuta la función nativa.

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

Ninguna parte del archivo DLL se expone al código administrado mediante la directiva tradicional #include. De hecho, solo se accede al archivo DLL en runtime, por lo que los problemas con las funciones importadas mediante DllImportAttribute no se pueden detectar en tiempo de compilación.

Consulte también

Utilizar un elemento PInvoke explícito en C++ (Atributo DllImport)