Почему обратный вызов клавиатуры работает дважды и выполняет двойную печать клавиш (Java/JNA)?

Решено. Смотрите мой ответ ниже.


В моем коде я пытаюсь переназначить клавиши клавиатуры, например. z->s. Я использую библиотеку JNA 5.6.0 и jna-platform 5.6.0, но люди, знакомые с языками C, тоже меня поймут, потому что JNA использует функции WinAPI из dll User32 и Kernel32.

Моя проблема в том, что новый ключ печатается дважды, а между новыми ключами появляется старый ключ. Когда я набираю «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();
    }
}

Мой пример печати:

Может ли кто-нибудь помочь мне понять, почему это происходит?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
104
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Решено путем изменения возвращаемого значения функции собственного callback(). После переназначения oldKey на newKey мы должны явно вернуть 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();
    }
}

Теперь это работает как шарм :)

Другие вопросы по теме