java - 我如何更改此 JNA Hook 以监听特定键而不是所有键?

标签 java winapi hook jna keyboardinterrupt

我目前正在尝试实现一个全局 key 监听器,以便用户在机器人工作时与机器人进行交互。例如 escape 退出、F1 暂停等。 这段代码有效,但它适用于输入的每个键,我正在自学 JNA,但我不明白这段代码在哪里执行特定操作以及如何更改这些操作/输入以区分它们。

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HINSTANCE;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HOOKPROC;

public class MainTestKeyHook {

    public static void main(String[] args) throws Exception {
        HOOKPROC hookProc = new HOOKPROC_bg();
        HINSTANCE hInst = Kernel32.INSTANCE.GetModuleHandle(null);

        User32.HHOOK hHook = User32.INSTANCE.SetWindowsHookEx(User32.WH_KEYBOARD_LL, hookProc, hInst, 0);
        if (hHook == null)
            return;
        User32.MSG msg = new User32.MSG();
        System.err.println("Please press any key ....");
        while (true) {
            User32.INSTANCE.GetMessage(msg, null, 0, 0);
        }   
    }
}

class HOOKPROC_bg implements HOOKPROC {

    public HOOKPROC_bg() {
    }

    public LRESULT callback(int nCode, WPARAM wParam, LPARAM lParam) {
        System.err.println("callback bbbnhkilhjkibh nCode: " + nCode);
        return new LRESULT(0);
    }
}

最佳答案

您正在寻找有关 Windows API 低级键盘 Hook 的信息。

您的回调是一个 LowLevelKeyboardProc 回调,其文档可以在 here 中找到。 .

文档显示传递给回调的 LPARAM 实际上是一个指向 KBDLLHOOKSTRUCT structure 的指针。 。该结构的成员vkCode包含按下的键的虚拟键代码。查看 virtual keycodes 的完整列表.

幸运的是,由于您已经在使用 jna-platform 包,因此您可以使用现有的类型映射。我建议你遵循 JNA demo application for global keyhooks 的内容是:

/* ... */
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;

public class MainTestKeyHook {

    public static void main(String[] args) throws Exception {
        HINSTANCE moduleHandle = Kernel32.INSTANCE.GetModuleHandle(null);
        
        HHOOK hookHandle;
        LowLevelKeyboardProc keyboardHook = new LowLevelKeyboardProc() {
            @Override
            public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
                // LowLevelKeyboardProc docs: "If nCode is less than zero, the hook
                // procedure must pass the message to the CallNextHookEx function
                // without further processing and should return the value returned
                // by CallNextHookEx."
                
                if (nCode >= 0) {
                    switch (wParam.intValue()) {
                        // alternatively WM_KEYUP and WM_SYSKEYUP
                        case WinUser.WM_KEYDOWN:
                        case WinUser.WM_SYSKEYDOWN:
                            handleKeyDown(info.vkCode);
                    }
                }
                
                Pointer ptr = info.getPointer();
                long peer = Pointer.nativeValue(ptr);
                return User32.INSTANCE.CallNextHookEx(hookHandle, nCode, wParam, new LPARAM(peer));
            }
        };
        
        hookHandle = User32.INSTANCE.SetWindowsHookEx(User32.WH_KEYBOARD_LL, keyboardHook, moduleHandle, 0);
        if (hookHandle == null)
            return;
        
        System.out.println("Please press any key ...");
        
        int result;
        MSG msg = new MSG();
        while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                System.err.println("error in GetMessage");
                break;
            }
            
            User32.INSTANCE.TranslateMessage(msg);
            User32.INSTANCE.DispatchMessage(msg);
        }
        
        User32.INSTANCE.UnhookWindowsHookEx(hookHandle);
    }
    
    // https://learn.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
    private static final int VK_F1 = 0x70;
    
    public static void handleKeyDown(int vkCode) {
        System.out.println("Key = " + vkCode");
        if (vkCode == VK_F1) {
            System.out.println("F1 pressed!");
        }
    }
}

您应该注意到,此示例调用并返回 CallNextHookEx 的值。来自 documentation :

If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.

If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD_LL hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.

正如最后一句所述,如果该键与您想要拦截的任何键匹配,您可以返回一个非零值,以防止该按键事件到达目标窗口。但要小心这一点,因为您可能会意外阻止自己的键盘输入:-)

我还建议您阅读 Windows API 文档文章 Hooks overview .

关于java - 我如何更改此 JNA Hook 以监听特定键而不是所有键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51446208/

相关文章:

java - JDBC连接池问题

c++ - 以编程方式在Windows上创建文件夹快捷方式

excel - 在VBA中,如何在不使用Application.Run()的情况下从未知模块调用特定方法?

wordpress - WordPress的。 Woocommerce。 Action Hook 在添加到购物车之前

java - 减去 bigDecimal 数字会产生精度 0?

java - RMI 或 Web 服务示例应用程序

c++ - 你能从被调用者那里得到调用者DLL或可执行模块吗

C++ WinAPI。列表框中显示错误的文本

c++ - 使用 C++ 模板包装第 3 方 C 代码

Java "Printer is not accepting jobs"老兄弟打印机