在使用我自己的鼠标有问题时我的gpw1代出现了回滚的问题

python的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pynput.mouse import Listener

def on_scroll(x, y, dx, dy):
if dy > 0:
print("up")
# else:
# print("向下滚动")

def on_move(x, y):
pass

def on_click(x, y, button, pressed):
pass

def on_release(x, y, button):
pass

with Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll) as listener:
listener.join()

这个代码简单好理解

c++

  1. 首先我们当然是需要借助windowsapi

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    #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;
    }

    最佳答案

    请阅读文档:

    LowLevelMouseProc callback function :

    […]

    wParam [in]
    Type: WPARAM
    The identifier of the mouse message. This parameter can be one of the following messages:
    WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP.

    lParam [in]
    Type: LPARAM
    A pointer to an MSLLHOOKSTRUCT structure.

    所以 wParam 可以是 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEELWM_RBUTTONDOWNWM_RBUTTONUP。没有神奇的方法可以从中获取更多信息。即使有,也不会记录在案,应该避免。

    lParam 但是指向一个 MSLLHOOKSTRUCT:

    tagMSLLHOOKSTRUCT structure :

    Contains information about a low-level mouse input event.

    1
    2
    3
    4
    5
    6
    7
    typedef struct tagMSLLHOOKSTRUCT {
    POINT pt;
    DWORD mouseData;
    DWORD flags;
    DWORD time;
    ULONG_PTR dwExtraInfo;
    } MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;

    […]

    mouseData
    Type: DWORD

    If the message is WM_MOUSEWHEEL, the high-order word of this member is the wheel delta. The low-order word is reserved. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user. One wheel click is defined as WHEEL_DELTA, which is 120.

    If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released, and the low-order word is reserved. This value can be one or more of the following values. Otherwise, mouseData is not used.

    Value Meaning
    XBUTTON1 0x0001 The first X button was pressed or released.
    XBUTTON2 0x0002 The second X button was pressed or released.

    因此您的回调的简化版本可能如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #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 有机会调用已安装的 Hook 回调。但问题是,调用 GetMessage() 之后的代码将永远不会执行,因为结束程序的唯一方法是关闭窗口或按 Ctrl + C.

    为了确保 UnhookWindowsHookEx() 被调用,我建议设置一个 ConsoleCtrlHandler:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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);
    }
  2. 使用qt对滚动事件的监听

