This topic explains how managed delegates can be used in place of function pointers when interoperating with unmanaged functions using .NET Framework P/Invoke features. However, C++ programmers are encouraged to use the C++ Interop features instead (when possible) because P/Invoke provides little compile-time error reporting, is not type-safe, and can be tedious to implement. If the unmanaged API is packaged as a DLL and the source code is not available, P/Invoke is the only option. Otherwise, see the following topics:
Unmanaged APIs that take functions pointers as arguments can be called from managed code with a managed delegate in place of the native function pointer. The compiler automatically marshals the delegate to unmanaged functions as a function pointer and inserts the necessary managed/unmanaged transition code.
Example
The following code consists of an unmanaged and a managed module. The unmanaged module is a DLL that defines a function called TakesCallback that accepts a function pointer. This address is used to execute the function.
The managed module defines a delegate that is marshaled to the native code as a function pointer and uses the
The managed function suppresses garbage collection for the managed delegate to prevent .NET Framework garbage collection from relocating the delegate while the native function executes.
The managed module is compiled with /clr, but /clr:pure works as well.
В | Copy Code |
---|---|
// 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" { typedef int (*CALLBACK)(); TRADITIONALDLL_API int TakesCallback(CALLBACK fp); } int TakesCallback(CALLBACK fp) { printf("[unmanaged] got callback address, calling it...\n"); return fp(); } |
В | Copy Code |
---|---|
// MarshalDelegate.cpp // compile with: /clr using namespace System; using namespace System::Runtime::InteropServices; public delegate int GetTheAnswerDelegate(); public value struct TraditionalDLL { [DllImport("TraditionalDLL5.dll")] static public int TakesCallback(GetTheAnswerDelegate^ pfn); }; int GetNumber() { Console::WriteLine("[managed] callback!"); static int x = 0; return ++x; } int main() { GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber); pin_ptr<GetTheAnswerDelegate^> pp = &fp; Console::WriteLine("[managed] sending delegate as callback..."); int answer = TraditionalDLL::TakesCallback(fp); } |
Note that no portion of the DLL is exposed to the managed code using the traditional #include directive. In fact, the DLL is accessed at run time only, so problems with functions imported with DllImportAttribute will not be detected at compile time.