C中一个相当普遍的习惯用于采用多态闭包的函数将其表示为两个参数,一个函数指针和一个void指针(作为函数指针的一个参数传递。
取自GPGME库的示例:
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);
从概念上讲,函数指针加上void指针代表与C#(一个闭包)中的委托相同的东西。在进行这种P / Invoke调用时,是否有一种很好的,规范的方法来编组委托?
答案 0 :(得分:2)
在进行此类P / Invoke调用时,是否有一种很好的,规范的方法来编组委托?
您不需要使用void*
参数,因为C#委托是一个闭包。将IntPtr.Zero
作为挂钩值传递。您的C#委托仍然需要接受void*
参数,但它可以忽略它,因为它不需要它。
答案 1 :(得分:1)
您实际上可以将委托从C#传递给C函数指针。您应该使用[UnmanagedFunctionPointer]
属性装饰此委托。这就是我们如何包装一个带有函数指针的C方法:
C方法:
__declspec(dllexport) globle int EnvAddRouterEx(
int (*queryFunction)(void *,char *))
P \ Invoke方法:
[DllImport(clipsDllLocation, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int EnvAddRouterEx(ClipsRouterQueryFunctionDelegate queryFunction);
P \ Invoke委托:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int ClipsRouterQueryFunctionDelegate(IntPtr theEnv, string logicalName);
答案 2 :(得分:1)
其他答案是正确的,但是,我只想附加一些C#9信息:
现在可以按以下方式在C#中使用函数指针(要求启用unsafe
):
static int add(int a, int b) => a + b;
delegate*<int, int, int> pointer = &add;
int result = add(40, 2);
例如上面代码片段中的函数指针在内部被处理为nint
(或IntPtr
),并且可以在P / Invoke声明中使用:
[DllImport("my_library.dll")]
static extern int CallMyCallback(delegate*<int, int, int> callback, int arg1, int arg2);
my_library.dll
的C ++实现示例可以是:
extern "C" __declspec(dllexport) int CallMyCallback(
int (*callback)(int, int),
int arg1,
int arg2)
{
return callback(arg1, arg2);
}
CallMyCallback
可以按如下方式使用(来自C#):
static int multiply(int a, int b) => a * b;
int result = CallMyCallback(&multiply, -12, 7);
注意:此语言功能不能用于lambda表达式或非静态方法(实例方法需要传递隐式this
指针)。
尚无官方文档,但是您可以查看C#9 GitHub问题/跟踪器:https://github.com/dotnet/csharplang/issues/191