c sharp

  1. 使用windows api

  2. 使用scroller view对滚动事件监听

    使用 XAML 声明方式:
    在 XAML 中,你可以为滚动控件(例如 ScrollViewer)的相应事件添加事件处理程序。以下是一个示例,演示了如何在 XAML 中声明并处理 ScrollViewer 的滚动事件:

    1
    2
    3
    <ScrollViewer ScrollChanged="ScrollViewer_ScrollChanged">
    <!-- 内容 -->
    </ScrollViewer>

    在代码中定义了名为 ScrollViewer_ScrollChanged 的事件处理程序:

    1
    2
    3
    4
    private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
    // 处理滚动事件
    }在该事件处理程序中,你可以编写代码来处理滚动事件,根据需要执行相应的逻辑。

    使用代码绑定方式:
    使用代码绑定的方式,你可以在代码中订阅滚动事件。以下是一个示例,演示了如何使用代码绑定方式监听 ScrollViewer 的滚动事件:

    1
    2
    3
    4
    5
    6
    ScrollViewer scrollViewer = new ScrollViewer();
    scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
    private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
    // 处理滚动事件
    }

    在这个示例中,我们创建了一个 ScrollViewer 实例,并将事件处理程序 ScrollViewer_ScrollChanged 绑定到 ScrollChanged 事件上。当滚动事件发生时,事件处理程序将被调用,你可以在其中编写逻辑来处理滚动事件。

    无论是 XAML 声明方式还是代码绑定方式,你都需要根据你的实际需求,选择适合的方式来监听滚动事件,并在事件处理程序中编写相应的逻辑。

  3. 在 WPF 中,你可以通过以下方式来监听滚动事件:

    在全局鼠标事件中在我们使用的

    1
    2
    3
    4
    5
    LRESULT CALLBACK LowLevelMouseProc(
    _In_ int nCode,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    );

    我们需要在知道每个参数的意义

    参数 wParam 表示鼠标事件类型。几个主要的数值如下表所示:

    鼠标移动 0x200
    鼠标左键按下 0x201
    鼠标左键抬起 0x202
    鼠标右键按下 0x204
    鼠标右键抬起 0x205
    鼠标滚轮滚动 0x20a
    鼠标侧键按下 0x20b
    鼠标侧健抬起 0x20c
    鼠标水平滚轮滚动 0x20e

    参数 lParam 同样是事件详细信息结构体的地址,该结构体的原型如下所示:

    1
    2
    3
    4
    5
    6
    7
    typedef struct tagMSLLHOOKSTRUCT {
    POINT pt;
    DWORD mouseData;
    DWORD flags;
    DWORD time;
    ULONG_PTR dwExtraInfo;
    } MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;

    成员 pt 表示事件的坐标值结构体,其原型如下所示:

    1
    2
    3
    4
    typedef struct tagPOINT {
    LONG x;
    LONG y;
    } POINT, *PPOINT;

    需要注意的是,虽然它被声明为 ‘LONG’ 类型,但它实际上只有4个字节长度。另外,如果你对结构体的了解足够深刻,一定能理解在实际开发中直接用一个 long 型来替代 POINT 类型是完全可行的,只需要知道Windows桌面编程使用的是小端序就可以了,当然,如果你理解不了这句话,那老老实实再创建一个POINT结构体来套进去就是了。

    成员mouseData 不太需要关注。当事件是滚轮滚动时,它的高16位记录的是滚动方向及距离。正值表示远离用户的滚动,负值表示靠近用户的滚动,其数值恒定为120,可以理解为表示一格滚动。

    成员 flags 不需要理会。

    成员 time 表示事件发生时间,单位为毫秒,自系统启动以来的相对时间值。

    成员 dwExtraInfo 不需要理会。

    同时键盘的回调函数也是需要注意的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    using System;
    using System.Runtime.InteropServices;
    using System.Windows;


    namespace KMHook
    {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
    public MainWindow()
    {
    InitializeComponent();
    }

    internal struct Keyboard_LL_Hook_Data
    {
    public UInt32 vkCode;
    public UInt32 scanCode;
    public UInt32 flags;
    public UInt32 time;
    public IntPtr extraInfo;
    }

    internal struct Mouse_LL_Hook_Data
    {
    internal long yx;
    internal readonly int mouseData;
    internal readonly uint flags;
    internal readonly uint time;
    internal readonly IntPtr dwExtraInfo;
    }

    private static IntPtr pKeyboardHook = IntPtr.Zero;
    private static IntPtr pMouseHook = IntPtr.Zero;
    //钩子委托声明
    public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
    private static HookProc keyboardHookProc;
    private static HookProc mouseHookProc;

    //安装钩子
    [DllImport("user32.dll")]
    public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr pInstance, int threadID);
    //卸载钩子
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
    [DllImport("user32.dll")]
    public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); //parameter 'hhk' is ignored.

    private static int keyboardHookCallback(int code, IntPtr wParam, IntPtr lParam)
    {
    if (code < 0)
    {
    return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

    Keyboard_LL_Hook_Data khd = (Keyboard_LL_Hook_Data)Marshal.PtrToStructure(lParam, typeof(Keyboard_LL_Hook_Data));
    System.Diagnostics.Debug.WriteLine($"key event:{wParam}, key code:{khd.vkCode}, event time:{khd.time}");

    return 0;
    }

    private static int mouseHookCallback(int code, IntPtr wParam, IntPtr lParam)
    {
    if (code < 0)
    {
    return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

    Mouse_LL_Hook_Data mhd = (Mouse_LL_Hook_Data)Marshal.PtrToStructure(lParam, typeof(Mouse_LL_Hook_Data));
    System.Diagnostics.Debug.WriteLine($"mouse event:{wParam}, ({mhd.yx & 0xffffffff},{mhd.yx >> 32})");

    return 0;
    }

    internal static bool InsertHook()
    {
    bool iRet;
    iRet = InsertKeyboardHook();
    if (!iRet)
    {
    return false;
    }

    iRet = InsertMouseHook();
    if (!iRet)
    {
    removeKeyboardHook();
    return false;
    }

    return true;
    }

    //安装钩子方法
    private static bool InsertKeyboardHook()
    {
    if (pKeyboardHook == IntPtr.Zero)//不存在钩子时
    {
    //创建钩子
    keyboardHookProc = keyboardHookCallback;
    pKeyboardHook = SetWindowsHookEx(13, //13表示全局键盘事件。
    keyboardHookProc,
    (IntPtr)0,
    0);

    if (pKeyboardHook == IntPtr.Zero)//如果安装钩子失败
    {
    removeKeyboardHook();
    return false;
    }
    }

    return true;
    }

    private static bool InsertMouseHook()
    {
    if (pMouseHook == IntPtr.Zero)
    {
    mouseHookProc = mouseHookCallback;
    pMouseHook = SetWindowsHookEx(14, //14表示全局鼠标事件
    mouseHookProc,
    (IntPtr)0,
    0);

    if (pMouseHook == IntPtr.Zero)
    {
    removeMouseHook();
    return false;
    }
    }

    return true;
    }

    internal static bool RemoveHook()
    {
    bool iRet;
    iRet = removeKeyboardHook();
    if (iRet)
    {
    iRet = removeMouseHook();
    }

    return iRet;
    }

    private static bool removeKeyboardHook()
    {
    if (pKeyboardHook != IntPtr.Zero)
    {
    if (UnhookWindowsHookEx(pKeyboardHook))
    {
    pKeyboardHook = IntPtr.Zero;
    }
    else
    {
    return false;
    }
    }

    return true;
    }

    private static bool removeMouseHook()
    {
    if (pMouseHook != IntPtr.Zero)
    {
    if (UnhookWindowsHookEx(pMouseHook))
    {
    pMouseHook = IntPtr.Zero;
    }
    else
    {
    return false;
    }
    }

    return true;
    }

    private void Button_Install_Click(object sender, RoutedEventArgs e)
    {
    InsertHook();
    }

    private void Button_Remove_Click(object sender, RoutedEventArgs e)
    {
    RemoveHook();
    }
    }
    }

参考

C++ 识别 X 按钮和滚轮方向 - IT工具网 (coder.work)

windows桌面编程–监听全局键盘鼠标事件 - 大窟窿 - 博客园 (cnblogs.com)