Como: PInvoke usando ponteiros de função de empacotamento
Este tópico explica os delegados como gerenciado pode ser usado no lugar de ponteiros de função quando interoperar com não gerenciado funções usando os recursos do .NET estrutura P/Invoke.No entanto, os programadores de Visual C++ são incentivados a usar os recursos de interoperabilidade C++ em vez disso (quando for possível) porque P/Invoke fornece relatórios de erro em time de compilar pouco, não é fortemente tipado e pode ser entediante implementar.Se a API não gerenciada é empacotada sistema autônomo uma DLL e o código-fonte não está disponível, P/Invoke é a única opção.Caso contrário, consulte os tópicos a seguir:
APIs não gerenciadas levar ponteiros de funções sistema autônomo argumentos podem ser chamados a partir de código com um delegado gerenciado no lugar do gerenciado de nativo ponteiro de função.O compilador automaticamente empacota o delegado para funções não gerenciadas sistema autônomo um ponteiro de função e insere o código necessário transição gerenciado/não gerenciado.
Exemplo
O código a seguir consiste em um não-gerenciados e um módulo gerenciado.O módulo não gerenciado é uma DLL que define uma função chamada TakesCallback que aceita um ponteiro de função.Esse endereço é usado para executar a função.
O módulo gerenciado define um delegado que empacotado para código nativo sistema autônomo um ponteiro de função e usa o DllImportAttribute atributo para expor a função TakesCallback nativa para código gerenciado. Na função principal, uma instância do delegado é criada e passada para a função TakesCallback.A saída do programa demonstra que essa função seja executada pela função nativo TakesCallback.
A função gerenciada suprime a coleta de lixo para o delegado gerenciado evitar a coleta de lixo do .NET estrutura de realocar o delegado enquanto executa a função nativa.
O módulo gerenciado é compilado com/CLR, mas com/CLR: pura funciona bem.
// 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);
}
Observe que nenhuma parte da DLL seja exposta a código gerenciado usando o # tradicional diretiva include.Na verdade, a DLL é acessada na execução time apenas, portanto, problemas com funções importados com DllImportAttribute não serão detectados no momento da compilar.