Я хотел использовать снайперскую винтовку в качестве винтовки, когда играл в PUBG, поэтому я написал кликер мыши, который в большинстве случаев работает очень хорошо, но в некоторых случаях, когда я отпускаю мышь, он все еще продолжает непрерывно стрелять. Я подозреваю, что это происходит потому, что я отпускаю левую кнопку мыши после того, как код определяет, что мышь нажата, но код еще не выполнил смоделированный щелчок, что приведет к тому, что операционная система получит аппаратное сообщение об освобождении мыши, но программное обеспечение отправляет еще одну левую кнопку мыши. Сообщение о том, что кнопка нажата, заставляет левую кнопку мыши оставаться нажатой.
Я пытался использовать потоки или мьютексы, чтобы убедиться, что следующее событие будет выполнено после завершения выполнения, но это не имеет никакого эффекта. Поэтому я подозреваю, что это вызвано задержкой выполнения процессора. Как решить эту проблему.
Ниже приведена часть оценки цикла таймера в коде.
static void click() {
if (KEY_DOWN(VK_LBUTTON)) {
POINT point;
GetCursorPos(&point);
if (KEY_DOWN(VK_LBUTTON)) {
mouse_event(MOUSEEVENTF_LEFTDOWN, point.x, point.y, 0, 0);
}
};
}
Все коды ниже.
#include <windows.h>
#include<vector>
#include<string>
#include<uxtheme.h>
#pragma comment(lib,"UxTheme.lib")
using namespace std;
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#pragma region Help
#define WM_MONITOR 0x8075u
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
INT toint(std::wstring str) {
INT nRet = 0;
if (!str.empty())
{
nRet = _wtoi(str.c_str());
}
else
nRet = 0;
return nRet;
}
std::wstring GetText(HWND m_hWnd)
{
if (!m_hWnd)
{
return std::wstring();
}
int length = GetWindowTextLengthW(m_hWnd);
std::vector<wchar_t> buffer(length + 1);
GetWindowTextW(m_hWnd, buffer.data(), buffer.size());
return std::wstring(buffer.data());
}
#pragma endregion
#pragma region Helpclass
class Clock
{
public:
//-回调函数 返回值:无 (整数型 hWnd,整数型 msg,整数型 时钟ID,整数型 系统启动时间)
//-void (HWND hWnd,UINT msg,UINT_PTR 时钟ID,UINT_PTR 系统启动时间)
bool Create(HWND hWnd, UINT time, void* callback)
{
m_hWnd = hWnd;
m_hClock = (UINT_PTR)this;
m_Callback = callback;
m_Time = time;
if (time == 0)
{
return true;
}
m_hClock = SetTimer(hWnd, m_hClock, time, (TIMERPROC)m_Callback);;
return m_hClock == 0;
};
bool Time(UINT time) {
if (time != m_Time)
{
if (m_Time == 0) {
m_hClock = SetTimer(m_hWnd, m_hClock, time, (TIMERPROC)m_Callback);
if (m_hClock) {
m_Time = time;
return true;
};
}
if (time == 0)
{
m_Time = time;
return KillTimer(m_hWnd, m_hClock);
}
if (KillTimer(m_hWnd, m_hClock)) {
m_hClock = SetTimer(m_hWnd, m_hClock, time, (TIMERPROC)m_Callback);
if (m_hClock) {
m_Time = time;
return true;
};
};
}
return false;
};
UINT Time() {
return m_Time;
};
bool Destory() {
return KillTimer(m_hWnd, m_hClock);
};
Clock() {
m_hWnd = 0;
m_hClock = (UINT_PTR)this;
m_Callback = 0;
m_Time = 0;
};
~Clock() {
if (m_hClock)
{
KillTimer(m_hWnd, m_hClock);
m_Callback = nullptr;
m_hClock = 0;
m_hWnd = 0;
m_Time = 0;
}
};
private:
UINT_PTR m_hClock;
HWND m_hWnd;
void* m_Callback;
UINT m_Time;
};
class MyRegisterHotKey
{
public:
int Register(HWND hWnd, HWND labelhwnd, UINT fsModifiers, UINT vk)
{
// 将 fsModifiers 转换为 RegisterHotKey 函数所需的值
label_hwnd = labelhwnd;
switch (fsModifiers)
{
case 1: // Alt
fsModifiers = MOD_ALT;
break;
case 2: // Ctrl
fsModifiers = MOD_CONTROL;
break;
case 4: // Shift
fsModifiers = MOD_SHIFT;
break;
case 3: // Alt + Ctrl
fsModifiers = MOD_ALT | MOD_CONTROL;
break;
case 5: // Alt + Shift
fsModifiers = MOD_ALT | MOD_SHIFT;
break;
case 6: // Ctrl + Shift
fsModifiers = MOD_CONTROL | MOD_SHIFT;
break;
}
// 如果窗口还没有设置窗口过程,就将窗口过程设置为 shellEx__RegWindowProc
if (!old_proc)
old_proc = (WNDPROC)SetWindowLongPtrA(hWnd, GWLP_WNDPROC, (LONG_PTR)RegWindowProc);//获取缺省回调,不太安全是否有方法修改
SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)this);
IDarry = ++regcount + 33000;//本次标识
if (RegisterHotKey(hWnd, regcount + 33000, fsModifiers, vk))
return IDarry;
else
return 0;
}
private:
static LRESULT WINAPI RegWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
MyRegisterHotKey* r = (MyRegisterHotKey*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
// 当接收到消息类型为 WM_HOTKEY 时,执行循环遍历
if (uMsg == WM_HOTKEY)
{
for (int i = 33000; i <= r->IDarry; i++)//易里标识里从33000开始,不知道为什么怕冲突??
{
// 检查 传入参数 是否与 值 相等
if (i == wParam)//ID值
{
// 如果相等,则发送消息类型为 WM_COMMAND 的消息给 label_hwnd
SendMessageA(r->label_hwnd, WM_MONITOR, wParam, 0);
}
}
}
// 调用原来的窗口过程函数
return CallWindowProcA(r->old_proc, hWnd, uMsg, wParam, lParam);
}
int regcount = 0;
int IDarry = 0;
HWND label_hwnd = 0;
WNDPROC old_proc = 0;
};
#pragma endregion
static int F11 = 0;
static int F10 = 0;
static bool IsF10 = false;
static Clock Click;
static HWND hStatic;
static HWND hEditBox;
static WNDPROC StaticOldProc = NULL;
class ClickWindow
{
static void StarContinuousClick() {
if (!IsF10)
{
IsF10 = true;
::MessageBeep(MB_OK);
int t = 0;
if (!GetText(hEditBox).empty())
{
t = toint(GetText(hEditBox));
}
//开始连点就不允许编辑
EnableWindow(hEditBox, FALSE);
Click.Time(t);
}
};
static void EndContinuousClick() {
if (IsF10)
{
IsF10 = false;
::MessageBeep(0xFFFFFFFF);
EnableWindow(hEditBox, TRUE);
Click.Time(0);
}
};
static LRESULT CALLBACK StaticWNDPROC(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
if (StaticOldProc == NULL)
{
StaticOldProc = DefWindowProc;
}
PAINTSTRUCT ps;
HDC hdcWnd;
HDC hdcStatic;
static HBRUSH hBrush; //画刷
hBrush = CreateSolidBrush(RGB(0x41, 0x96, 0x4F));
switch (Message) {
case WM_MONITOR:
if (wParam == F10)
{
StarContinuousClick();
}
else if (wParam == F11)
{
EndContinuousClick();
}
break;
default:
break;
}
return StaticOldProc(hwnd, Message, wParam, lParam);
}
static void click() {
if (KEY_DOWN(VK_LBUTTON)) {
POINT point;
GetCursorPos(&point);
if (KEY_DOWN(VK_LBUTTON)) {
mouse_event(MOUSEEVENTF_LEFTDOWN, point.x, point.y, 0, 0);
}
};
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
static HBRUSH hBrush; //画刷
switch (Message)
{
case WM_CREATE: {
//创建画刷
hBrush = CreateSolidBrush(RGB(255, 255, 255)); //白色
//创建控件
hEditBox = CreateWindow(L"Edit",
L"80",
WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL,
125,
11,
96,
24,
hwnd,
0,
GetModuleHandleW(0),
NULL);
LONG_PTR exStyle = GetWindowLongPtr(hEditBox, GWL_EXSTYLE);
HWND temp = CreateWindow(L"Static",
L"请输入频率",
WS_CHILD | WS_VISIBLE | ES_LEFT | BS_MULTILINE,
40,
13,
72,
24,
hwnd,
0,
GetModuleHandleW(0),
NULL);
hStatic = CreateWindow(L"Static",
L"F10:开启 F11:关闭",
WS_CHILD | WS_VISIBLE | ES_LEFT | BS_MULTILINE,
60,
52,
136,
24,
hwnd,
0,
GetModuleHandleW(0),
NULL);
SetBkMode(GetDC(hStatic), TRANSPARENT);
LOGFONTW m_Font = { 0 };
wchar_t FontName[LF_FACESIZE] = L"Times New Roman";
wcscpy_s(m_Font.lfFaceName, FontName);
m_Font.lfHeight = 16;
SendMessageW(hStatic, WM_SETFONT, (WPARAM)CreateFontIndirectW(&m_Font), 1);
SendMessageW(hEditBox, WM_SETFONT, (WPARAM)CreateFontIndirectW(&m_Font), 1);
SendMessageW(temp, WM_SETFONT, (WPARAM)CreateFontIndirectW(&m_Font), 1);
StaticOldProc = (WNDPROC)SetWindowLongPtrW(hStatic, GWLP_WNDPROC, (LONG_PTR)StaticWNDPROC);
Click.Create(hwnd, 0, click);
//win98风格
SetWindowTheme(hwnd, L"", L"");
break;
}
case WM_CTLCOLORSTATIC:
{
//可以通过子类化窗口回调加上获取HDC需要绘制的rect,来改变指定控件
HDC hdc = (HDC)wParam;
SetTextColor(hdc, 255);
SetBkColor(hdc, RGB(255, 255, 255));
return (LRESULT)hBrush;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
public:
ClickWindow(HMODULE hInstance) :hwnd(NULL), msg{ 0 }{
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"ALWindowClass";
if (!RegisterClassEx(&wc)) {
exit(0);
return;
}
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
L"ALWindowClass",
L"阿龙吃鸡连点器:",
WS_VISIBLE | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,
280,
119,
NULL,
NULL,
hInstance,
NULL);
//置WIN98风格
F11 = r.Register(hwnd, hStatic, 0, 122);
F10 = r.Register(hwnd, hStatic, 0, 121);
};
WPARAM ShowAndLoop() {
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
~ClickWindow() {
DestroyWindow(hwnd);
exit(0);
};
private:
HWND hwnd;
MSG msg;
MyRegisterHotKey r;
};
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
ClickWindow MainWindow(hInstance);
MainWindow.ShowAndLoop();
}
Я предполагаю, что программа стреляет себе в лицо с приведением m_hClock = SetTimer(hWnd, m_hClock, time, (TIMERPROC)m_Callback);; Многолетний опыт научил меня, что если вам нужно привести указатель на функцию, вы уже проиграли. Более глубокое чтение доказывает, что я ошибаюсь, но вы должны поддерживать правильный тип на протяжении всей системы. Приведение к void*, а затем обратно лишает компилятор способности предотвращать МНОЖЕСТВО ошибок.
Неа. Я был прав в первый раз. void click() не совпадает void (*)(HWND__*, unsigned int, long long unsigned int, long unsigned int) Взял компилятор, чтобы увидеть.
Пожалуйста, обрежьте свой код, чтобы было легче найти вашу проблему. Следуйте этим рекомендациям, чтобы создать минимально воспроизводимый пример.
@user4581301 user4581301 Это правильно только для 64-битной версии, я поставил правильную подпись в своем ответе ...
@Андерс меня не удивляет. Хуже того, я возился с gcc, когда я должен был просто сделать это в начале и избавил себя от второго предположения





Установите крючок WH_MOUSE_LL. Когда кнопка мыши будет отпущена, отправьте сообщение окну, чтобы остановить его или установить глобальную переменную.
Как отмечено в комментариях, сигнатура функции click неверна для обратного вызова таймера. Снимите гипс и исправьте click() и свой класс.
void CALLBACK click(HWND, UINT, UINT_PTR, DWORD) { ... }
Никогда не давайте весь код. Укажите абсолютный минимум, необходимый для воспроизведения ошибки. Это код события win32, так что будет много шаблонов, но 400 строк — это немного круто. Мало кто захочет пройти через это бесплатно.