Udostępnij za pośrednictwem


Porady: kierowanie wywołań zwrotnych i delegatów za pomocą międzyoperacyjności języka C++

W tym temacie przedstawiono marshalling wywołań zwrotnych i delegatów (zarządzana wersja wywołania zwrotnego) między zarządzanym i niezarządzanym kodem przy użyciu języka Visual C++.

Poniższe przykłady kodu używają zarządzanych, niezarządzanych dyrektyw #pragma w celu zaimplementowania funkcji zarządzanych i niezarządzanych w tym samym pliku, ale funkcje mogą być również zdefiniowane w oddzielnych plikach. Pliki zawierające tylko funkcje niezarządzane nie muszą być kompilowane przy użyciu /clr (kompilacja środowiska uruchomieniowego języka wspólnego)..

Przykład: Konfigurowanie niezarządzanych interfejsów API w celu wyzwolenia zarządzanego delegata

W poniższym przykładzie pokazano, jak skonfigurować niezarządzany interfejs API w celu wyzwolenia zarządzanego delegata. Zarządzany delegat jest tworzony, a jeden z metod GetFunctionPointerForDelegatemiędzyoperacyjności służy do pobierania bazowego punktu wejścia dla delegata. Ten adres jest następnie przekazywany do funkcji niezarządzanej, która wywołuje go bez znajomości faktu, że jest implementowany jako funkcja zarządzana.

Zwróć uwagę, że istnieje możliwość przypięcia delegata przy użyciu pin_ptr (C++/CLI), aby zapobiec ponownemu zlokalizowaniu lub likwidacji przez moduł odśmiecania pamięci. Wymagana jest ochrona przed przedwczesnym odzyskiwaniem pamięci, ale przypinanie zapewnia większą ochronę niż jest to konieczne, ponieważ zapobiega to zbieraniu, ale także zapobiega relokacji.

Jeśli delegat zostanie ponownie zlokalizowany przez odzyskiwanie pamięci, nie wpłynie to na bazowe zarządzane wywołanie zwrotne, dlatego Alloc służy do dodawania odwołania do delegata, co umożliwia przeniesienie delegata, ale uniemożliwia usunięcie. Użycie biblioteki GCHandle zamiast pin_ptr zmniejsza potencjał fragmentacji zarządzanego sterta.

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

Przykład: wskaźnik funkcji przechowywany przez niezarządzany interfejs API

Poniższy przykład jest podobny do poprzedniego przykładu, ale w tym przypadku udostępniony wskaźnik funkcji jest przechowywany przez niezarządzany interfejs API, więc można go wywołać w dowolnym momencie, co wymaga pomijania odzyskiwania pamięci przez dowolny czas. W związku z tym w poniższym przykładzie użyto wystąpienia globalnego , GCHandle aby zapobiec przeniesieniu delegata niezależnie od zakresu funkcji. Jak wspomniano w pierwszym przykładzie, użycie pin_ptr jest niepotrzebne w przypadku tych przykładów, ale w tym przypadku mimo to nie zadziała, ponieważ zakres pin_ptr jest ograniczony do pojedynczej funkcji.

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

Zobacz też

Korzystanie z międzyoperacyjności języka C++ (niejawna funkcja PInvoke)