Så här gör du för att marshala funktionspekare med P/Invoke

Hanterade delegeringar kan användas istället för funktionspekare när man interagerar med ohanterade funktioner med hjälp av .NET Framework P/Invoke-funktioner. Vi rekommenderar dock att du använder C++ Interop-funktionerna i stället när det är möjligt. P/Invoke ger lite kompileringstidsfelrapportering, är inte typsäker och kan vara omständlig att implementera. Om det ohanterade API:et paketeras som en DLL och källkoden inte är tillgänglig är P/Invoke det enda alternativet. I annat fall kan du läsa följande artiklar:

Ohallade API:er som tar funktionspekare som argument kan anropas från hanterad kod genom att använda en hanterad delegering istället för den inhemska funktionspekaren. Kompilatorn omvandlar automatiskt delegeringen till ohanterad kod som en funktionspekare. Den infogar nödvändig hanterad/ohanterad övergångskod.

Exempel

Följande kod består av en ohanterad och en hanterad modul. Den ohanterade modulen är en DLL som definierar en funktion med namnet TakesCallback som accepterar en funktionspekare. Den här adressen används för att köra funktionen.

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

Den hanterade modulen definierar en delegering som konverteras till den inbyggda koden till en funktionspekare. Det använder DllImportAttribute attributet för att exponera den interna TakesCallback funktionen för den hanterade koden. I main-funktionen skapas en instans av delegaten och skickas till TakesCallback-funktionen. Programutdata visar att den här funktionen körs av den interna TakesCallback funktionen.

Den hanterade funktionen undertrycker sophantering för den hanterade delegaten för att förhindra att .NET Frameworks sophantering flyttar delegaten medan den inbyggda funktionen körs.

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

Ingen del av DLL:n exponeras för den hanterade koden med hjälp av det traditionella #include direktivet. I själva verket nås DLL endast vid körning, så problem med funktioner som importeras med hjälp av DllImportAttribute kan inte identifieras vid kompileringstillfället.

Se även

Använda explicit P/Invoke i C++ (DllImport attribut)