Delen via


Hoe te: Marshallen van Callbacks en Delegates met behulp van C++ Interop

In dit onderwerp wordt het marshallen van callbacks en gemachtigden (de beheerde versie van een callback) tussen beheerde en onbeheerde code gedemonstreerd met behulp van Visual C++.

In de volgende codevoorbeelden worden de beheerde, onbeheerde #pragma-instructies gebruikt voor het implementeren van beheerde en onbeheerde functies in hetzelfde bestand, maar de functies kunnen ook worden gedefinieerd in afzonderlijke bestanden. Bestanden met alleen niet-beheerde functies hoeven niet te worden gecompileerd met de /clr (Common Language Runtime Compilation).

Voorbeeld: Niet-beheerde API configureren om beheerde gemachtigde te activeren

In het volgende voorbeeld ziet u hoe u een niet-beheerde API configureert om een beheerde gemachtigde te activeren. Er wordt een beheerde gemachtigde gemaakt en een van de interoperabiliteitsmethoden, GetFunctionPointerForDelegatewordt gebruikt om het onderliggende toegangspunt voor de gemachtigde op te halen. Dit adres wordt vervolgens doorgegeven aan de onbeheerde functie, die het aanroept zonder kennis van het feit dat het wordt geïmplementeerd als een beheerde functie.

U ziet dat het mogelijk is, maar niet nodig, om de delegee te pinnen met pin_ptr (C++/CLI) om te voorkomen dat deze verplaatst of verwijderd wordt door de garbage collector. Bescherming tegen voortijdige vuilnisophaling is nodig, maar pinnen biedt meer bescherming dan nodig is, omdat het ophalen voorkomt, maar ook verplaatsing verhindert.

Als een delegate wordt verplaatst door een garbage collection, heeft dit geen invloed op de onderliggende beheerde callback, dus Alloc wordt gebruikt om een verwijzing naar de delegate toe te voegen, waardoor de delegate opnieuw kan worden verplaatst, maar wordt verwijdering voorkomen. Het gebruik van GCHandle in plaats van pin_ptr vermindert fragmentatiepotentieel van de beheerde heap.

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

Voorbeeld: Functiepointer opgeslagen door niet-beheerde API

Het volgende voorbeeld is vergelijkbaar met het vorige voorbeeld, maar in dit geval wordt de opgegeven functieaanwijzer opgeslagen door de niet-beheerde API, zodat deze op elk gewenst moment kan worden aangeroepen, waardoor garbagecollection gedurende een willekeurige tijdsduur wordt onderdrukt. Als gevolg hiervan gebruikt het volgende voorbeeld een globaal exemplaar van GCHandle om te voorkomen dat de gedelegeerde wordt verplaatst, onafhankelijk van het functiebereik. Zoals besproken in het eerste voorbeeld, is het gebruik van pin_ptr niet nodig voor deze voorbeelden, maar in dit geval zou het niet werken, omdat het bereik van een pin_ptr beperkt is tot één functie.

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

Zie ook

C++ Interop gebruiken (impliciete PInvoke)