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.