java - 用 Java JNA 编写的 key 监听器。无法停止线程

标签 java jna

我使用下面的代码来监听全局按键事件:

Win32HookManager.java

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;
import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;
import com.sun.jna.platform.win32.WinUser.MSG;


import java.awt.event.KeyEvent;

public class Win32HookManager {
    private static HHOOK keyboardHook;

    public static boolean installKeyboardHook(final NativeKeyboardListener listener) {
        final User32  lib  = User32.INSTANCE;
        final HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);

        final LowLevelKeyboardProc keyboardHookProc = new LowLevelKeyboardProc() {
            @Override
            public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
                NativeKeyboardEvent ev = null;
                long                ti = System.currentTimeMillis();
                boolean             nh = true;

                if (nCode >= 0) {
                    switch (wParam.intValue()) {
                        case WinUser.WM_KEYDOWN:
                        case WinUser.WM_SYSKEYDOWN:
                            ev = new NativeKeyboardEvent(KeyEvent.KEY_PRESSED, ti, 0, info.vkCode);
                            nh = listener.keyPressed(ev);
                            break;

                        case WinUser.WM_KEYUP:
                        case WinUser.WM_SYSKEYUP:
                            ev = new NativeKeyboardEvent(KeyEvent.KEY_RELEASED, ti, 0, info.vkCode);
                            nh = listener.keyReleased(ev);
                            break;
                    }
                }

                if(nh) {
                    return lib.CallNextHookEx(keyboardHook, nCode, wParam, info.getPointer());
                }
                return new LRESULT(1);
            }
        };

        new Thread() {
            @Override
            public void run() {
                keyboardHook = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHookProc, hMod, 0);
                msgLoop();
                lib.UnhookWindowsHookEx(keyboardHook);
            }
        }.start();

        return keyboardHook != null;
    }

    public static boolean uninstallKeyboardHook() {
        if(keyboardHook != null) {
            return User32.INSTANCE.UnhookWindowsHookEx(keyboardHook);
        }

        return false;
    }

    private static void msgLoop()
    {
        final User32 lib = User32.INSTANCE;

        int result;
        MSG msg = new MSG();
        while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                System.err.println("error in get message");
                break;
            }
            else {
                System.err.println("got message");
                lib.TranslateMessage(msg);
                lib.DispatchMessage(msg);
            }
        }
    }
}

本地键盘监听器

public interface NativeKeyboardListener {
    public boolean keyPressed (NativeKeyboardEvent e);
    public boolean keyReleased(NativeKeyboardEvent e);
}

本地键盘事件

public class NativeKeyboardEvent {
    private int  id;
    private int  keyCode;

    public NativeKeyboardEvent(int id, long when, int modifiers, int keyCode) {
        this.id        = id;
        this.keyCode   = keyCode;
    }

    public int getId() {
        return id;
    }

    public int getKeyCode() {
        return keyCode;
    }
}

不幸的是,它没有像我预期的那样工作,即它检测何时按下/释放键,但由于 msgLoop 方法中的 GetMessage() 而无法完成由 installKeyboardHook() 方法启动的线程。是的,我可以停止监听关键事件,但我不能停止线程。但是,此代码中似乎需要 GetMessage()。您是否看到了解决此问题的任何解决方法?

谢谢!

最佳答案

正如@SLaks 所建议的,您需要某种标志来指示消息循环是否应继续运行。然后您可以使用 PeekMessage ( JNA Doc ) 与 GetMessage 一样,从队列中检索消息,但不是阻塞操作。然后,您的消息循环应更改为如下所示:

while (!shouldQuit) {
    while ((result = lib.PeekMessage(msg, null, 0, 0, 1)) != 0) {
        // ...
    }
}

关于java - 用 Java JNA 编写的 key 监听器。无法停止线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10684631/

相关文章:

c# - 什么等同于 Java 中的 IntPtr (C#)?

java - 如何在 vert.x 中泵送具有偏移量和长度的流?

Java String split() 丢失字符串

java - HBase FuzzyRowFilter 不返回任何结果

java - 在线程中运行消息循环

java - JMM 保证 try-with-resource 和 JNI 调用中的重新排序

java - 用于 Java/JNA 对象引用的 C 中的 C++ API 包装器

java - 在 JNA 中使用 GetWindowModuleFileName

java - 如何在 TimeoutException 上获得 future 的堆栈跟踪

java - 将不同的 GWT 实现返回到 GAE 上的移动和桌面客户端