I want to run a low level keyboard hook using JNA. I've adapted an example found within JNA contrib folder
private static HHOOK keyHook;
private static LowLevelKeyboardProc keyCallback;
public static void main(String[] args) {
final User32 U32 = User32.INSTANCE;
final Kernel32 K32 = Kernel32.INSTANCE;
HMODULE module = K32.GetModuleHandle(null);
keyCallback = (int code, WPARAM wParam, KBDLLHOOKSTRUCT info) -> {
if (code >= 0) {
System.err.println("Key=" + info.vkCode);
if (info.vkCode == 81) {
U32.PostQuitMessage(0);
}
}
long peer = Pointer.nativeValue(info.getPointer());
return U32.CallNextHookEx(keyHook, code, wParam, new LPARAM(peer));
};
keyHook = U32.SetWindowsHookEx(WH_KEYBOARD_LL, keyCallback, module, 0);
System.out.println("Hook installed, type anywhere, 'q' to quit");
MSG msg = new MSG();
while (U32.GetMessage(msg, null, 0, 0) > 0) {
U32.TranslateMessage(msg);
U32.DispatchMessage(msg);
}
U32.UnhookWindowsHookEx(keyHook);
}
This is working as expected. Now however, I want to run the message loop in a thread to avoid blocking my application. A naive approach would be
Executor thread = Executors.newSingleThreadExecutor();
thread.execute(() -> {
int result;
MSG msg = new MSG();
while ((result = U32.GetMessage(msg, null, 0, 0)) > 0) {
U32.TranslateMessage(msg);
U32.DispatchMessage(msg);
}
U32.UnhookWindowsHookEx(keyHook);
});
Sadly this is not working. The callback is no longer invoked and keyboard inputs on the system start lagging massively. How can I thread my message loop correctly? I suppose I need to find the thread id and pass it to SetWindowsHookEx instead of 0, but how to obtain that id?
Edit: I've tried a different approach by encapsulating the whole hook registering and message loop in one thread. The callback now works correctly, but PostQuitMessage does not seem to post the message to the correct queue and the thread cannot be stopped. PostThreadMessage(0, User32.WM_QUIT, null, null) does not work either in that case.