Sdílet prostřednictvím


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)