Jak: Marshal zpětná volání a Delegáti pomocí C++ Interop
Toto téma ukazuje zařazování zpětných volání a delegátů (spravovaná verze zpětného volání) mezi spravovaným a nespravovaným kódem pomocí Visual C++.
Následující příklady kódu používají managed, unmanaged direktivy #pragma k implementaci spravovaných a nespravovaných funkcí ve stejném souboru, ale tyto funkce mohou být také definovány v samostatných souborech.Soubory, obsahující pouze nespravované funkce, nemusí být kompilovány s /CLR (kompilace společné Language Runtime).
Příklad
Následující příklad ukazuje, jak nakonfigurovat nespravované rozhraní API pro spuštění spravovaného delegáta.Je vytvořen spravovaný delegát a jedna z metod interoperability, GetFunctionPointerForDelegate, je použita k načtení základního vstupního bodu pro delegáta.Tato adresa je pak předána nespravované funkci, která ji volá bez znalosti faktu, že je implementována jako spravovaná funkce.
Všimněte si, že je možné, ale ne nezbytné, připnout delegáta pomocí pin_ptr (C++/CLI), aby mu bylo zabráněno ve změně umístění nebo odstranění uvolňováním paměti.Ochrana proti předčasnému uvolnění paměti je nutná, ale připnutí poskytuje větší ochranu než je nezbytné, protože zabraňuje uvolňování, ale také zabraňuje přemístění.
Pokud je delegát přemístěn uvolněním paměti, nebude to mít vliv na podkladové zpětné volání, takže je použito Alloc pro přidání odkazu na delegáta, s umožněním přemístění delegáta, ale zabráněním vyřazení.Použití GCHandle namísto pin_ptr redukuje potenciál fragmentace spravované haldy.
// 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();
}
Následující příklad je podobný jako předchozí příklad, ale v tomto případě je poskytnutý ukazatel funkce uložen nespravovaným rozhraním API, takže může být vyvolán kdykoli, s vyžadováním potlačení uvolňování paměti pro libovolnou dobu.V důsledku toho následující příklad používá globální instanci GCHandle pro zabránění přemístění delegáta, nezávisle na rozsahu funkce.Jak je uvedeno v prvním příkladu, použití pin_ptr je pro tyto příklady zbytečné, ale v tomto případě by stejně nepracovalo, protože rozsah pin_ptr je omezen na jedinou funkci.
// 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();
}