1

我一直在尝试使用 JNI 实现简单的低级 keyhook,并且一切顺利,直到我发现当 DLL 处于无限循环(消息循环)时我无法调用这些方法。所以我决定创建新线程,但不知何故,在我创建它之后,消息循环在它自己的循环上运行,低级 keyhook 停止响应,这意味着它不再调用 keyproc,我不知道这是为什么?还有其他解决方法吗?我需要能够在键盘挂钩仍在运行时调用 DLL 的方法。

我当前的代码很简单

注册键盘钩子:

keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardProc, hInstance, 0);

启动线程

HANDLE threadHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, &threadId);

我的键盘 proc 和 threadproc 如下:

DWORD WINAPI ThreadProc(LPVOID lpVoid) {
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

LRESULT CALLBACK keyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    KBDLLHOOKSTRUCT keyEvent = *((KBDLLHOOKSTRUCT*)lParam);
    jint vkCode = keyEvent.vkCode;
    jint flags = keyEvent.flags;
    jint action = wParam;
    (*globalEnv).CallVoidMethod(globalObj, keyboardMethodId, vkCode, flags, action);
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

我哪里错了?即使我为从未调用过的 keyproc 添加简单的日志记录,我也确信它不是 java。但是,如果我停止使用线程并在主线程上运行消息循环,它工作正常,但 DLL 不会响应它之后的方法调用。

4

2 回答 2

3

您需要在调用 SetWindowsHookEx() 的同一线程上抽取消息循环。因此,只需将调用移至您的 ThreadProc()。当然,请注意您的 CallVoidMethod() 回调也在同一个线程上运行,因此请小心您在该函数中所做的事情。您访问的任何共享状态都需要使用锁进行保护。

于 2012-04-06T15:09:25.247 回答
2

您正在尝试安装桌面范围的挂钩,该挂钩跨越桌面的所有进程。也就是说,您的 DLL 被映射到具有特定于进程的全局变量集的多个进程中。你在其他进程中没有valid globalEnv,你可能会遇到访问冲突或类似的错误(全局变量可以用共享数据段创建,详见这篇文章)。

要安装线程特定的钩子,您将需要不同类型的钩子(WH_KEYBOARD_LL仅限全局!)和非零的最后一个参数SetWindowsHookEx

于 2012-04-06T15:04:33.393 回答