☰
C++鼠标助手代码(点击,输入,滑动,翻页)
2026/11/1 1:44:19 网站建设 项目流程

该程序为一个基于Windows API的鼠标与键盘自动化辅助工具,支持通过快捷键(如Ctrl+F1~F8)实现自动左/右键点击、滚轮滚动、按W键、空格键连按及鼠标左右缓慢移动。界面透明可调,实时显示状态,使用SendInput模拟输入,具备高精度定时控制,适用于游戏或自动化操作场景。

使用方式:拷贝程序代码,c++编译后即可运行,可以快速点击,智能翻页,也可玩游戏

#ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #define NOMINMAX #include <windows.h> #include <mmsystem.h> // timeBeginPeriod / timeEndPeriod #include <algorithm> #include <sstream> #include <string> #include <cwchar> #pragma comment(lib, "winmm.lib") #ifndef MOD_NOREPEAT #define MOD_NOREPEAT 0x4000 #endif #define TICK_MS 20 #define CLICK_INTERVAL_MS 0 // 左/右键连点间隔 #define SCROLL_INTERVAL_MS 10 // 滚轮间隔 #define KEY_INTERVAL_MS 50 // 空格连按间隔 #define MOVE_SPEED_PX_S 3000 #define WIN_W 440 #define WIN_H 270 #define WIN_ALPHA 120 // 数值越小越透明(0~255) #define HOTKEY_CLICK_L 1 #define HOTKEY_CLICK_R 2 #define HOTKEY_SCROLL 3 #define HOTKEY_KEY_W 4 #define HOTKEY_KEY_SPACE 5 #define HOTKEY_MOVE_R 6 #define HOTKEY_MOVE_L 7 #define HOTKEY_STOP 8 #define TIMER_TICK 1 HWND g_hwnd = nullptr; bool g_autoL = false; // 自动左键 bool g_autoR = false; // 自动右键 bool g_autoScroll = false; // 自动滚轮 bool g_autoW = false; // 自动按 W(按住) bool g_autoSpace = false; // 自动按空格 bool g_moveRight = false; bool g_moveLeft = false; int g_accumL = 0; int g_accumR = 0; int g_accumScroll = 0; int g_accumSpace = 0; int g_moveSpeed = MOVE_SPEED_PX_S; wchar_t g_status[1024] = L""; /* ---------------- 输入模拟 ---------------- */ void SendLeftClick() { INPUT in[2] = {}; in[0].type = INPUT_MOUSE; in[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; in[1].type = INPUT_MOUSE; in[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; SendInput(2, in, sizeof(INPUT)); } void SendRightClick() { INPUT in[2] = {}; in[0].type = INPUT_MOUSE; in[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; in[1].type = INPUT_MOUSE; in[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; SendInput(2, in, sizeof(INPUT)); } void SendWheel() { INPUT in = {}; in.type = INPUT_MOUSE; in.mi.dwFlags = MOUSEEVENTF_WHEEL; in.mi.mouseData = (DWORD)(-WHEEL_DELTA); // 负值=向下滚;改成 WHEEL_DELTA 就是向上 SendInput(1, &in, sizeof(INPUT)); } // 使用硬件扫描码发送按键,可能穿透部分 DirectInput 程序 void SendKeyTap(WORD vk) { WORD scan = (WORD)MapVirtualKeyW(vk, MAPVK_VK_TO_VSC); INPUT in[2] = {}; in[0].type = INPUT_KEYBOARD; in[0].ki.wVk = 0; in[0].ki.wScan = scan; in[0].ki.dwFlags = KEYEVENTF_SCANCODE; in[1].type = INPUT_KEYBOARD; in[1].ki.wVk = 0; in[1].ki.wScan = scan; in[1].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; SendInput(2, in, sizeof(INPUT)); } /* W 键采用"按住"方式,开关切换时按下/抬起 */ bool g_wHeld = false; void SetWHeld(bool held) { if (held == g_wHeld) return; WORD scan = (WORD)MapVirtualKeyW(0x57, MAPVK_VK_TO_VSC); // 'W' INPUT in = {}; in.type = INPUT_KEYBOARD; in.ki.wVk = 0; in.ki.wScan = scan; in.ki.dwFlags = KEYEVENTF_SCANCODE | (held ? 0 : KEYEVENTF_KEYUP); SendInput(1, &in, sizeof(INPUT)); g_wHeld = held; } void SendMove(int dx) { INPUT in = {}; in.type = INPUT_MOUSE; in.mi.dx = dx; in.mi.dy = 0; in.mi.dwFlags = MOUSEEVENTF_MOVE; SendInput(1, &in, sizeof(INPUT)); } /* ---------------- 状态文本 ---------------- */ void UpdateStatus() { std::wstringstream ss; ss << L"鼠标助手\n" << L"----------------------------------------\n" << L"Ctrl+F1 自动左键点击 " << (g_autoL ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F2 自动右键点击 " << (g_autoR ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F3 自动滚轮滚动 " << (g_autoScroll ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F4 自动按 W 键 " << (g_autoW ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F5 自动按空格 " << (g_autoSpace ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F6 向右缓慢滑动 " << (g_moveRight ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F7 向左缓慢滑动 " << (g_moveLeft ? L"[开]" : L"[关]") << L"\n" << L"Ctrl+F8 全部停止\n" << L"----------------------------------------\n" << L"双击本窗口退出"; std::wstring s = ss.str(); if (s.size() > 1023) s.resize(1023); std::wmemcpy(g_status, s.c_str(), s.size()); g_status[s.size()] = 0; if (g_hwnd) InvalidateRect(g_hwnd, nullptr, TRUE); } /* ---------------- 窗口过程 ---------------- */ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_HOTKEY: switch (wp) { case HOTKEY_CLICK_L: g_autoL = !g_autoL; g_accumL = 0; break; case HOTKEY_CLICK_R: g_autoR = !g_autoR; g_accumR = 0; break; case HOTKEY_SCROLL: g_autoScroll = !g_autoScroll; g_accumScroll = 0; break; case HOTKEY_KEY_W: g_autoW = !g_autoW; SetWHeld(g_autoW); break; case HOTKEY_KEY_SPACE: g_autoSpace = !g_autoSpace; g_accumSpace = 0; break; case HOTKEY_MOVE_R: g_moveRight = !g_moveRight; if (g_moveRight) g_moveLeft = false; break; case HOTKEY_MOVE_L: g_moveLeft = !g_moveLeft; if (g_moveLeft) g_moveRight = false; break; case HOTKEY_STOP: g_autoL = g_autoR = g_autoScroll = g_autoSpace = false; g_moveRight = g_moveLeft = false; g_autoW = false; SetWHeld(false); g_accumL = g_accumR = g_accumScroll = g_accumSpace = 0; break; } UpdateStatus(); return 0; case WM_TIMER: if (wp == TIMER_TICK) { if (g_autoL) { g_accumL += TICK_MS; if (g_accumL >= CLICK_INTERVAL_MS) { g_accumL = 0; SendLeftClick(); } } if (g_autoR) { g_accumR += TICK_MS; if (g_accumR >= CLICK_INTERVAL_MS) { g_accumR = 0; SendRightClick(); } } if (g_autoScroll) { g_accumScroll += TICK_MS; if (g_accumScroll >= SCROLL_INTERVAL_MS) { g_accumScroll = 0; SendWheel(); } } if (g_autoSpace) { g_accumSpace += TICK_MS; if (g_accumSpace >= KEY_INTERVAL_MS) { g_accumSpace = 0; SendKeyTap(VK_SPACE); } } if (g_moveRight || g_moveLeft) { int step = g_moveSpeed * TICK_MS / 1000; if (step < 1) step = 1; if (g_moveLeft) step = -step; POINT p; GetCursorPos(&p); int leftEdge = GetSystemMetrics(SM_XVIRTUALSCREEN); int virtWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); int rightEdge = leftEdge + virtWidth; int nx = p.x + step; if (nx < leftEdge || nx >= rightEdge) { g_moveRight = g_moveLeft = false; UpdateStatus(); } else { SendMove(step); } } } return 0; case WM_NCLBUTTONDBLCLK: // 双击窗口任意位置退出 DestroyWindow(hwnd); return 0; case WM_ERASEBKGND: return 1; case WM_NCHITTEST: return HTCAPTION; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); RECT rc; GetClientRect(hwnd, &rc); HBRUSH bg = CreateSolidBrush(RGB(20, 22, 28)); FillRect(hdc, &rc, bg); DeleteObject(bg); HPEN pen = CreatePen(PS_SOLID, 1, RGB(0, 220, 130)); HGDIOBJ oldPen = SelectObject(hdc, pen); HGDIOBJ oldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH)); Rectangle(hdc, 0, 0, rc.right, rc.bottom); SelectObject(hdc, oldPen); SelectObject(hdc, oldBrush); DeleteObject(pen); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, RGB(0, 235, 150)); RECT tr = rc; tr.left += 16; tr.top += 10; tr.right -= 16; tr.bottom -= 10; DrawTextW(hdc, g_status, -1, &tr, DT_LEFT | DT_TOP | DT_NOPREFIX); EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: SetWHeld(false); // 退出前务必松开 W,避免卡键 KillTimer(hwnd, TIMER_TICK); UnregisterHotKey(hwnd, HOTKEY_CLICK_L); UnregisterHotKey(hwnd, HOTKEY_CLICK_R); UnregisterHotKey(hwnd, HOTKEY_SCROLL); UnregisterHotKey(hwnd, HOTKEY_KEY_W); UnregisterHotKey(hwnd, HOTKEY_KEY_SPACE); UnregisterHotKey(hwnd, HOTKEY_MOVE_R); UnregisterHotKey(hwnd, HOTKEY_MOVE_L); UnregisterHotKey(hwnd, HOTKEY_STOP); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } /* ---------------- 入口 ---------------- */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) { // 提高定时器精度,让连点间隔更准 timeBeginPeriod(1); // 建议:REALTIME_PRIORITY_CLASS 可能导致系统卡顿,推荐改为 HIGH_PRIORITY_CLASS // 如果你确实需要最高优先级,可以保留下面两行,但请自行承担风险。 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); WNDCLASSW wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = L"MouseHelperWndClass"; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = nullptr; if (!RegisterClassW(&wc)) return 0; int x = GetSystemMetrics(SM_CXSCREEN) - WIN_W - 30; int y = 30; g_hwnd = CreateWindowExW( WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, L"MouseHelperWndClass", L"鼠标助手", WS_POPUP, x, y, WIN_W, WIN_H, nullptr, nullptr, hInstance, nullptr); if (!g_hwnd) return 0; SetLayeredWindowAttributes(g_hwnd, 0, WIN_ALPHA, LWA_ALPHA); RegisterHotKey(g_hwnd, HOTKEY_CLICK_L, MOD_CONTROL | MOD_NOREPEAT, VK_F1); RegisterHotKey(g_hwnd, HOTKEY_CLICK_R, MOD_CONTROL | MOD_NOREPEAT, VK_F2); RegisterHotKey(g_hwnd, HOTKEY_SCROLL, MOD_CONTROL | MOD_NOREPEAT, VK_F3); RegisterHotKey(g_hwnd, HOTKEY_KEY_W, MOD_CONTROL | MOD_NOREPEAT, VK_F4); RegisterHotKey(g_hwnd, HOTKEY_KEY_SPACE, MOD_CONTROL | MOD_NOREPEAT, VK_F5); RegisterHotKey(g_hwnd, HOTKEY_MOVE_R, MOD_CONTROL | MOD_NOREPEAT, VK_F6); RegisterHotKey(g_hwnd, HOTKEY_MOVE_L, MOD_CONTROL | MOD_NOREPEAT, VK_F7); RegisterHotKey(g_hwnd, HOTKEY_STOP, MOD_CONTROL | MOD_NOREPEAT, VK_F8); UpdateStatus(); ShowWindow(g_hwnd, SW_SHOW); UpdateWindow(g_hwnd); SetTimer(g_hwnd, TIMER_TICK, TICK_MS, nullptr); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } timeEndPeriod(1); return 0; }

