Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico demonstra a organização de callbacks e delegates (a versão de callback gerido) entre código gerido e não gerido usando o Visual C++.
Os exemplos de código a seguir usam as diretivas de #pragma gerenciadas e não gerenciadas para implementar funções gerenciadas e não gerenciadas no mesmo arquivo, mas as funções também podem ser definidas em arquivos separados. Os arquivos que contêm apenas funções não gerenciadas não precisam ser compilados com o /clr (Common Language Runtime Compilation).
Exemplo: Configurar API não gerenciada para acionar o delegado gerenciado
O exemplo a seguir demonstra como configurar uma API não gerenciada para acionar um delegado gerenciado. Um delegado gerenciado é criado e um dos métodos de interoperabilidade, GetFunctionPointerForDelegate, é usado para recuperar o ponto de entrada subjacente para o delegado. Esse endereço é então passado para a função não gerenciada, que o chama sem conhecimento do fato de que ele é implementado como uma função gerenciada.
Observe que é possível, mas não necessário, fixar o delegado usando pin_ptr (C++/CLI) para evitar que ele seja realocado ou descartado pelo coletor de lixo. A proteção contra a coleta prematura de lixo é necessária, mas a fixação fornece mais proteção do que o necessário, pois evita a coleta, mas também impede a realocação.
Se um delegado for realocado por uma coleta de lixo, ele não afetará o retorno de chamada gerenciado subjacente, portanto Alloc , será usado para adicionar uma referência ao delegado, permitindo a realocação do delegado, mas impedindo a eliminação. Usar GCHandle em vez de pin_ptr reduz o potencial de fragmentação do heap gerenciado.
// MarshalDelegate1.cpp
// compile with: /clr
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma unmanaged
// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);
int TakesCallback(ANSWERCB fp, int n, int m) {
printf_s("[unmanaged] got callback address, calling it...\n");
return fp(n, m);
}
#pragma managed
public delegate int GetTheAnswerDelegate(int, int);
int GetNumber(int n, int m) {
Console::WriteLine("[managed] callback!");
return n + m;
}
int main() {
GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
GCHandle gch = GCHandle::Alloc(fp);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
Console::WriteLine("[managed] sending delegate as callback...");
// force garbage collection cycle to prove
// that the delegate doesn't get disposed
GC::Collect();
int answer = TakesCallback(cb, 243, 257);
// release reference to delegate
gch.Free();
}
Exemplo: ponteiro de função armazenado por API não gerenciada
O exemplo a seguir é semelhante ao exemplo anterior, mas neste caso o ponteiro de função fornecido é armazenado pela API não gerenciada, portanto, pode ser invocado a qualquer momento, exigindo que a coleta de lixo seja suprimida por um período de tempo arbitrário. Como resultado, o exemplo a seguir usa uma instância global de GCHandle para impedir que o delegado seja realocado, independentemente do escopo da função. Como discutido no primeiro exemplo, usar pin_ptr é desnecessário para esses exemplos, mas neste caso não funcionaria de qualquer maneira, pois o escopo de um pin_ptr é limitado a uma única função.
// MarshalDelegate2.cpp
// compile with: /clr
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma unmanaged
// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);
static ANSWERCB cb;
int TakesCallback(ANSWERCB fp, int n, int m) {
cb = fp;
if (cb) {
printf_s("[unmanaged] got callback address (%d), calling it...\n", cb);
return cb(n, m);
}
printf_s("[unmanaged] unregistering callback");
return 0;
}
#pragma managed
public delegate int GetTheAnswerDelegate(int, int);
int GetNumber(int n, int m) {
Console::WriteLine("[managed] callback!");
static int x = 0;
++x;
return n + m + x;
}
static GCHandle gch;
int main() {
GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
gch = GCHandle::Alloc(fp);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
Console::WriteLine("[managed] sending delegate as callback...");
int answer = TakesCallback(cb, 243, 257);
// possibly much later (in another function)...
Console::WriteLine("[managed] releasing callback mechanisms...");
TakesCallback(0, 243, 257);
gch.Free();
}