Win32软件开发: 按键精灵 鼠标模拟器(VibraClick)

Paula ·
更新时间:2024-09-20
· 947 次阅读

1. 简介

玩游戏的时候难免会遇到一些游戏的 "折磨"。例如:我们要使用道具的时候,可这个道具居然没有批量使用!!!

那行吧,我们就来动手做一个按键精灵解放我们的双手。

PS:目前只做了鼠标按键版的,如果有需要键盘的,可以私信或下方留言,后续看需补充吧~

2. 那我们就开始吧~

①. 首先是Win32的框架(这里我就直接套用过来了,不懂可以看下我之前的文章哈~)

//++++++++++++++++++++++++++++++++++ // 宏定义 //---------------------------------- #ifndef UNICODE #define UNICODE // 使用UNICODE编码,如果在编译器设置了使用UNICODE字符集此处可免 #endif #ifndef _UNICODE #define _UNICODE // 使用UNICODE编码,如果在编译器设置了使用UNICODE字符集此处可免 #endif //++++++++++++++++++++++++++++++++++ // 头文件 //---------------------------------- #include // Win32程序最重要的头文件 #include // 兼容字符集头文件 #include "VibraClick.h" // 鼠标模拟器头文件 //++++++++++++++++++++++++++++++++++ // 全局变量 //---------------------------------- TCHAR g_lpszClassName[] = _T("VibraClick"); // 窗口类的名称 TCHAR g_lpszWindowName[] = _T("VibraClick"); // 窗口的名称,(也就是窗口的标题) VibraClick g_vibraClick; // 鼠标模拟器 //++++++++++++++++++++++++++++++++++ // 函数声明 //---------------------------------- LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 窗口消息处理过程 VOID VibraClick_Init(HWND); // 初始化软件 //++++++++++++++++++++++++++++++++++ // 游戏主函数 //---------------------------------- INT APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, INT nCmdShow) { /* 1.设计一个窗口类 */ ... /* 2.注册窗口类 */ ... /* 3.创建窗口, 并居中显示 */ ... /* 4.更新显示窗口 */ ... /* 初始化 */ VibraClick_Init(hWnd); /* 5.消息循环 */ ... return msg.wParam; } //++++++++++++++++++++++++++++++++++ // 窗口消息处理过程 //---------------------------------- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_HOTKEY: g_vibraClick.OnHotKey(wParam); break; case WM_DESTROY: // 窗口销毁,释放资源 ::PostQuitMessage(0); break; default: return ::DefWindowProc(hWnd, message, wParam, lParam); } return ((LRESULT)0); } VOID VibraClick_Init(HWND hWnd) { g_vibraClick.Init(hWnd); } 简单讲解上面内容:

1. 定义了一个按键模拟器类(VibraClick) 的变量 g_vibraClick

2. 初始化这个按键模拟器

3. 由于注册了热键 CTRL + S 和 CTRL + R 进行录制和运行,所以消息处理了WM_HOTKEY

②. VibraClick(按键模拟器类)

VibraClick.h

#pragma once #ifndef __VIBRA_CLICK_H__ #define __VIBRA_CLICK_H__ #include #include // 参照 INPUT 类 struct MouseRecInput { DWORD type; MOUSEINPUT mi; }; // 定时器回调 VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT nIDEvent, DWORD dwTime); // 鼠标Hook消息处理 LRESULT CALLBACK MouseMessageProc(INT nCode, WPARAM wParam, LPARAM lParam); class VibraClick { using MouseRecInputVector = std::vector; public: VibraClick(); ~VibraClick(); public: void Init(HWND hWnd); // 初始化 void StartMouseRec(); // 开始录制鼠标操作 void StopMouseRec(); // 停止录制鼠标操作 void StartRunMouseRec(); // 开始运行鼠标录制的内容 void StopRunMouseRec(); // 停止运行鼠标录制的内容 void CleanMouseRecInput(); // 清除鼠标记录内容 void OnHotKey(WPARAM nHotKeyId); // 热键处理 public: static VibraClick *GetInstance() { return _inst; } bool IsStartMouseRec() { return m_IsStartMouseRec; } bool IsStartRunMouseRec() { return m_IsStartRunMouseRec; } HHOOK GetHHMouseHook() { return m_hhMouseHook; } MouseRecInputVector &GetMouseRecInputVector() { return m_vecMouseRecInput; } int GetMouseRecIndex() { return m_MouseRecIndex; } void SetMouseRecIndex(int value) { m_MouseRecIndex = value; } protected: static VibraClick *_inst; // 实例自己 HWND m_hWnd; // 窗口句柄 MouseRecInputVector m_vecMouseRecInput; // 鼠标操作数据 bool m_IsStartMouseRec; // 是否开始记录鼠标操作 bool m_IsStartRunMouseRec; // 是否开始鼠标操作 int m_MouseRecIndex; // 鼠标操作索引 UINT_PTR m_RunTimerId; // 运行的定时器Id ATOM m_VibraClick_StartRec; // 开始记录热键id ATOM m_VibraClick_RunRec; // 开始运行记录热键id // 鼠标的Hook HHOOK m_hhMouseHook; }; #endif // !__VIBRA_CLICK_H__

