Condividi tramite


Procedura: Effettuare il marshalling dei puntatori di funzione tramite P/Invoke

I delegati gestiti possono essere usati al posto dei puntatori a funzione durante l'interoperabilità con funzioni non gestite usando le funzionalità P/Invoke di .NET Framework. Tuttavia, è consigliabile usare le funzionalità di interoperabilità C++ quando possibile. P/Invoke fornisce una segnalazione di errori in fase di compilazione, non è indipendente dai tipi e può essere noiosa da implementare. Se l'API non gestita viene inserita in un pacchetto come DLL e il codice sorgente non è disponibile, P/Invoke è l'unica opzione. In caso contrario, vedere questi articoli:

Le API non gestite che accettano puntatori a funzioni come argomenti possono essere chiamate dal codice gestito usando un delegato gestito al posto del puntatore a funzione nativo. Il compilatore effettua automaticamente il marshalling del delegato alle funzioni non gestite come puntatore a funzione. Inserisce il codice di transizione gestito/non gestito necessario.

Esempio

Il codice seguente è costituito da un modulo non gestito e gestito. Il modulo non gestito è una DLL che definisce una funzione denominata TakesCallback che accetta un puntatore a funzione. Questo indirizzo viene usato per eseguire la funzione.

// TraditionalDll5.cpp
// compile with: /LD /EHsc
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

extern "C" {
   /* Declare an unmanaged function type that takes two int arguments
      Note the use of __stdcall for compatibility with managed code */
   typedef int (__stdcall *CALLBACK)(int);
   TRADITIONALDLL_API int TakesCallback(CALLBACK fp, int);
}

int TakesCallback(CALLBACK fp, int n) {
   printf_s("[unmanaged] got callback address, calling it...\n");
   return fp(n);
}

Il modulo gestito definisce un delegato sottoposto a marshalling al codice nativo come puntatore a funzione. Usa l'attributo DllImportAttribute per esporre la funzione nativa TakesCallback al codice gestito. main Nella funzione viene creata un'istanza del delegato e passata alla TakesCallback funzione . L'output del programma dimostra che questa funzione viene eseguita dalla funzione nativa TakesCallback .

La funzione gestita elimina l'operazione di Garbage Collection per il delegato gestito per impedire la rilocazione del delegato di .NET Framework durante l'esecuzione della funzione nativa.

// MarshalDelegate.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

public delegate int GetTheAnswerDelegate(int);
public value struct TraditionalDLL {
   [DllImport("TraditionalDLL5.dll")]
   static public int TakesCallback(GetTheAnswerDelegate^ pfn, int n);
};

int GetNumber(int n) {
   Console::WriteLine("[managed] callback!");
   static int x = 0;
   ++x;
   return x + n;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   pin_ptr<GetTheAnswerDelegate^> pp = &fp;
   Console::WriteLine("[managed] sending delegate as callback...");

   int answer = TraditionalDLL::TakesCallback(fp, 42);
}

Nessuna parte della DLL viene esposta al codice gestito usando la direttiva tradizionale #include . Infatti, la DLL è accessibile solo in fase di esecuzione, quindi i problemi con le funzioni importate tramite DllImportAttribute non possono essere rilevati in fase di compilazione.

Vedi anche

Uso esplicito di P/Invoke in C++ (DllImport attributo)