Partilhar via


Como Marshalizar Ponteiros de Função Usando P/Invoke

Os delegados gerenciados podem ser usados no lugar de ponteiros de função ao interoperar com funções não gerenciadas usando os recursos P/Invoke do .NET Framework. No entanto, recomendamos que você use os recursos de interoperabilidade C++ em vez disso, quando possível. P/Invoke fornece poucos relatórios de erros em tempo de compilação, não é seguro para digitação e pode ser tedioso de implementar. Se a API não gerenciada for empacotada como uma DLL e o código-fonte não estiver disponível, P/Invoke será a única opção. Caso contrário, consulte estes artigos:

APIs não geridas que usam ponteiros de funções como parâmetros podem ser chamadas a partir de código gerido usando um delegado gerido em vez do ponteiro de função nativo. O compilador encaminha automaticamente o delegado para funções não geridas como um ponteiro de função. Ele insere o código de transição gerenciado/não gerenciado necessário.

Exemplo

O código a seguir consiste em um módulo não gerenciado 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. Este endereço é usado para executar a função.

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

O módulo gerenciado define um delegado que é transferido para o código nativo como um ponteiro de função. Ele usa o DllImportAttribute atributo para expor a função nativa TakesCallback ao código gerenciado. Na função main, uma instância do delegado é criada e passada para a função TakesCallback. A saída do programa demonstra que esta função é executada pela função nativa TakesCallback .

A função gerenciada suprime a coleta de lixo para o delegado gerenciado para impedir que a coleta de lixo do .NET Framework realoque o delegado enquanto a função nativa é executada.

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

Nenhuma parte da DLL é exposta ao código gerenciado usando a diretiva tradicional #include . Na verdade, a DLL é acessada apenas em tempo de execução, portanto, problemas com funções importadas usando DllImportAttribute não podem ser detetados em tempo de compilação.

Ver também

Usando P/Invoke explícito em C++ (DllImport atributo )