Vorgehensweise: Marshal-Funktionszeiger mit P/Invoke

Verwaltete Stellvertretungen können anstelle von Funktionszeigern verwendet werden, wenn Sie mit nicht verwalteten Funktionen interagieren, indem Sie .NET Framework P/Invoke-Features verwenden. Wir empfehlen Ihnen jedoch, stattdessen nach Möglichkeit die C++-Interop-Features zu verwenden. P/Invoke bietet wenig Kompilierungszeitfehlerberichterstattung, ist nicht typsicher und kann nicht implementiert werden. Wenn die nicht verwaltete API als DLL verpackt ist und der Quellcode nicht verfügbar ist, ist P/Invoke die einzige Option. Andernfalls lesen Sie die folgenden Artikel:

Nicht verwaltete APIs, die Funktionszeiger als Argumente verwenden, können mithilfe eines verwalteten Delegaten anstelle des systemeigenen Funktionszeigers aus verwaltetem Code aufgerufen werden. Der Compiler marshallt den Delegaten automatisch als Funktionszeiger an nicht verwaltete Funktionen. Er fügt den erforderlichen verwalteten/nicht verwalteten Übergangscode ein.

Beispiel

Der folgende Code besteht aus einem nicht verwalteten und einem verwalteten Modul. Das nicht verwaltete Modul ist eine DLL, die eine Funktion definiert, die einen TakesCallback Funktionszeiger akzeptiert. Diese Adresse wird verwendet, um die Funktion auszuführen.

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

Das verwaltete Modul definiert einen Delegaten, der als Funktionszeiger an den systemeigenen Code gemarstet wird. Es verwendet das DllImportAttribute Attribut, um die systemeigene TakesCallback Funktion für den verwalteten Code verfügbar zu machen. In der main Funktion wird eine Instanz des Delegaten erstellt und an die TakesCallback Funktion übergeben. Die Programmausgabe zeigt, dass diese Funktion von der systemeigenen TakesCallback Funktion ausgeführt wird.

Die verwaltete Funktion unterdrückt die Garbage Collection für den verwalteten Delegaten, um zu verhindern, dass die Garbage Collection von .NET Framework den Delegaten neu angibt, während die systemeigene Funktion ausgeführt wird.

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

Für den verwalteten Code wird kein Teil der DLL mit der herkömmlichen #include Direktive verfügbar gemacht. Tatsächlich wird zur Laufzeit nur auf die DLL zugegriffen, sodass Probleme mit funktionen, die mithilfe der Verwendung DllImportAttribute importiert werden, zur Kompilierungszeit nicht erkannt werden können.

Siehe auch

Verwenden expliziter P/Invoke in C++ (DllImport Attribut)