Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

P/Invoke - Marshaling delegate as function pointer + void*

Tags:

c#

pinvoke

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?

like image 694
geoff_h Avatar asked Sep 26 '22 22:09

geoff_h


2 Answers

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.

like image 184
David Heffernan Avatar answered Oct 11 '22 05:10

David Heffernan


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

like image 31
unknown6656 Avatar answered Oct 11 '22 06:10

unknown6656