我一直在尝试使用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);
}
我哪里出错了? 我确信它不是java,即使我为从未调用的keyproc添加简单的日志记录。但是如果我停止使用线程并在主线程上运行消息循环它工作正常但DLL不会响应它之后的方法调用。
答案 0 :(得分:2)
您需要在调用SetWindowsHookEx()的同一线程上抽取消息循环。所以只需将调用移到ThreadProc()即可。当然,请注意您的CallVoidMethod()回调也在同一个线程上运行,因此请小心您在该函数中执行的操作。您访问的任何共享状态都需要使用锁保护。
答案 1 :(得分:1)
您正在尝试安装桌面范围的挂钩,该挂钩跨越桌面的所有进程。也就是说,您的DLL被映射到具有特定于进程的全局变量集的多个进程。您在其他进程中没有有效globalEnv
,并且您可能会遇到访问冲突或类似错误(可以使用共享数据段创建全局变量,请参阅this article for details)。
要安装特定于线程的钩子,您需要一个不同类型的钩子(WH_KEYBOARD_LL
仅为全局!)和SetWindowsHookEx
中的非零最后一个参数。