Я хочу запустить низкоуровневый хук клавиатуры, используя JNA. Я адаптировал пример, найденный в папке JNA contrib
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);
}
Это работает, как ожидалось. Однако теперь я хочу запустить цикл сообщений в потоке, чтобы избежать блокировки моего приложения. Наивный подход будет
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);
});
К сожалению, это не работает. Обратный вызов больше не вызывается, и клавиатурные входы в системе начинают сильно отставать. Как я могу правильно заправить цикл сообщений? Я полагаю, мне нужно найти идентификатор потока и передать его в SetWindowsHookEx
вместо 0, но как получить этот идентификатор?
Edit: я попробовал другой подход, заключив в единое целое весь процесс регистрации хуков и цикл обработки сообщений. Обратный вызов теперь работает правильно, но PostQuitMessage
, похоже, не отправляет сообщение в правильную очередь, и поток не может быть остановлен. PostThreadMessage(0, User32.WM_QUIT, null, null)
также не работает в этом случае.