6

A fairly common idiom in C is for functions taking a polymorphic closure to represent this as two arguments, a function pointer and void pointer (which is passed as one of the arguments to the function pointer.

An example taken from the GPGME library:

typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd); void gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, void *hook_value); 

Conceptually, the function pointer plus void pointer represent the same thing as a delegate in C# (a closure). Is there a nice, canonical way to marshal a delegate when making this sort of P/Invoke call?

1
  • 1
    Be careful, it is your job to keep the delegate object alive. Store it in a static variable so it cannot be garbage-collected too early. Only set the variable back to null when you are sure that the C code can no longer make the callback. Commented Dec 16, 2015 at 11:38

3 Answers 3

2

Is there a nice, canonical way to marshal a delegate when making this sort of P/Invoke call?

You don't need to use the void* parameter because a C# delegate is a closure. Pass IntPtr.Zero as the hook value. Your C# delegate still needs to accept the void* parameter but it can simply ignore it since it does not need it.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. It's been a little while since I've worked with .NET. I would guess that means that some sort of shim is held in global memory to adapt the delegate to a pure function pointer?
Correct. The marshalling framework generates code during marshalling that contains the closure information.
2

You can actually pass delegate from C# to C function pointer. You should decorate this delegate with [UnmanagedFunctionPointer]attribute. This is how we wrapped a C method that takes function pointer:

The C method:

__declspec(dllexport) globle int EnvAddRouterEx(int (*queryFunction)(void*, char*)) 

The P\Invoke method:

[DllImport(clipsDllLocation, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern int EnvAddRouterEx(ClipsRouterQueryFunctionDelegate queryFunction); 

The P\Invoke delegate:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int ClipsRouterQueryFunctionDelegate(IntPtr theEnv, string logicalName); 

Comments

2

The other answers are correct, however, I just wanted to append some C#9 information:

It is now possible to use function pointers in C# as follows (requires unsafe to be enabled):

static int add(int a, int b) => a + b; delegate*<int, int, int> pointer = &add; int result = add(40, 2); 

Function pointers such as in the code snippet above are internally handled as nint (or IntPtr) and can be used in P/Invoke declarations:

[DllImport("my_library.dll")] static extern int CallMyCallback(delegate*<int, int, int> callback, int arg1, int arg2); 

An example C++ implementation of my_library.dll could be:

extern "C" __declspec(dllexport) int CallMyCallback( int (*callback)(int, int), int arg1, int arg2) { return callback(arg1, arg2); } 

CallMyCallback could be used as follows (from C#):

static int multiply(int a, int b) => a * b; int result = CallMyCallback(&multiply, -12, 7); 

Note: This language feature cannot be used with lambda-expressions or non-static methods (instance methods require an implicit this-pointer to be passed).


The official documentation does not exist yet, but you can take a look at the C#9 GitHub issue/tracker: https://github.com/dotnet/csharplang/issues/191

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.