一、程序功能概览

这个工具的核心目标很明确:把日常重复的鼠标点击、滚轮滚动和键盘按键交给程序自动完成,同时保持界面轻量、透明、不抢焦点。它基于 Windows 原生 API 实现,不依赖任何第三方库,编译后就是一个独立的小程序。

主要能力包括以下几项:

  • 自动左键点击:按Ctrl+F1开关,适合需要高频点击的场景。
  • 自动右键点击:按Ctrl+F2开关,可用于菜单刷新、右键连点等操作。
  • 自动滚轮滚动:按Ctrl+F3开关,默认向下滚动,适合长页面翻页。
  • 自动按 W 键:按Ctrl+F4开关,采用“按住”方式,而不是反复点按。
  • 自动按空格:按Ctrl+F5开关,按固定间隔连按。
  • 鼠标缓慢左右移动:按Ctrl+F6向右、Ctrl+F7向左,速度可调。
  • 一键全部停止:按Ctrl+F8立即关闭所有自动化功能。

窗口本身是置顶、半透明、无边框的,并且设置了WS_EX_NOACTIVATE,因此点击它不会抢走当前前台程序的焦点,这一点在游戏或全屏应用中尤其重要。

二、环境准备与编译方式

程序只依赖 Windows SDK 自带的头文件和库,使用 Visual Studio 或 MinGW 都可以编译。

