C ++ Идентификация кнопок X и направления колес прокрутки - PullRequest
0 голосов
/ 25 ноября 2018

Я недавно экспериментировал с небольшим проектом в течение моего ограниченного свободного времени, чтобы попытаться получить больше опыта и понимания с C ++, но я столкнулся с препятствиями в моей текущей программе:

Япытаясь создать глобальный низкоуровневый слушатель мыши с помощью хука окон, что в большинстве случаев кажется довольно простым.Тем не менее, , определяющий, какая кнопка мыши X была нажата (MB4 или MB5) и , в каком направлении вращалось колесо прокрутки , вызывает у меня сильную головную боль.

Согласнов документах Microsoft, текущий способ, которым я пытаюсь определить соответствующую нажатую кнопку X и направление колеса прокрутки , является правильным, но моя реализация этого не работает.

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

Хотя C ++ не мой самый знакомый язык, я хотел бы продолжать изучать его и использовать его чаще.Надеюсь, я просто делаю простую ошибку, так как я впервые работаю с хуками Windows.Заранее благодарим вас за любые советы или помощь, которые кто-либо может предложить!

#include <iostream>
#include <windows.h>

static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) 
{
    if(nCode >= 0)
    {
        switch(wParam)
        {
            case WM_LBUTTONDOWN:
                system("CLS");
                std::cout << "left mouse button down\n";
                break;
            case WM_LBUTTONUP:
                std::cout << "left mouse button up\n";
                break;
            case WM_RBUTTONDOWN:
                system("CLS");
                std::cout << "right mouse button down\n";
                break;
            case WM_RBUTTONUP:
                std::cout << "right mouse button up\n";
                break;
            case WM_MBUTTONDOWN:
                system("CLS");
                std::cout << "middle mouse button down\n";
                break;
            case WM_MBUTTONUP:
                std::cout << "middle mouse button up\n";
                break;
            case WM_MOUSEWHEEL:
                if(GET_WHEEL_DELTA_WPARAM(wParam) > 0)
                    std::cout << "mouse wheel scrolled up\n";
                else if(GET_WHEEL_DELTA_WPARAM(wParam) < 0)
                    std::cout << "mouse wheel scrolled down\n";
                else //always goes here
                    std::cout << "unknown mouse wheel scroll direction\n";
                break;
            case WM_XBUTTONDOWN:
                system("CLS");
                if(GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
                    std::cout << "X1 mouse button down\n";
                else if(GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
                    std::cout << "X2 mouse button down\n";
                else //always goes here
                    std::cout << "unknown X mouse button down\n";
                break;
            case WM_XBUTTONUP:
                if(GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
                    std::cout << "X1 mouse button up\n";
                else if(GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
                    std::cout << "X2 mouse button up\n";
                else //always goes here
                    std::cout << "unknown X mouse button up\n";
                break;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main()
{
    HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
    MSG msg;

    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(mouseHook);
    return 0;
}

1 Ответ

0 голосов
/ 25 ноября 2018

Пожалуйста, ознакомьтесь с документацией:

Функция обратного вызова LowLevelMouseProc :

[...]

wParam [in]Тип: WPARAMИдентификатор сообщения мыши.Этот параметр может быть одним из следующих сообщений:WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN или WM_RBUTTONUP.lParam [в]Тип: LPARAMУказатель на структуру MSLLHOOKSTRUCT.

То есть wParam может быть WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN или WM_RBUTTONUP.Нет никакого волшебного способа получить больше информации из этого.И если бы это было так, это было бы недокументировано и этого следует избегать.

lParam однако указывает на структуру MSLLHOOKSTRUCT:

tagMSLLHOOKSTRUCT :

Содержит информацию о низкоуровневом событии ввода мыши.

typedef struct tagMSLLHOOKSTRUCT {
  POINT     pt;
  DWORD     mouseData;
  DWORD     flags;
  DWORD     time;
  ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;

[...]

mouseDataТип: DWORD

Если сообщение WM_MOUSEWHEEL, старшее слово этого члена - это колесо delta .Слово младшего разряда зарезервировано.Положительное значение указывает на то, что колесо было повернуто вперед от пользователя;отрицательное значение указывает, что колесо было повернуто назад, к пользователю.Один щелчок колеса определяется как WHEEL_DELTA, что равно 120.

Если сообщение WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP или WM_NCXBUTTONDBLCLK, старшее слово указывает, какая кнопка X была нажата или отпущена , а младшее слово зарезервировано.Это значение может быть одним или несколькими из следующих значений.В противном случае mouseData не используется.Значение ЗначениеXBUTTON1 0x0001 Первая кнопка X была нажата или отпущена.XBUTTON2 0x0002 Вторая кнопка X была нажата или отпущена.

Таким образом, упрощенная версия вашего обратного вызова может выглядеть так:

#include <iostream>
#include <type_traits> // std::make_signed_t<>

#include <windows.h>

LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode != HC_ACTION)  // Nothing to do :(
        return CallNextHookEx(NULL, nCode, wParam, lParam);


    MSLLHOOKSTRUCT *info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);

    char const *button_name[] = { "Left", "Right", "Middle", "X" };
    enum { BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, BTN_XBUTTON, BTN_NONE } button = BTN_NONE;

    char const *up_down[] = { "up", "down" };
    bool down = false;


    switch (wParam)
    {

    case WM_LBUTTONDOWN: down = true;
    case WM_LBUTTONUP: button = BTN_LEFT;
        break;
    case WM_RBUTTONDOWN: down = true;
    case WM_RBUTTONUP: button = BTN_RIGHT;
        break;
    case WM_MBUTTONDOWN: down = true;
    case WM_MBUTTONUP: button = BTN_MIDDLE;
        break;
    case WM_XBUTTONDOWN: down = true;
    case WM_XBUTTONUP: button = BTN_XBUTTON;
        break;

    case WM_MOUSEWHEEL:
        // the hi order word might be negative, but WORD is unsigned, so
        // we need some signed type of an appropriate size:
        down = static_cast<std::make_signed_t<WORD>>(HIWORD(info->mouseData)) < 0;
        std::cout << "Mouse wheel scrolled " << up_down[down] << '\n';
        break;
    }

    if (button != BTN_NONE) {
        std::cout << button_name[button];
        if (button == BTN_XBUTTON)
            std::cout << HIWORD(info->mouseData);
        std::cout << " mouse button " << up_down[down] << '\n';
    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

Относительно вашего main():

Поскольку в вашем приложении нет окон, нетсообщения будут отправлены на него и GetMessage() никогда не вернется.Это делает сообщение бесполезным.Одного вызова на GetMessage() достаточно, чтобы дать Windows возможность вызвать установленный обратный вызов.Однако проблема в том, что код после вызова GetMessage() никогда не будет выполнен, потому что единственные способы завершить программу - закрыть окно или нажать Ctrl + C .

Чтобы убедиться, что UnhookWindowsHookEx() вызван, я бы предложил установить ConsoleCtrlHandler:

HHOOK hook = NULL;

BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
{
    if (hook) {
        std::cout << "Unhooking " << hook << '\n';
        UnhookWindowsHookEx(hook);
        hook = NULL;  // ctrl_handler might be called multiple times
        std::cout << "Bye :(";
        std::cin.get();  // gives the user 5 seconds to read our last output
    }

    return TRUE;
}

int main()
{
    SetConsoleCtrlHandler(ctrl_handler, TRUE);
    hook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, nullptr, 0);

    if (!hook) {
        std::cerr << "SetWindowsHookExW() failed. Bye :(\n\n";
        return EXIT_FAILURE;
    }

    std::cout << "Hook set: " << hook << '\n';
    GetMessageW(nullptr, nullptr, 0, 0);
}
...