【问题标题】:Pass a raw function pointer where the JNA definition expects a Callback?在 JNA 定义需要回调的地方传递一个原始函数指针?
【发布时间】:2014-07-06 03:16:03
【问题描述】:

我正在使用需要 Callback 的 JNA 接口。我有原生函数g 的原始地址,这是我想作为回调安装的函数。我正在调用本机函数f,它需要一个回调,并且我希望JNA 将我的原始指针编组为g 作为回调,只需将g 的地址传递给f。 JNA 可以做到这一点吗?

插图

这是我的意思的具体说明。我正在使用 Win32,我想注册一个默认窗口过程为DefWindowProc 的窗口类。在普通课程中,我会在 C 中执行以下操作来注册一个具有默认窗口过程的窗口类:

WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(WNDCLASSEX));
wcex.cbSize = sizeof(WNDCLASSEX);
...
wcex.lpfnWndProc = LoadLibrary("user32", "DefWindowProcA");
...
ATOM atom = RegisterClassEx(&wcex);

但是,有时我想用不同的窗口过程注册一个窗口类。在 C 中,我会完全按照上面的方式做,除了:

wcex.lpfnWndProc = MyWindowProc; // Address of my custom window procedure

希望现在可以清楚 JNA 的难点所在。我正在编写类似于以下内容的 Java 代码:

WNDCLASSEX wcex = new WNDCLASSEX.ByReference();
wcex.cbSize = WNDCLASSEX.size();
...
wcex.lpfnWndProc = new MyWindowProc(); // where MyWindowProc implements the Callback interface;
                                       // but what if I want to just set it to the address of
                                       // DefWindowProcA?
ATOM atom = User32.RegisterClassEx(wcex);

警告

我知道我可以在 Java 中定义函数f 的两个替代版本,其中一个采用Callback,另一个采用Pointer,并将我的地址传递给g Pointer 版本。我也知道我可以为 DefWindowProcA 创建一个“包装回调”。由于各种原因,这些都不是适当的解决方案。

【问题讨论】:

  • 写一个适配器。编写调用函数指针的回调实现。
  • @David 请从头到尾阅读问题。 OP 已经表示他们不认为“包装回调”方法是可以的。此外,这也是我的答案的初始迭代的解决方法,OP 明确评论说他们想要不同的东西。
  • @Chris 我可以阅读。只是需要按照我说的方式进行。我看不出原始指针如何成为Callback
  • @DavidHeffernan 在基本层面上(即,如果您超越 JNA),回调 指针。在 JNA 级别,它们当然不一样,因为Callback 有一个this 指针,而Pointer(更具体地说,Function)没有。但是,通过我的帖子使用的编组/解组方法,JNA 为 Callback 创建了一个代理对象,使其能够正确委托。
  • @DavidHeffernan(还有 OP):我已经编写了一个有趣的小程序来测试回调,包括 Java 端的以及类型化的 C 端的:gist.github.com/cky/7397db4908fa9616539b

标签: java winapi callback jna


【解决方案1】:

为了能够将外部指针用作Callback,您可以像 C 程序员一样使用双关语:使用联合。

public static class WindowProcUnion extends Union {
    public Pointer ptr;
    public WinUser.WindowProc wndProc;

    public WindowProcUnion(Pointer ptr) {
        this.ptr = ptr;
        setType("ptr");
        write();
        setType("wndProc");
        read();
    }
}

您现在可以读取wndProc 字段并取回可用的窗口过程回调。


对于这个用例,DefWindowProc 实际上是通过User32 类导出的,所以你可以创建一个回调来直接调用它:

public static class DefWindowProc implements WinUser.WindowProc {
    @Override
    public LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
        return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

查看Win32WindowDemo class 以获取委托给DefWindowProc 的窗口过程示例。

【讨论】:

  • 感谢@Chris Jester-Young。这并没有回答我的问题,但它确实教会了我一些我不知道的东西,这非常有帮助。我确实“真的想要”使用外部指针作为回调。这是我正在做的工作规范的一部分。然而,不仅如此。我希望能够将要么 Callback 的实例 一个存在于DLL 中的函数的原始地址传递给相同的功能,取决于我目前手头的东西。
  • 我完全理解。在我的工作中,我们的第三方库需要一个全局变量形式的回调,我必须通过 JNA 获取和设置它。我将尝试根据您的情况调整我们的工作代码。我可能需要先做一些测试,所以请耐心等待。
  • 当然。就像我说的,我不得不为工作代码做这样的事情,所以整个主题对我来说绝不是新的。那时,我不得不花费 大量 时间研究 JNA 源代码来弄清楚如何首先让它运行,所以我很高兴与他人分享这项技术。 :-D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多