【问题标题】:P/Invoke - Marshaling delegate as function pointer + void*P/Invoke - 将代理编组为函数指针 + void*
【发布时间】:2015-12-16 10:37:40
【问题描述】:

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 调用时,是否有一种很好的规范方式来编组委托?

【问题讨论】:

  • 小心,让委托对象保持活动状态是您的工作。将其存储在静态变量中,这样就不会过早地进行垃圾收集。仅当您确定 C 代码无法再进行回调时,才将变量设置回 null。

标签: c# pinvoke


【解决方案1】:

在进行此类 P/Invoke 调用时,是否有一种很好的、​​规范的方式来编组委托?

您不需要使用void* 参数,因为C# 委托是一个闭包。传递IntPtr.Zero 作为挂钩值。您的 C# 委托仍需要接受 void* 参数,但它可以简单地忽略它,因为它不需要它。

【讨论】:

  • 谢谢。自从我使用 .NET 以来已经有一段时间了。我猜这意味着某种垫片被保存在全局内存中,以使委托适应纯函数指针?
  • 正确。编组框架在编组期间生成包含闭包信息的代码。
【解决方案2】:

您实际上可以将委托从 C# 传递给 C 函数指针。你应该用[UnmanagedFunctionPointer]attribute 装饰这个委托。这就是我们包装一个接受函数指针的 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);

【讨论】:

    【解决方案3】:

    其他答案是正确的,但是,我只是想附加一些 C#9 信息:

    现在可以按如下方式在 C# 中使用函数指针(需要启用 unsafe):

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

    上面代码 sn-p 中的函数指针在内部被处理为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 issue/tracker:https://github.com/dotnet/csharplang/issues/191

    【讨论】:

      猜你喜欢
      • 2013-10-19
      • 1970-01-01
      • 2014-04-22
      • 1970-01-01
      • 2012-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多