Search code examples
javahookjna

Windows low level key hook stops working - not a timeout issue


PROBLEM
I'm implementing a Java program that uses a low level key hook through JNA. Everything works fine for the first few runs, but after some runs the hook stops working and only work again if I restart the computer.
- Additional detail: 'stops working' in this case means that the hook detects the first key down event and no other (even the respective key up event)

POTENTIAL ROOT CAUSES ALREADY TRIED
1 - Hook timeout -> apparently, there is some unexplained Windows 7 behaviour through which Windows removes a hook. If that was the case, restarting the app and the hook would 'ressurect' it - but thats not what happens.
2 - Excess of hooks -> at first, I didn't removed my hooks from previous runs. Now I only end the app after removing the hooks, but the problem continues.

CODE

[...]
    public static void start() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                lib = User32.INSTANCE;
                HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
                ll = new MyLowLevelKeyboardProc();
                hook = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, ll, hMod, 0);

                // We must maintain this code to keep the listener thread alive
                MSG msg = new MSG();
                while (lib.GetMessage(msg, null, 0, 0) != 0) {
                    System.out.println("cycle");
                }
                finish();
            }
        });
    }
[...]
    static class MyLowLevelKeyboardProc implements LowLevelKeyboardProc {

        @Override
        public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
            if (nCode >= 0) {
            switch (wParam.intValue()) {
                    case WinUser.WM_SYSKEYUP:
                    case WinUser.WM_KEYUP:
                        System.out.println("KEY_UP");
                        if (AppConfig.DEBUG_MODE) {
                            if (info.vkCode == WindowsKeys.WK_Q.value()) {
                                finish();
                                System.exit(0);
                            }
                        }
                default:
                    break;
            }
        }
        return lib.CallNextHookEx(hook, nCode, wParam, info.getPointer());
    }

    public static void finish() {
        if (lib != null) {
            lib.UnhookWindowsHookEx(hook);
        }
    }

Solution

  • The key is to do as little as possible in the hook. Set a flag and then perform the System.exit() call on a different thread.