为什么键盘回调工作两次并进行双键打印(Java/JNA)?

Why keyboard callback work twice and make double key printing (Java/JNA)?

已解决。请参阅下面我的回答。


在我的代码中,我试图重新映射键盘键,例如z->s。我正在使用 JNA 库 5.6.0 和 jna-platform 5.6.0,但是熟悉 C 语言的人也能理解我,因为 JNA正在使用来自 User32 和 Kernel32 dll 的 WinAPI 函数。

我的问题是新密钥打印了两次,并且在新密钥之间出现了旧密钥。 当我键入“z”时,它应该打印“s”,但它打印的是“szs”。

我的代码:

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;

public class ReMapper {

    private static WinUser.HHOOK hHook;
    final User32 user32Library = User32.INSTANCE;
    WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
    static WinUser.INPUT input = new WinUser.INPUT();

    public void reMapOn() {
        WinUser.LowLevelKeyboardProc keyboardHook = new WinUser.LowLevelKeyboardProc() {

            @Override
            public WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, WinUser.KBDLLHOOKSTRUCT kbdllhookstruct) {
                if (nCode >= 0) {
                    switch (wParam.intValue()) {
                        case WinUser.WM_KEYUP:
                        case WinUser.WM_KEYDOWN:
                        case WinUser.WM_SYSKEYUP:
                        case WinUser.WM_SYSKEYDOWN:
                            if (kbdllhookstruct.vkCode == 90) {
                                sendKey(83);
                                break;
                            }
                        default:
                            wParam.setValue(WinUser.WM_SYSKEYDOWN);
                            break;
                    }
                }
                Pointer ptr = kbdllhookstruct.getPointer();
                long peer = Pointer.nativeValue(ptr);
                return user32Library.CallNextHookEx(hHook, nCode, wParam, new WinDef.LPARAM(peer));
            }
        };

        hHook = user32Library.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0);

        int result;
        WinUser.MSG msg = new WinUser.MSG();
        while ((result = user32Library.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                break;
            } else {
                user32Library.TranslateMessage(msg);
                user32Library.DispatchMessage(msg);
            }
        }
    }

    static void sendKey(int keyCode) {

        input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
        input.input.setType("ki"); // Because setting INPUT_INPUT_KEYBOARD is not enough: https://groups.google.com/d/msg/jna-users/NDBGwC1VZbU/cjYCQ1CjBwAJ
        input.input.ki.wScan = new WinDef.WORD(0);
        input.input.ki.time = new WinDef.DWORD(0);
        input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
// Press
        input.input.ki.wVk = new WinDef.WORD(keyCode); // 0x41
        input.input.ki.dwFlags = new WinDef.DWORD(0);  // keydown

        User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());

// Release
        input.input.ki.wVk = new WinDef.WORD(keyCode); // 0x41
        input.input.ki.dwFlags = new WinDef.DWORD(2);  // keyup

        User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());
    }

    public static void main(String[] args) {
        new ReMapper().reMapOn();
    }
}

我的打印示例:

谁能帮我理解为什么会这样?

通过更改本机 callback() 函数的 returnig 值解决了。 将 oldKey 重新映射到 newKey 后,我们应该显式 return new WinDef.LRESULT(1);

if (wParam.intValue() == WinUser.WM_KEYDOWN) {
    if (kbDllHookStruct.vkCode == 90) {
     sendKey(83);
     return new WinDef.LRESULT(1);
    }
}

这是我所有更改后的工作代码,将“z”重新映射为“s”:

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;

public class ReMapper {

    private static WinUser.HHOOK hHook;
    final User32 user32Library = User32.INSTANCE;
    WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
    static WinUser.INPUT input = new WinUser.INPUT();

    public void reMapOn() {
        WinUser.LowLevelKeyboardProc keyboardHook = new WinUser.LowLevelKeyboardProc() {

            @Override
            public WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, WinUser.KBDLLHOOKSTRUCT kbDllHookStruct) {
                if (nCode >= 0) {
                    if (wParam.intValue() == WinUser.WM_KEYDOWN) {
                        if (kbDllHookStruct.vkCode == 90) {
                            sendKey(83);
                            return new WinDef.LRESULT(1);
                        }
                    }
                }
                Pointer ptr = kbDllHookStruct.getPointer();
                long peer = Pointer.nativeValue(ptr);
                return user32Library.CallNextHookEx(hHook, nCode, wParam, new WinDef.LPARAM(peer));
            }
        };

        hHook = user32Library.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0);

        int result;
        WinUser.MSG msg = new WinUser.MSG();
        while ((result = user32Library.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                break;
            } else {
                user32Library.TranslateMessage(msg);
                user32Library.DispatchMessage(msg);
            }
        }
    }

    static void sendKey(int keyCode) {

        input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
        input.input.setType("ki"); // Because setting INPUT_INPUT_KEYBOARD is not enough: https://groups.google.com/d/msg/jna-users/NDBGwC1VZbU/cjYCQ1CjBwAJ
        input.input.ki.wScan = new WinDef.WORD(0);
        input.input.ki.time = new WinDef.DWORD(0);
        input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
// Press
        input.input.ki.wVk = new WinDef.WORD(keyCode); // 0x41
        input.input.ki.dwFlags = new WinDef.DWORD(0);  // keydown

        User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());

// Release
        input.input.ki.wVk = new WinDef.WORD(keyCode); // 0x41
        input.input.ki.dwFlags = new WinDef.DWORD(2);  // keyup

        User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());
    }

    public static void main(String[] args) {
        new ReMapper().reMapOn();
    }
}

现在它就像一个魅力:)