Postupy: Zařazování zpětných volání a delegátů pomocí funkcí interoperability C++
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í jazyka Visual C++.
Následující příklady kódu používají spravované, nespravované 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 pomocí /clr (Common Language Runtime Compilation).
Příklad: Konfigurace nespravovaného rozhraní API pro aktivaci spravovaného delegáta
Následující příklad ukazuje, jak nakonfigurovat nespravované rozhraní API pro aktivaci spravovaného delegáta. Spravovaný delegát je vytvořen a jedna z metod vzájemné spolupráce , GetFunctionPointerForDelegateslouží k načtení podkladového vstupního bodu pro delegáta. Tato adresa se pak předá nespravované funkci, která ji volá bez znalosti skutečnosti, že je implementovaná jako spravovaná funkce.
Všimněte si, že je to možné, ale není nutné připnout delegáta pomocí pin_ptr (C++/CLI), aby se zabránilo jeho opětovnému umístění nebo odstranění uvolňováním paměti. Ochrana před předčasně uvolňováním paměti je nutná, ale připnutí poskytuje větší ochranu, než je nutné, protože brání shromažďování, ale také brání přemístění.
Pokud je delegát znovu umístěn v uvolňování paměti, nebude mít vliv na základní spravované zpětné volání, takže Alloc se používá k přidání odkazu na delegáta, což umožňuje přemístění delegáta, ale zabrání odstranění. Použití GCHandle místo pin_ptr snižuje fragmentaci potenciálu 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();
}
Příklad: Ukazatel funkce uložený nespravovaným rozhraním API
Následující příklad je podobný předchozímu příkladu, ale v tomto případě je zadaný ukazatel funkce uložen nespravovaným rozhraním API, takže ho lze vyvolat kdykoli a vyžadovat, aby se uvolňování paměti potlačí po libovolnou dobu. V důsledku toho následující příklad používá globální instanci GCHandle , aby se delegát nemohl přemísťovat, nezávisle na rozsahu funkce. Jak je popsáno v prvním příkladu, použití pin_ptr pro tyto příklady není nutné, ale v tomto případě by stejně nefungovalo, protože rozsah pin_ptr je omezený na jednu 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();
}
Viz také
Použití zprostředkovatele komunikace C++ (implicitní služba PInvoke)
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro