Condividi tramite


Procedura: Effettuare il marshalling di callback e delegati utilizzando l'interoperabilità C++

In questo argomento viene illustrato il marshalling di callback e delegati (la versione gestita di un callback) tra codice gestito e non gestito usando Visual C++.

Gli esempi di codice seguenti usano le direttive #pragma gestite e non gestite per implementare funzioni gestite e non gestite nello stesso file, ma le funzioni possono essere definite anche in file separati. I file contenenti solo funzioni non gestite non devono essere compilati con /clr (compilazione Common Language Runtime).

Esempio: Configurare l'API non gestita per attivare il delegato gestito

L'esempio seguente illustra come configurare un'API non gestita per attivare un delegato gestito. Viene creato un delegato gestito e uno dei metodi di interoperabilità, GetFunctionPointerForDelegate, viene usato per recuperare il punto di ingresso sottostante per il delegato. Questo indirizzo viene quindi passato alla funzione non gestita, che lo chiama senza alcuna conoscenza del fatto che viene implementato come funzione gestita.

Si noti che è possibile, ma non necessario, aggiungere il delegato usando pin_ptr (C++/CLI) per impedire che si trovi nuovamente o venga eliminato dal Garbage Collector. È necessaria la protezione da un'operazione di Garbage Collection prematura, ma l'aggiunta garantisce una maggiore protezione del necessario, in quanto impedisce la raccolta, ma impedisce anche la rilocazione.

Se un delegato si trova nuovamente da un'operazione di Garbage Collection, non influirà sul callback gestito sottostante, quindi Alloc viene usato per aggiungere un riferimento al delegato, consentendo la rilocazione del delegato, ma impedendo l'eliminazione. L'uso di GCHandle anziché pin_ptr riduce il potenziale di frammentazione dell'heap gestito.

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

Esempio: Puntatore a funzione archiviato dall'API non gestita

L'esempio seguente è simile all'esempio precedente, ma in questo caso il puntatore alla funzione fornito viene archiviato dall'API non gestita, quindi può essere richiamato in qualsiasi momento, richiedendo che la Garbage Collection venga eliminata per un periodo di tempo arbitrario. Di conseguenza, l'esempio seguente usa un'istanza globale di GCHandle per impedire che il delegato venga spostato, indipendentemente dall'ambito della funzione. Come illustrato nel primo esempio, l'uso di pin_ptr non è necessario per questi esempi, ma in questo caso non funzionerebbe comunque, poiché l'ambito di un pin_ptr è limitato a una singola funzione.

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

Vedi anche

Uso delle funzionalità di interoperabilità C++ (PInvoke implicito)