C++;识别X按钮&;滚轮方向 我最近在有限的空闲时间里尝试了一个小项目,尝试用C++获得更多的经验和理解,但是我现在的程序遇到了一个障碍:

C++;识别X按钮&;滚轮方向 我最近在有限的空闲时间里尝试了一个小项目,尝试用C++获得更多的经验和理解,但是我现在的程序遇到了一个障碍:,c++,winapi,mouseevent,mouselistener,setwindowshookex,C++,Winapi,Mouseevent,Mouselistener,Setwindowshookex,我正试图通过使用windows钩子创建一个全局低级鼠标侦听器,这在大多数情况下看起来都相当简单。然而,识别单击了哪个X鼠标按钮(MB4或MB5)和滚动滚轮的滚动方向让我非常头疼 根据微软文档,我目前试图确定的方法是正确的,但我的实现不起作用 我已经找到了一个解决X按钮问题()的有效方法,但当Microsoft代码段更干净并且应该可以工作时,这似乎有点像是跳过了不必要的障碍 虽然C++不是我最熟悉的语言,但我还是想继续学习它,并经常使用它。我希望我只是犯了一个简单的错误,因为这是我第一次使用Win

我正试图通过使用windows钩子创建一个全局低级鼠标侦听器,这在大多数情况下看起来都相当简单。然而,识别单击了哪个X鼠标按钮(MB4或MB5)和滚动滚轮的滚动方向让我非常头疼

根据微软文档,我目前试图确定的方法是正确的,但我的实现不起作用

我已经找到了一个解决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;
}
#包括
#包括
静态LRESULT回调MouseHookProc(int-nCode、WPARAM-WPARAM、LPARAM-LPARAM)
{
如果(nCode>=0)
{
交换机(wParam)
{
案例WM_LBUTTONDOWN:
系统(“CLS”);

标准::cout请阅读文档:

:

[……]

wParam
[in]
键入:
wParam

鼠标的标识符 消息。此参数可以是以下消息之一:
WM\u LBUTTONDOWN
WM\u LBUTTONUP
WM\u MOUSEMOVE
WM\u mouseweel
WM_鼠标滚轮
WM_RBUTTONDOWN
,或
WM_RBUTTONUP


lParam
[in]
键入:
LPARAM

指向
MSLLHOOKSTRUCT
结构。

因此,
wParam
可以是
WM\u lbuttonown
WM\u LBUTTONUP
WM\u MOUSEMOVE
WM\u mouseweel
WM\u mouseheel
,或者
WM\u RBUTTONUP
。没有神奇的方法从中获取更多信息。如果有,它将是未记录的,应该是未记录的避免

lParam
但是指向一个
MSLLHOOKSTRUCT

:

包含有关低级鼠标输入事件的信息

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

mouseData

类型:
DWORD

如果消息为
WM_mouseweel
,则此成员的高位字为 控制盘增量。保留低位字。正值 表示转轮已向前旋转,远离用户;a 负值表示车轮已向后、朝向旋转 用户。一次滚轮点击定义为
滚轮_DELTA
,即120

如果消息为
WM_XBUTTONDOWN
WM_XBUTTONUP
WM_-xbuttondblck
WM_-NCXBUTTONDOWN
WM_-NCXBUTTONUP
,或
WM\u ncxbuttondblck
高阶字指定使用哪个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);
}

Microsoft代码段比较干净,应该可以工作。-代码在哪里?@Swardfish我将其链接到“X按钮单击”单词(和)。您可以对“XBUTTON1和XBUTTON2”执行Ctrl+F组合键如果你愿意的话,我也可以在这里粘贴他们的未格式化代码段。非常感谢你的回答。我正在查看你链接的所有文档,以便更好地了解所有这些是如何工作的。我仍然不明白为什么Microsoft文档会有代码段从wParams中获取附加信息。可能是过时的信息在那里?无论如何,再次感谢!@Charles No,这没有矛盾。
wParam
只是一个名称,并不意味着它在任何地方都有相同的含义。我用简化的代码更新了我的答案。我想这是有意义的。所以在我链接的Microsoft文档中,GET_XBUTTON_wParam和GET_WHEEL_DELTA_wParam方法实际上并不有效从我们在这里使用的同一类型的wParam中匹配它们?此外,您提供的编辑代码没有像原始答案那样包含对X按钮的区分。@Charles您提供的编辑代码没有包含[…]–抱歉,已修复该问题。@Swardfish“要在主线程中接收消息,我必须创建一个窗口”-实际上不是,您可以使用线程消息而不是窗口消息。
GetMessage()
在其HWND参数为空时支持这两种消息。您的处理程序可以使用
PostThreadMessage()
将消息发布到主线程,然后
GetMessage()
可以将该消息返回到
main
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);
}