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
)