Как назначить несколько горячих клавиш низкого уровня с помощью SetWindowsHookExA и LowLevelKeyboardProc - PullRequest
0 голосов
/ 01 апреля 2020

Я пытаюсь написать класс для LowLevel Global HotKeys. Идея состоит в том, что один экземпляр этого класса представляет последовательность клавиш HotKey или HotKey, например Alt+SHift+G, и это работает. Когда я создаю сейчас второй экземпляр этого класса для второго HotKey, который перезаписывает мой первый, может быть запущен только второй или последний. Любая идея, как я мог бы расширить это для работы с большим количеством экземпляров? Могу ли я сделать LowLevelKeyboardPro c не статичным c методом для моего класса? Возможно, это решило бы мою проблему.

WinKeyHandler.h:

class WinKeyHandler : public AbstractKeyHandler
{
public:
    WinKeyHandler();
    ~WinKeyHandler() override;

    bool registerKey(const QKeySequence &keySequence) override;
    bool isHotkeyTriggered(void* message) override;

private:
    KeySequenceToWinKeyCodeTranslator mKeyCodeMapper;
    static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
    HHOOK mLowLevelKeyboardHook;

    QVector<DWORD> mPressedKeys;
    unsigned int mPressedModifiers;
    unsigned int mPressedKey;
    bool mTriggered;
    KeyCodeCombo mKeySequence;

    DWORD translateVkCode(DWORD vkcode);
    void handleKeySequence();
    void handleKeyPress(DWORD vkCode);
    void handleKeyRelease(DWORD vkCode);
    void resetKeys();
};

WinKeyHandler. cpp:

WinKeyHandler * mWinKeyHandlerReference;

WinKeyHandler::WinKeyHandler()
{
    resetKeys();
    mWinKeyHandlerReference = this;
}

WinKeyHandler::~WinKeyHandler()
{
    UnhookWindowsHookEx(mLowLevelKeyboardHook);
}

bool WinKeyHandler::registerKey(const QKeySequence &keySequence)
{
    mKeySequence = mKeyCodeMapper.map(keySequence);
    mLowLevelKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, nullptr, 0);

    return mLowLevelKeyboardHook != nullptr;
}

bool WinKeyHandler::isHotkeyTriggered(void* message)
{
    Q_UNUSED(message)

    return false;
}

LRESULT CALLBACK WinKeyHandler::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        auto vkCode = mWinKeyHandlerReference->translateVkCode(PKBDLLHOOKSTRUCT(lParam)->vkCode);
        switch (wParam)
        {
            case WM_KEYDOWN:
            case WM_SYSKEYDOWN:
                    mWinKeyHandlerReference->handleKeyPress(vkCode);
                break;
            case WM_KEYUP:
                mWinKeyHandlerReference->handleKeyRelease(vkCode);
                break;
        }

        mWinKeyHandlerReference->handleKeySequence();
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}

DWORD WinKeyHandler::translateVkCode(DWORD vkCode)
{
    if(vkCode == VK_LCONTROL || vkCode == VK_RCONTROL) {
        return VK_CONTROL;
    } else if(vkCode == VK_LSHIFT || vkCode == VK_RSHIFT) {
        return VK_SHIFT;
    } else if(vkCode == VK_LMENU || vkCode == VK_RMENU) {
        return VK_MENU;
    } else {
        return vkCode;
    }
}

void WinKeyHandler::handleKeySequence()
{
    if(mKeySequence.modifier == mPressedModifiers && mKeySequence.key == mPressedKey) {
        if(!mTriggered) {
            emit triggered();
            resetKeys();
        }
        mTriggered = true;
    } else {
        mTriggered = false;
    }
}

void WinKeyHandler::handleKeyPress(DWORD vkCode)
{
    if(!mPressedKeys.contains(vkCode)) {
        mPressedKeys.append(vkCode);

        if(vkCode == VK_CONTROL || vkCode == VK_MENU || vkCode == VK_SHIFT) {
            mPressedModifiers |= vkCode;
        } else {
            mPressedKey = vkCode;
        }
    }
}

void WinKeyHandler::handleKeyRelease(DWORD vkCode)
{
    if(mPressedKeys.contains(vkCode)) {
        mPressedKeys.removeOne(vkCode);

        if(vkCode == VK_CONTROL || vkCode == VK_MENU || vkCode == VK_SHIFT) {
            mPressedModifiers ^= vkCode;
        } else {
            mPressedKey = 0;
        }
    }
}

void WinKeyHandler::resetKeys()
{
    mPressedKey = 0;
    mPressedModifiers = 0;
}

1 Ответ

0 голосов
/ 01 апреля 2020

Можно зарегистрировать несколько хуков одновременно, они будут соединены вместе (поэтому каждый хук должен вызывать CallNextHookEx(), поэтому вызывается следующий хук в цепочке).

Ваша проблема в том, не потому, что у вас зарегистрировано несколько хуков, а потому, что вы используете глобальную переменную mWinKeyHandlerReference, которая может ссылаться только на один экземпляр класса за раз. Он настроен на обращение к последнему созданному экземпляру, что означает, что все хуки отправляют свои события этому одному экземпляру.

Нет, вы не можете напрямую использовать non-stati c метод класса как процедура ловушки, из-за неявного параметра this, который понадобится, который ловушка не будет знать, как передать.

Что вы можете do вместо этого имеет класс create thunk - блок из исполняемого файла памяти, выделяемый динамически через VirtualAlloc() с правами PAGE_EXECUTE, содержащими только достаточное количество инструкций ЦП для пересылки его входных параметров в нестатический * Метод 1030 * с использованием указателя this объекта - и затем вы можете использовать этот блок в качестве процедуры подключения. Это позволит вам создавать процедуры перехвата для каждого экземпляра, использующие отдельные указатели this.

См. Обратный вызов функций-членов через библиотеку сторонних производителей и Thunking в Win32: Упрощение обратных вызовов в не-статус c Функции-члены .

...