1. 簡介
玩遊戲的時候難免會遇到一些遊戲的 "折磨"。例如:我們要使用道具的時候,可這個道具居然沒有批量使用!!!
那行吧,我們就來動手做一個按鍵精靈解放我們的雙手。
PS:目前只做了鼠標按鍵版的,如果有需要鍵盤的,可以私信或下方留言,後續看需補充吧~
2. 那我們就開始吧~
①. 首先是Win32的框架(這裏我就直接套用過來了,不懂可以看下我之前的文章哈~)
//++++++++++++++++++++++++++++++++++
// 宏定義
//----------------------------------
#ifndef UNICODE
#define UNICODE // 使用UNICODE編碼,如果在編譯器設置了使用UNICODE字符集此處可免
#endif
#ifndef _UNICODE
#define _UNICODE // 使用UNICODE編碼,如果在編譯器設置了使用UNICODE字符集此處可免
#endif
//++++++++++++++++++++++++++++++++++
// 頭文件
//----------------------------------
#include <windows.h> // Win32程序最重要的頭文件
#include <tchar.h> // 兼容字符集頭文件
#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 <Windows.h>
#include <vector>
// 參照 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<MouseRecInput *>;
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 <tchar.h>
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 < 0 || pStruct->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 運行模擬
後續有需求可以下方留言,到時候在補充吧~~~
源碼:已上傳到Github了哦,有興趣的讀者可以去下載了~