2.1 使用 Visual Studio 编译

  1. 新建一个“Windows 桌面应用程序”或空的 C++ 项目。
  2. 把代码保存为main.cpp,添加到项目中。
  3. 项目属性中确认字符集为“使用 Unicode 字符集”。
  4. 链接器会自动处理#pragma comment(lib, "winmm.lib"),无需手动添加。
  5. 选择 Release / x64 编译,生成可执行文件。

2.2 使用 MinGW 编译

g++ main.cpp -o MouseHelper.exe -municode -lwinmm -static -O2

其中-municode用于支持WinMain的宽字符入口,-lwinmm对应timeBeginPeriod所在的库。

提示:如果编译时报错找不到WinMain,请检查是否使用了 Unicode 入口,或者把int WINAPI WinMain改为int WINAPI wWinMain并配合-municode。

三、核心实现原理

3.1 用 SendInput 模拟输入

所有鼠标和键盘事件都通过SendInput注入到系统输入流中。相比mouse_event和keybd_event,SendInput是更现代、更可靠的接口,可以一次性提交一组事件,保证按下和抬起之间的原子性。

例如左键点击的实现,就是把MOUSEEVENTF_LEFTDOWN和MOUSEEVENTF_LEFTUP两个事件打包后一次发送:

void SendLeftClick() { INPUT in[2] = {}; in[0].type = INPUT_MOUSE; in[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; in[1].type = INPUT_MOUSE; in[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; SendInput(2, in, sizeof(INPUT)); }

3.2 用扫描码发送按键

键盘部分没有直接使用虚拟键码,而是通过MapVirtualKeyW把虚拟键转换成硬件扫描码,再配合KEYEVENTF_SCANCODE发送。这样做的好处是兼容性更好,部分使用 DirectInput 的程序只认扫描码,不认虚拟键码。

void SendKeyTap(WORD vk) { WORD scan = (WORD)MapVirtualKeyW(vk, MAPVK_VK_TO_VSC); INPUT in[2] = {}; in[0].type = INPUT_KEYBOARD; in[0].ki.wScan = scan; in[0].ki.dwFlags = KEYEVENTF_SCANCODE; in[1].type = INPUT_KEYBOARD; in[1].ki.wScan = scan; in[1].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; SendInput(2, in, sizeof(INPUT)); }

3.3 定时器驱动的累加式调度

程序只注册了一个 20ms 的系统定时器TIMER_TICK,所有自动化动作都在这一个定时器里按累加时间判断是否触发。这种设计避免了为每个功能单独开线程或定时器,逻辑集中、开销小。

case WM_TIMER: if (wp == TIMER_TICK) { if (g_autoL) { g_accumL += TICK_MS; if (g_accumL >= CLICK_INTERVAL_MS) { g_accumL = 0; SendLeftClick(); } } // 其余功能同理…… } return 0;

每个功能维护一个累加器,累加到设定间隔就执行一次并清零。这样即使定时器本身有抖动,长期平均间隔也能保持稳定。

3.4 提高定时精度

Windows 默认的定时器精度大约在 15.6ms 左右,对于高频连点来说不够用。程序在入口处调用了timeBeginPeriod(1),把系统定时器精度提升到 1ms,退出时再调用timeEndPeriod(1)恢复。

timeBeginPeriod(1); // ... 消息循环 ... timeEndPeriod(1);

四、界面与交互设计

4.1 透明置顶窗口

窗口创建时使用了几个关键扩展样式:

  • WS_EX_TOPMOST:始终置顶,不被其他窗口遮挡。
  • WS_EX_LAYERED:支持整体透明度,通过SetLayeredWindowAttributes设置。
  • WS_EX_TOOLWINDOW:不在任务栏显示,减少干扰。
  • WS_EX_NOACTIVATE:点击窗口不激活,不抢前台焦点。

透明度由宏WIN_ALPHA控制,取值 0~255,数值越小越透明,默认 120 是一个比较适中的半透明效果。

4.2 自绘界面

窗口没有使用任何控件,全部内容在WM_PAINT中自绘:先填充深色背景,再画一圈绿色边框,最后用DrawTextW输出状态文本。状态文本由UpdateStatus统一生成,每次开关变化后调用InvalidateRect触发重绘。

4.3 拖动与退出

在WM_NCHITTEST中直接返回HTCAPTION,让整个窗口都可以拖动;在WM_NCLBUTTONDBLCLK中处理双击退出。这样既省去了标题栏,又保留了基本的窗口操作体验。

五、参数调优建议

代码顶部的几个宏决定了实际手感,可以根据使用场景调整:

宏名称默认值作用调优建议
TICK_MS20定时器周期不建议低于 10,否则 CPU 占用上升
CLICK_INTERVAL_MS0左右键连点间隔0 表示每个 tick 都点,实际约 20ms 一次
SCROLL_INTERVAL_MS10滚轮间隔翻页太快可调到 30~50
KEY_INTERVAL_MS50空格连按间隔根据目标程序响应速度调整
MOVE_SPEED_PX_S3000鼠标移动速度(像素/秒)想更慢可降到 500~1000
WIN_ALPHA120窗口透明度0~255,越小越透明

需要注意的是,CLICK_INTERVAL_MS设为 0 时,实际点击频率受定时器周期限制,约为每秒 50 次。如果目标程序对点击频率敏感,建议适当增大该值。

六、注意事项与风险提示

  • 进程优先级:代码中使用了REALTIME_PRIORITY_CLASS,这是最高优先级,可能导致系统响应变慢甚至卡顿。除非确有需要,建议改为HIGH_PRIORITY_CLASS。
  • W 键卡键风险:W 键采用按住方式,程序在WM_DESTROY中会主动松开,但如果进程被强制结束,仍可能出现卡键。建议保留Ctrl+F8作为应急停止。
  • 权限问题:如果目标程序以管理员权限运行,本工具也需要以管理员身份启动,否则SendInput可能被 UIPI 拦截。
  • 合规使用:自动化工具应仅用于提升个人工作效率或测试场景,请勿用于违反游戏用户协议、破坏公平性或其他违规用途。
  • 杀毒软件误报:模拟输入的 API 组合有时会被安全软件标记,建议自行编译并添加信任。

七、可扩展方向

如果希望在此基础上继续完善,可以考虑以下几个方向:

  1. 配置文件化:把间隔、速度、透明度等参数写入 ini 或 json,运行时读取,避免每次改代码重新编译。
  2. 自定义热键:把固定的Ctrl+F1~F8改为可配置,通过RegisterHotKey动态注册。
  3. 托盘图标:增加系统托盘菜单,提供更直观的开关入口。
  4. 录制回放:记录一段鼠标键盘操作序列,再按时间轴回放,适合重复性流程。
  5. 多套方案切换:为不同应用保存不同的参数组合,一键切换。

整体来看,这个程序虽然代码量不大,但把 Windows 输入模拟、定时调度、分层窗口和自绘界面几个知识点串在了一起,是一个很适合练手的完整小项目。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询