INPUT 结构体(WinUser.h头文件中)

typedef struct tagINPUT { DWORD type; union { MOUSEINPUT mi; KEYBDINPUT ki; HARDWAREINPUT hi; } DUMMYUNIONNAME; } INPUT, *PINPUT, FAR* LPINPUT;

简单讲解上面的内容:

1. MouseRecInput结构体,具体参照 INPUT 结构体。因为MOUSEINPUT是在共用体内,所以对其进行一个扩展

2. TimerProc是一个定时器的处理,用在运行按键模拟的时候

3. MouseMessageProc这个一个鼠标的录制Hook处理

4. 类的方法和成员变量都有对应的注释就不详细说明了哈~(不懂的话在下方留言吧,到时候再进行补充)

VibraClick.cpp

#include "VibraClick.h" #include VibraClick *VibraClick::_inst = nullptr; VibraClick::VibraClick() { // 初始化一些变量 m_IsStartMouseRec = false; m_IsStartRunMouseRec = false; m_MouseRecIndex = 0; m_RunTimerId = -1; m_hhMouseHook = NULL; m_hWnd = NULL; _inst = this; } VibraClick::~VibraClick() { // 清除鼠标记录内容 CleanMouseRecInput(); StopMouseRec(); StopRunMouseRec(); // 反注册热键 UnregisterHotKey(this->m_hWnd, m_VibraClick_StartRec); // Ctrl + S UnregisterHotKey(this->m_hWnd, m_VibraClick_RunRec); // Ctrl + R } void VibraClick::Init(HWND hWnd) { m_hWnd = hWnd; m_VibraClick_StartRec = GlobalAddAtom(_T("VibraClick_StartRec")) - 0xC000; m_VibraClick_RunRec = GlobalAddAtom(_T("VibraClick_RunRec")) - 0xC000; RegisterHotKey(this->m_hWnd, m_VibraClick_StartRec, MOD_CONTROL, 'S'); // Ctrl + S RegisterHotKey(this->m_hWnd, m_VibraClick_RunRec, MOD_CONTROL, 'R'); // Ctrl + R } void VibraClick::StartMouseRec() { // 清除鼠标记录内容 CleanMouseRecInput(); // 设置当前为录制鼠标操作状态 m_IsStartMouseRec = true; // 开始鼠标Hook(全局鼠标钩子) m_hhMouseHook = SetWindowsHookEx(WH_MOUSE_LL, &MouseMessageProc, GetModuleHandle(NULL), NULL); } void VibraClick::StopMouseRec() { // 取消设置当前为录制鼠标操作状态 m_IsStartMouseRec = false; // 释放鼠标Hook if (m_hhMouseHook != NULL) UnhookWindowsHookEx(m_hhMouseHook); } void VibraClick::StartRunMouseRec() { // 开始模拟鼠标操作 m_IsStartRunMouseRec = true; // 设置运行索引 m_MouseRecIndex = 0; // 开启定时器 m_RunTimerId = SetTimer(m_hWnd, 999, 10, &TimerProc); } void VibraClick::StopRunMouseRec() { // 停止运行鼠标记录的内容 m_IsStartRunMouseRec = false; // 关闭定时器 if (m_RunTimerId != -1) { KillTimer(m_hWnd, m_RunTimerId); m_RunTimerId = -1; } } void VibraClick::CleanMouseRecInput() { // 清除记录的内容 for (auto input : m_vecMouseRecInput) { delete input; input = nullptr; } // 释放vector占用内存 MouseRecInputVector tmp; m_vecMouseRecInput.swap(tmp); // 设置运行索引 m_MouseRecIndex = 0; } void VibraClick::OnHotKey(WPARAM nHotKeyId) { if (nHotKeyId == m_VibraClick_StartRec) { if (m_IsStartMouseRec) StopMouseRec(); else StartMouseRec(); } else if (nHotKeyId == m_VibraClick_RunRec) { if (m_IsStartRunMouseRec) StopRunMouseRec(); else StartRunMouseRec(); } } VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT nIDEvent, DWORD dwTime) { switch (nIDEvent) { case 999: { // 取余方式进行循环运行 int MouseRecIndex = VibraClick::GetInstance()->GetMouseRecIndex(); MouseRecIndex %= VibraClick::GetInstance()->GetMouseRecInputVector().size(); // 读取当前索引的鼠标模拟消息 auto pMHD = VibraClick::GetInstance()->GetMouseRecInputVector()[MouseRecIndex++]; // 通过INPUT进行模拟操作 INPUT Input; Input.type = pMHD->type; memcpy((void *)&Input.mi, (void *)&pMHD->mi, sizeof(MOUSEINPUT)); // 发送模拟消息 SendInput(1, &Input, sizeof(INPUT)); VibraClick::GetInstance()->SetMouseRecIndex(MouseRecIndex); } break; } } LRESULT CALLBACK MouseMessageProc(INT nCode, WPARAM wParam, LPARAM lParam) { PMSLLHOOKSTRUCT pStruct = (PMSLLHOOKSTRUCT)lParam; // LLMHF_INJECTED标志着: 事件是否被注入,通过SendInput后会触发这个标志,也就是模拟的处理消息则不记录了 // nCode 表示有关Hook的消息 /* * Hook Codes * * #define HC_ACTION 0 * #define HC_GETNEXT 1 * #define HC_SKIP 2 * #define HC_NOREMOVE 3 * #define HC_NOREM HC_NOREMOVE * #define HC_SYSMODALON 4 * #define HC_SYSMODALOFF 5 */ if (nCode flags & LLMHF_INJECTED) { return CallNextHookEx(VibraClick::GetInstance()->GetHHMouseHook(), nCode, wParam, lParam); } // 是否开启录制鼠标操作 if (!VibraClick::GetInstance()->IsStartMouseRec()) return CallNextHookEx(VibraClick::GetInstance()->GetHHMouseHook(), nCode, wParam, lParam); // 判断是否为鼠标数据, 当前只是列举一部分的鼠标消息,有需要可以自己加哈~ if ( wParam == WM_LBUTTONDOWN || wParam == WM_LBUTTONUP || wParam == WM_RBUTTONDOWN || wParam == WM_RBUTTONUP || wParam == WM_MBUTTONDOWN || wParam == WM_MBUTTONUP || wParam == WM_MOUSEMOVE) { MouseRecInput *Input = new MouseRecInput; // 现在固定为鼠标的模拟输入 Input->type = INPUT_MOUSE; // 设置输入的数据 Input->mi.dx = pStruct->pt.x; Input->mi.dy = pStruct->pt.y; Input->mi.mouseData = pStruct->mouseData; Input->mi.time = pStruct->time; Input->mi.dwExtraInfo = pStruct->dwExtraInfo; switch (wParam) { case WM_LBUTTONDOWN: Input->mi.dwFlags = MOUSEEVENTF_LEFTDOWN; break; case WM_LBUTTONUP: Input->mi.dwFlags = MOUSEEVENTF_LEFTUP; break; case WM_RBUTTONDOWN: Input->mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; break; case WM_RBUTTONUP: Input->mi.dwFlags = MOUSEEVENTF_RIGHTUP; break; case WM_MBUTTONDOWN: Input->mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; break; case WM_MBUTTONUP: Input->mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break; case WM_MOUSEMOVE: { int cx_screen = ::GetSystemMetrics(SM_CXSCREEN); int cy_screen = ::GetSystemMetrics(SM_CYSCREEN); Input->mi.dx = pStruct->pt.x * 65536 / cx_screen; Input->mi.dy = pStruct->pt.y * 65536 / cy_screen; Input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; } break; } VibraClick::GetInstance()->GetMouseRecInputVector().push_back(Input); } return CallNextHookEx(VibraClick::GetInstance()->GetHHMouseHook(), nCode, wParam, lParam); }

简单讲解上面的内容:

1. 对静态的_inst进行一个初始化,做这个主要是在Hook和定时器中进行使用到这个按键模拟器

2. 初始化的时候进行保存窗口的句柄,然后进行注册热键和定时器的使用

3. 讲一下这个HOOK的MOUSEMOVE,鼠标点击的x和y坐标需要转换到绝对位置,屏幕的全屏范围是 0~65535,所以需要用当前的电脑分辨率进行转换到绝对的位置

最后PS:这个按键模拟器没有按钮,所以目前只能靠热键进行模拟

分别是:

CTRL + S 开启和关闭录制

CTRL + R 运行模拟

后续有需求可以下方留言,到时候在补充吧~~~

源码:关注公众号输入 (鼠标模拟器) 即可领取哦~


作者:南雨兮



软件开 模拟器 win32 按键精灵 win 软件

需要 登录 后方可回复, 如果你还没有账号请 注册新账号