geoff_h
geoff_h

Reputation: 1253

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

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?

Upvotes: 5

Views: 1250

Answers (3)

unknown6656
unknown6656

Reputation: 2963

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

Upvotes: 2

Amittai Shapira
Amittai Shapira

Reputation: 3827

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

Upvotes: 1

David Heffernan
David Heffernan

Reputation: 612963

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.

Upvotes: 2

Related Questions