#ifndef UNICODE #define UNICODE #endif #ifndef _UN...
생성일: 2026년 5월 12일
답변에 사용된 모델 GPT-5.5 Thinking by Chat01
생성일: 2026년 5월 12일
답변에 사용된 모델 GPT-5.5 Thinking by Chat01
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <sstream>
#include <mutex>
#include <algorithm>
#include <cwchar>
#pragma comment(lib, "Comctl32.lib")
// =====================================================
// Настройки
// =====================================================
static constexpr int APP_W = 1020;
static constexpr int APP_H = 760;
static constexpr COLORREF CLR_BG = RGB(8, 8, 10);
static constexpr COLORREF CLR_PANEL = RGB(14, 14, 17);
static constexpr COLORREF CLR_PANEL_2 = RGB(21, 21, 25);
static constexpr COLORREF CLR_BORDER = RGB(120, 0, 15);
static constexpr COLORREF CLR_ACCENT = RGB(235, 25, 40);
static constexpr COLORREF CLR_ACCENT_2 = RGB(165, 15, 25);
static constexpr COLORREF CLR_TEXT = RGB(235, 235, 235);
static constexpr COLORREF CLR_MUTED = RGB(165, 165, 165);
// =====================================================
// Глобальные переменные
// =====================================================
struct WindowItem {
HWND hwnd;
std::wstring title;
};
std::vector<WindowItem> g_windowList;
std::vector<HWND> g_selectedWindows;
std::mutex g_windowsMutex;
std::atomic<bool> g_clicking(false);
std::atomic<int> g_cps(500);
std::atomic<bool> g_useVirtualMouse(false);
std::atomic<long> g_virtualX(0);
std::atomic<long> g_virtualY(0);
HWND g_mainHwnd = nullptr;
HWND g_virtualMouse = nullptr;
HFONT g_font = nullptr;
HFONT g_fontBold = nullptr;
HFONT g_fontTitle = nullptr;
HBRUSH g_brBg = nullptr;
HBRUSH g_brPanel = nullptr;
HBRUSH g_brEdit = nullptr;
// =====================================================
// UI элементы
// =====================================================
HWND hBtnRefresh = nullptr;
HWND hBtnApplyWindows = nullptr;
HWND hStaticApplied = nullptr;
HWND hListView = nullptr;
HWND hEditCPS = nullptr;
HWND hBtnApplyCPS = nullptr;
HWND hStaticCPS = nullptr;
HWND hChkVirtual = nullptr;
HWND hStaticHint = nullptr;
HWND hBtnMin = nullptr;
HWND hBtnMax = nullptr;
HWND hBtnClose = nullptr;
// =====================================================
// ID
// =====================================================
enum {
ID_BTN_REFRESH = 101,
ID_LISTVIEW = 102,
ID_EDIT_CPS = 103,
ID_BTN_APPLY_CPS = 104,
ID_STATIC_CPS = 105,
ID_CHK_VIRTUAL = 106,
ID_BTN_APPLY_WINDOWS = 107,
ID_HOTKEY_F6 = 108,
ID_STATIC_APPLIED = 109,
ID_HOTKEY_F7 = 110,
textID_BTN_MIN = 201, ID_BTN_MAX = 202, ID_BTN_CLOSE = 203
};
// =====================================================
// Прототипы
// =====================================================
void RefreshWindowList();
void UpdateSelectedWindows();
void ClickLoop();
void CreateVirtualMouse();
void DestroyVirtualMouse();
void SetVirtualMouseFromCursor();
void MoveVirtualMouseWindow();
void UpdateCpsText();
void UpdateAppliedText();
static inline POINT GetVirtualPoint()
{
return POINT{ g_virtualX.load(), g_virtualY.load() };
}
// =====================================================
// GDI helpers
// =====================================================
void FillRound(HDC hdc, RECT rc, COLORREF fill, COLORREF border, int radius)
{
HBRUSH br = CreateSolidBrush(fill);
HPEN pen = CreatePen(PS_SOLID, 1, border);
textHGDIOBJ oldBr = SelectObject(hdc, br); HGDIOBJ oldPen = SelectObject(hdc, pen); RoundRect(hdc, rc.left, rc.top, rc.right, rc.bottom, radius, radius); SelectObject(hdc, oldBr); SelectObject(hdc, oldPen); DeleteObject(br); DeleteObject(pen);
}
void DrawTitleLogo(HDC hdc)
{
HPEN redPen = CreatePen(PS_SOLID, 3, CLR_ACCENT);
HGDIOBJ oldPen = SelectObject(hdc, redPen);
HGDIOBJ oldBr = SelectObject(hdc, GetStockObject(NULL_BRUSH));
textEllipse(hdc, 28, 24, 64, 60); Ellipse(hdc, 38, 34, 54, 50); MoveToEx(hdc, 46, 42, nullptr); LineTo(hdc, 70, 66); SelectObject(hdc, oldBr); SelectObject(hdc, oldPen); DeleteObject(redPen); SetBkMode(hdc, TRANSPARENT); HFONT oldFont = (HFONT)SelectObject(hdc, g_fontTitle); SetTextColor(hdc, CLR_ACCENT); TextOutW(hdc, 84, 27, L"Ред", 3); SetTextColor(hdc, CLR_TEXT); TextOutW(hdc, 130, 27, L"Кликер", 6); SelectObject(hdc, oldFont);
}
void DrawWindowChrome(HDC hdc)
{
RECT full{ 0, 0, APP_W, APP_H };
HBRUSH bg = CreateSolidBrush(CLR_BG);
FillRect(hdc, &full, bg);
DeleteObject(bg);
textRECT outer{ 0, 0, APP_W, APP_H }; FillRound(hdc, outer, CLR_BG, CLR_BORDER, 18); RECT topLine{ 0, 74, APP_W, 75 }; HBRUSH brLine = CreateSolidBrush(RGB(40, 20, 23)); FillRect(hdc, &topLine, brLine); DeleteObject(brLine); DrawTitleLogo(hdc); RECT appliedPanel{ 28, 180, 988, 230 }; FillRound(hdc, appliedPanel, CLR_PANEL, RGB(45, 45, 50), 8); RECT listPanel{ 28, 244, 988, 588 }; FillRound(hdc, listPanel, CLR_BG, CLR_BORDER, 8); SetBkMode(hdc, TRANSPARENT); HFONT oldFont = (HFONT)SelectObject(hdc, g_fontBold); SetTextColor(hdc, CLR_ACCENT); TextOutW(hdc, 50, 260, L"▦ Список окон", 15); SelectObject(hdc, oldFont);
}
// =====================================================
// Owner-draw buttons
// =====================================================
void DrawOwnerButton(const DRAWITEMSTRUCT* dis)
{
HDC hdc = dis->hDC;
RECT rc = dis->rcItem;
UINT id = dis->CtlID;
textbool pressed = (dis->itemState & ODS_SELECTED) != 0; bool hot = (dis->itemState & ODS_HOTLIGHT) != 0; COLORREF fill = CLR_PANEL_2; COLORREF border = RGB(55, 55, 60); COLORREF text = CLR_TEXT; if (id == ID_BTN_APPLY_WINDOWS || id == ID_BTN_APPLY_CPS) { fill = pressed ? CLR_ACCENT_2 : CLR_ACCENT; border = RGB(255, 60, 75); text = RGB(255, 255, 255); } if (id == ID_BTN_CLOSE) { fill = pressed ? RGB(130, 15, 25) : CLR_BG; border = CLR_BG; text = CLR_TEXT; } if (id == ID_BTN_MIN || id == ID_BTN_MAX) { fill = CLR_BG; border = CLR_BG; text = CLR_TEXT; } if (hot && id != ID_BTN_APPLY_WINDOWS && id != ID_BTN_APPLY_CPS) { fill = RGB(26, 26, 31); } FillRound(hdc, rc, fill, border, 9); wchar_t textBuf[128]{}; GetWindowTextW(dis->hwndItem, textBuf, 128); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, text); HFONT oldFont = (HFONT)SelectObject(hdc, g_fontBold); DrawTextW(hdc, textBuf, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); SelectObject(hdc, oldFont);
}
// =====================================================
// Перечисление окон
// =====================================================
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM)
{
if (!IsWindowVisible(hwnd))
return TRUE;
textif (hwnd == g_mainHwnd || hwnd == g_virtualMouse) return TRUE; wchar_t title[512]{}; GetWindowTextW(hwnd, title, 512); if (wcslen(title) == 0) return TRUE; LONG_PTR exStyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE); if (exStyle & WS_EX_TOOLWINDOW) return TRUE; g_windowList.push_back(WindowItem{ hwnd, title }); return TRUE;
}
void RefreshWindowList()
{
g_windowList.clear();
EnumWindows(EnumWindowsProc, 0);
textstd::sort(g_windowList.begin(), g_windowList.end(), [](const WindowItem& a, const WindowItem& b) { return a.title < b.title; }); ListView_DeleteAllItems(hListView); LVCOLUMNW col{}; col.mask = LVCF_TEXT | LVCF_WIDTH; col.pszText = const_cast<LPWSTR>(L"Окна"); col.cx = 850; if (Header_GetItemCount(ListView_GetHeader(hListView)) == 0) ListView_InsertColumn(hListView, 0, &col); else ListView_SetColumn(hListView, 0, &col); for (int i = 0; i < (int)g_windowList.size(); ++i) { LVITEMW item{}; item.mask = LVIF_TEXT | LVIF_PARAM; item.iItem = i; item.iSubItem = 0; item.pszText = const_cast<LPWSTR>(g_windowList[i].title.c_str()); item.lParam = reinterpret_cast<LPARAM>(g_windowList[i].hwnd); ListView_InsertItem(hListView, &item); } { std::lock_guard<std::mutex> lock(g_windowsMutex); g_selectedWindows.clear(); } UpdateAppliedText();
}
void UpdateAppliedText()
{
size_t count = 0;
text{ std::lock_guard<std::mutex> lock(g_windowsMutex); count = g_selectedWindows.size(); } std::wstringstream ss; ss << L"▦ Применённые окна: " << count; SetWindowTextW(hStaticApplied, ss.str().c_str());
}
void UpdateSelectedWindows()
{
std::vector<HWND> picked;
textint count = ListView_GetItemCount(hListView); for (int i = 0; i < count; ++i) { UINT state = ListView_GetItemState(hListView, i, LVIS_STATEIMAGEMASK); if (state == INDEXTOSTATEIMAGEMASK(2)) { LVITEMW item{}; item.mask = LVIF_PARAM; item.iItem = i; if (ListView_GetItem(hListView, &item)) { HWND hwnd = reinterpret_cast<HWND>(item.lParam); if (IsWindow(hwnd)) picked.push_back(hwnd); } } } { std::lock_guard<std::mutex> lock(g_windowsMutex); g_selectedWindows = picked; } UpdateAppliedText();
}
// =====================================================
// Ввод CPS
// =====================================================
void UpdateCpsText()
{
std::wstringstream ss;
ss << L"CPS: " << g_cps.load();
SetWindowTextW(hStaticCPS, ss.str().c_str());
}
void ApplyCpsFromEdit()
{
wchar_t buf[32]{};
GetWindowTextW(hEditCPS, buf, 32);
textint value = _wtoi(buf); if (value < 1) value = 1; // Можно поднять выше, но это уже будет сильнее грузить систему. if (value > 50000) value = 50000; g_cps.store(value); std::wstringstream ss; ss << value; SetWindowTextW(hEditCPS, ss.str().c_str()); UpdateCpsText();
}
// =====================================================
// Клики
// =====================================================
void SendGlobalClickBatch(int clicks)
{
const int MAX_BATCH = 64;
textwhile (clicks > 0) { int batch = std::min(clicks, MAX_BATCH); std::vector<INPUT> inputs(batch * 2); for (int i = 0; i < batch; ++i) { inputs[i * 2].type = INPUT_MOUSE; inputs[i * 2].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[i * 2 + 1].type = INPUT_MOUSE; inputs[i * 2 + 1].mi.dwFlags = MOUSEEVENTF_LEFTUP; } SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT)); clicks -= batch; }
}
void SendVirtualClickBatch(POINT p, int clicks)
{
const int MAX_BATCH = 64;
textint screenW = GetSystemMetrics(SM_CXSCREEN); int screenH = GetSystemMetrics(SM_CYSCREEN); LONG xAbs = (LONG)((double)p.x * 65535.0 / (screenW - 1)); LONG yAbs = (LONG)((double)p.y * 65535.0 / (screenH - 1)); while (clicks > 0) { int batch = std::min(clicks, MAX_BATCH); std::vector<INPUT> inputs(1 + batch * 2); inputs[0].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; inputs[0].mi.dx = xAbs; inputs[0].mi.dy = yAbs; for (int i = 0; i < batch; ++i) { inputs[1 + i * 2].type = INPUT_MOUSE; inputs[1 + i * 2].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[1 + i * 2 + 1].type = INPUT_MOUSE; inputs[1 + i * 2 + 1].mi.dwFlags = MOUSEEVENTF_LEFTUP; } SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT)); clicks -= batch; }
}
LPARAM MakeMouseLParam(POINT clientPoint)
{
return MAKELPARAM((SHORT)clientPoint.x, (SHORT)clientPoint.y);
}
void PostWindowClick(HWND hwnd, POINT screenPoint)
{
if (!IsWindow(hwnd))
return;
textPOINT clientPoint = screenPoint; ScreenToClient(hwnd, &clientPoint); LPARAM lp = MakeMouseLParam(clientPoint); PostMessageW(hwnd, WM_MOUSEMOVE, 0, lp); PostMessageW(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lp); PostMessageW(hwnd, WM_LBUTTONUP, 0, lp);
}
void DoClickBurst(int amount)
{
if (amount <= 0)
return;
textstd::vector<HWND> targets; { std::lock_guard<std::mutex> lock(g_windowsMutex); targets = g_selectedWindows; } bool virtualMode = g_useVirtualMouse.load(); POINT realPos{}; GetCursorPos(&realPos); POINT virtualPos = GetVirtualPoint(); POINT effectivePos = realPos; if (!targets.empty()) { bool cursorInsideTarget = false; for (HWND hwnd : targets) { RECT rc{}; if (IsWindow(hwnd) && GetWindowRect(hwnd, &rc) && PtInRect(&rc, realPos)) { cursorInsideTarget = true; break; } } if (!cursorInsideTarget && virtualMode) effectivePos = virtualPos; for (int i = 0; i < amount; ++i) { for (HWND hwnd : targets) { PostWindowClick(hwnd, effectivePos); } } } else { if (virtualMode) SendVirtualClickBatch(virtualPos, amount); else SendGlobalClickBatch(amount); }
}
void ClickLoop()
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
textLARGE_INTEGER freq{}; LARGE_INTEGER last{}; LARGE_INTEGER now{}; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&last); double accumulator = 0.0; int idleSpins = 0; while (g_clicking.load(std::memory_order_relaxed)) { QueryPerformanceCounter(&now); double elapsed = double(now.QuadPart - last.QuadPart) / double(freq.QuadPart); last = now; int localCps = g_cps.load(std::memory_order_relaxed); if (localCps < 1) localCps = 1; accumulator += elapsed * double(localCps); int due = (int)accumulator; if (due > 0) { // Ограничение одного бурста, чтобы после лага не зависнуть на догонялке. if (due > 256) due = 256; accumulator -= due; DoClickBurst(due); idleSpins = 0; } else { if (++idleSpins < 200) { YieldProcessor(); } else { Sleep(0); idleSpins = 0; } } }
}
void ToggleClicking()
{
bool now = !g_clicking.load();
textg_clicking.store(now); if (now) { std::thread(ClickLoop).detach(); SetWindowTextW(hStaticHint, L"F6: остановить F7: поставить виртуальную точку"); } else { SetWindowTextW(hStaticHint, L"F6: старт/стоп F7: поставить виртуальную точку"); }
}
// =====================================================
// Виртуальная мышь
// =====================================================
LRESULT CALLBACK VirtualMouseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint(hwnd, &ps);
textRECT rc{ 0, 0, 22, 22 }; HBRUSH black = CreateSolidBrush(RGB(0, 0, 0)); FillRect(hdc, &rc, black); DeleteObject(black); HBRUSH red = CreateSolidBrush(CLR_ACCENT); HGDIOBJ oldBr = SelectObject(hdc, red); HGDIOBJ oldPen = SelectObject(hdc, GetStockObject(NULL_PEN)); Ellipse(hdc, 1, 1, 21, 21); SelectObject(hdc, oldPen); SelectObject(hdc, oldBr); DeleteObject(red); EndPaint(hwnd, &ps); return 0; } } return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void MoveVirtualMouseWindow()
{
if (!g_virtualMouse)
return;
textPOINT p = GetVirtualPoint(); SetWindowPos( g_virtualMouse, HWND_TOPMOST, p.x - 11, p.y - 11, 22, 22, SWP_NOACTIVATE | SWP_SHOWWINDOW );
}
void CreateVirtualMouse()
{
if (g_virtualMouse)
return;
textPOINT p = GetVirtualPoint(); g_virtualMouse = CreateWindowExW( WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT, L"RedClickerVirtualMouse", L"", WS_POPUP, p.x - 11, p.y - 11, 22, 22, nullptr, nullptr, GetModuleHandleW(nullptr), nullptr ); SetLayeredWindowAttributes(g_virtualMouse, RGB(0, 0, 0), 230, LWA_COLORKEY | LWA_ALPHA); ShowWindow(g_virtualMouse, SW_SHOWNOACTIVATE);
}
void DestroyVirtualMouse()
{
if (g_virtualMouse) {
DestroyWindow(g_virtualMouse);
g_virtualMouse = nullptr;
}
}
void SetVirtualMouseFromCursor()
{
POINT p{};
GetCursorPos(&p);
textg_virtualX.store(p.x); g_virtualY.store(p.y); if (g_useVirtualMouse.load()) { CreateVirtualMouse(); MoveVirtualMouseWindow(); }
}
// =====================================================
// Главное окно
// =====================================================
LRESULT CALLBACK MainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_CREATE:
{
g_brBg = CreateSolidBrush(CLR_BG);
g_brPanel = CreateSolidBrush(CLR_PANEL);
g_brEdit = CreateSolidBrush(CLR_PANEL_2);
textg_font = CreateFontW( 20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI" ); g_fontBold = CreateFontW( 20, 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI" ); g_fontTitle = CreateFontW( 25, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI" ); hBtnMin = CreateWindowW( L"BUTTON", L"—", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, APP_W - 198, 22, 54, 38, hwnd, (HMENU)ID_BTN_MIN, GetModuleHandleW(nullptr), nullptr ); hBtnMax = CreateWindowW( L"BUTTON", L"□", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, APP_W - 134, 22, 54, 38, hwnd, (HMENU)ID_BTN_MAX, GetModuleHandleW(nullptr), nullptr ); hBtnClose = CreateWindowW( L"BUTTON", L"×", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, APP_W - 70, 22, 54, 38, hwnd, (HMENU)ID_BTN_CLOSE, GetModuleHandleW(nullptr), nullptr ); hBtnRefresh = CreateWindowW( L"BUTTON", L"⟳ Обновить список окон", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 28, 100, 355, 54, hwnd, (HMENU)ID_BTN_REFRESH, GetModuleHandleW(nullptr), nullptr ); hBtnApplyWindows = CreateWindowW( L"BUTTON", L"✓ Применить окна", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 400, 100, 355, 54, hwnd, (HMENU)ID_BTN_APPLY_WINDOWS, GetModuleHandleW(nullptr), nullptr ); hStaticApplied = CreateWindowW( L"STATIC", L"▦ Применённые окна: 0", WS_CHILD | WS_VISIBLE, 50, 194, 420, 28, hwnd, (HMENU)ID_STATIC_APPLIED, GetModuleHandleW(nullptr), nullptr ); hListView = CreateWindowW( WC_LISTVIEWW, nullptr, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER, 52, 292, 910, 276, hwnd, (HMENU)ID_LISTVIEW, GetModuleHandleW(nullptr), nullptr ); ListView_SetExtendedListViewStyle( hListView, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER ); ListView_SetBkColor(hListView, CLR_PANEL); ListView_SetTextBkColor(hListView, CLR_PANEL); ListView_SetTextColor(hListView, CLR_TEXT); hEditCPS = CreateWindowW( L"EDIT", L"500", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER, 28, 616, 180, 54, hwnd, (HMENU)ID_EDIT_CPS, GetModuleHandleW(nullptr), nullptr ); hBtnApplyCPS = CreateWindowW( L"BUTTON", L"⚡ Применить CPS", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 230, 616, 255, 54, hwnd, (HMENU)ID_BTN_APPLY_CPS, GetModuleHandleW(nullptr), nullptr ); hStaticCPS = CreateWindowW( L"STATIC", L"CPS: 500", WS_CHILD | WS_VISIBLE, 510, 630, 200, 32, hwnd, (HMENU)ID_STATIC_CPS, GetModuleHandleW(nullptr), nullptr ); hChkVirtual = CreateWindowW( L"BUTTON", L" Виртуальная мышь", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 28, 694, 260, 30, hwnd, (HMENU)ID_CHK_VIRTUAL, GetModuleHandleW(nullptr), nullptr ); hStaticHint = CreateWindowW( L"STATIC", L"F6: старт/стоп F7: поставить виртуальную точку", WS_CHILD | WS_VISIBLE, 320, 696, 500, 28, hwnd, nullptr, GetModuleHandleW(nullptr), nullptr ); HWND controls[] = { hBtnMin, hBtnMax, hBtnClose, hBtnRefresh, hBtnApplyWindows, hStaticApplied, hListView, hEditCPS, hBtnApplyCPS, hStaticCPS, hChkVirtual, hStaticHint }; for (HWND c : controls) SendMessageW(c, WM_SETFONT, (WPARAM)g_font, TRUE); SendMessageW(hStaticApplied, WM_SETFONT, (WPARAM)g_fontBold, TRUE); SendMessageW(hStaticCPS, WM_SETFONT, (WPARAM)g_fontBold, TRUE); RefreshWindowList(); RegisterHotKey(hwnd, ID_HOTKEY_F6, 0, VK_F6); RegisterHotKey(hwnd, ID_HOTKEY_F7, 0, VK_F7); POINT center{ GetSystemMetrics(SM_CXSCREEN) / 2, GetSystemMetrics(SM_CYSCREEN) / 2 }; g_virtualX.store(center.x); g_virtualY.store(center.y); return 0; } case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT ps{}; HDC hdc = BeginPaint(hwnd, &ps); DrawWindowChrome(hdc); EndPaint(hwnd, &ps); return 0; } case WM_NCHITTEST: { POINT p{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ScreenToClient(hwnd, &p); if (p.y >= 0 && p.y <= 75) { if (p.x >= APP_W - 220) return HTCLIENT; return HTCAPTION; } return HTCLIENT; } case WM_CTLCOLORSTATIC: { HDC hdc = (HDC)wParam; SetBkMode(hdc, TRANSPARENT); HWND ctl = (HWND)lParam; if (ctl == hStaticApplied || ctl == hStaticCPS) SetTextColor(hdc, CLR_TEXT); else SetTextColor(hdc, CLR_MUTED); return (LRESULT)g_brBg; } case WM_CTLCOLORBTN: { HDC hdc = (HDC)wParam; SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, CLR_TEXT); return (LRESULT)g_brBg; } case WM_CTLCOLOREDIT: { HDC hdc = (HDC)wParam; SetBkColor(hdc, CLR_PANEL_2); SetTextColor(hdc, CLR_TEXT); return (LRESULT)g_brEdit; } case WM_DRAWITEM: { DrawOwnerButton((DRAWITEMSTRUCT*)lParam); return TRUE; } case WM_NOTIFY: { LPNMHDR hdr = (LPNMHDR)lParam; if (hdr->idFrom == ID_LISTVIEW && hdr->code == NM_CUSTOMDRAW) { LPNMLVCUSTOMDRAW cd = (LPNMLVCUSTOMDRAW)lParam; if (cd->nmcd.dwDrawStage == CDDS_PREPAINT) { SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW); return TRUE; } if (cd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { cd->clrText = CLR_TEXT; cd->clrTextBk = CLR_PANEL; SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, CDRF_DODEFAULT); return TRUE; } } return 0; } case WM_HOTKEY: { if (wParam == ID_HOTKEY_F6) { ToggleClicking(); } else if (wParam == ID_HOTKEY_F7) { SetVirtualMouseFromCursor(); } return 0; } case WM_COMMAND: { switch (LOWORD(wParam)) { case ID_BTN_REFRESH: RefreshWindowList(); break; case ID_BTN_APPLY_WINDOWS: UpdateSelectedWindows(); break; case ID_BTN_APPLY_CPS: ApplyCpsFromEdit(); break; case ID_CHK_VIRTUAL: { LRESULT state = SendMessageW(hChkVirtual, BM_GETCHECK, 0, 0); bool enabled = state == BST_CHECKED; g_useVirtualMouse.store(enabled); if (enabled) { SetVirtualMouseFromCursor(); CreateVirtualMouse(); MoveVirtualMouseWindow(); } else { DestroyVirtualMouse(); } break; } case ID_BTN_MIN: ShowWindow(hwnd, SW_MINIMIZE); break; case ID_BTN_MAX: if (IsZoomed(hwnd)) ShowWindow(hwnd, SW_RESTORE); else ShowWindow(hwnd, SW_MAXIMIZE); break; case ID_BTN_CLOSE: DestroyWindow(hwnd); break; } return 0; } case WM_DESTROY: { g_clicking.store(false); UnregisterHotKey(hwnd, ID_HOTKEY_F6); UnregisterHotKey(hwnd, ID_HOTKEY_F7); DestroyVirtualMouse(); if (g_font) DeleteObject(g_font); if (g_fontBold) DeleteObject(g_fontBold); if (g_fontTitle) DeleteObject(g_fontTitle); if (g_brBg) DeleteObject(g_brBg); if (g_brPanel) DeleteObject(g_brPanel); if (g_brEdit) DeleteObject(g_brEdit); PostQuitMessage(0); return 0; } } return DefWindowProcW(hwnd, msg, wParam, lParam);
}
// =====================================================
// Entry point
// =====================================================
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
SetProcessDPIAware();
textINITCOMMONCONTROLSEX icex{}; icex.dwSize = sizeof(icex); icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES; InitCommonControlsEx(&icex); WNDCLASSW vm{}; vm.lpfnWndProc = VirtualMouseProc; vm.hInstance = hInstance; vm.lpszClassName = L"RedClickerVirtualMouse"; vm.hCursor = LoadCursorW(nullptr, IDC_ARROW); RegisterClassW(&vm); WNDCLASSW wc{}; wc.lpfnWndProc = MainProc; wc.hInstance = hInstance; wc.lpszClassName = L"RedClickerMainWindow"; wc.hCursor = LoadCursorW(nullptr, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); if (!RegisterClassW(&wc)) { MessageBoxW(nullptr, L"Не удалось зарегистрировать класс окна.", L"Ошибка", MB_OK | MB_ICONERROR); return 0; } int x = (GetSystemMetrics(SM_CXSCREEN) - APP_W) / 2; int y = (GetSystemMetrics(SM_CYSCREEN) - APP_H) / 2; g_mainHwnd = CreateWindowExW( WS_EX_APPWINDOW, L"RedClickerMainWindow", L"РедКликер", WS_POPUP | WS_MINIMIZEBOX, x, y, APP_W, APP_H, nullptr, nullptr, hInstance, nullptr ); if (!g_mainHwnd) { MessageBoxW(nullptr, L"Не удалось создать окно.", L"Ошибка", MB_OK | MB_ICONERROR); return 0; } HRGN rgn = CreateRoundRectRgn(0, 0, APP_W + 1, APP_H + 1, 18, 18); SetWindowRgn(g_mainHwnd, rgn, TRUE); ShowWindow(g_mainHwnd, nCmdShow); UpdateWindow(g_mainHwnd); MSG msg{}; while (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
return wWinMain(hInstance, hPrevInstance, GetCommandLineW(), nCmdShow);
}
вот код у него есть пару проблем
1 в списке окон сделай чтобы отоброжалась иконка
2 убери пояснение про f7 и нажатие на f7 но виртуальную мышь оставь рабочей но ток через галочку
3 добавь в начале выбор экран и есле экран то сразу тпс и тд а есле окна то будет то что есть щас
4 на 'виртуальная мышь надо чтобы небыло красной точки когда выбрал + нажатие ток на галочку а не тест
5 сделай ред кликер на англиском и сделай более красивый шрифт
6 самое гдлавное когда я ставлю 10к кпс он выдаёт ток 1000 а над чтоб 10к а он может ведб прототип выдавал 30к но я потерял код прототипа но не суть и + есле ты выбрал больше 10
Да, понял. Ниже — конкретные правки, которые нужно вставить в твой код. Самое важное по CPS: я убрал лишние расходы в оконном режиме, добавил настоящий режим Screen / Windows, убрал F7, оставил виртуальную мышь только через галочку и добавил иконки в список окон.
winmmПосле #include <cwchar> добавь:
cpp#include <mmsystem.h>
После #pragma comment(lib, "Comctl32.lib") добавь:
cpp#pragma comment(lib, "winmm.lib")
После:
cppstd::atomic<bool> g_useVirtualMouse(false);
добавь:
cppenum class ClickMode { Screen = 0, Windows = 1 }; std::atomic<int> g_clickMode((int)ClickMode::Screen); HIMAGELIST g_windowIcons = nullptr;
После:
cppHWND hChkVirtual = nullptr;
добавь:
cppHWND hStaticVirtualText = nullptr; HWND hRadioScreen = nullptr; HWND hRadioWindows = nullptr;
В enum добавь:
cppID_RADIO_SCREEN = 111, ID_RADIO_WINDOWS = 112, ID_STATIC_VIRTUALTEXT = 113,
А вот это можно удалить или больше не использовать:
cppID_HOTKEY_F7 = 110,
Добавь к прототипам:
cppvoid UpdateModeUi(); HICON GetWindowSmallIcon(HWND hwnd); void PostWindowClickBatch(HWND hwnd, POINT screenPoint, int amount);
Добавь эту функцию где-нибудь рядом с EnumWindowsProc:
cppHICON GetWindowSmallIcon(HWND hwnd) { HICON icon = (HICON)SendMessageW(hwnd, WM_GETICON, ICON_SMALL2, 0); if (!icon) icon = (HICON)SendMessageW(hwnd, WM_GETICON, ICON_SMALL, 0); if (!icon) icon = (HICON)GetClassLongPtrW(hwnd, GCLP_HICONSM); if (!icon) icon = (HICON)GetClassLongPtrW(hwnd, GCLP_HICON); if (!icon) icon = LoadIconW(nullptr, IDI_APPLICATION); return icon; }
Теперь замени функцию RefreshWindowList() на эту:
cppvoid RefreshWindowList() { g_windowList.clear(); EnumWindows(EnumWindowsProc, 0); std::sort(g_windowList.begin(), g_windowList.end(), [](const WindowItem& a, const WindowItem& b) { return a.title < b.title; }); ListView_DeleteAllItems(hListView); if (g_windowIcons) { ImageList_Destroy(g_windowIcons); g_windowIcons = nullptr; } g_windowIcons = ImageList_Create( 16, 16, ILC_COLOR32 | ILC_MASK, (int)g_windowList.size(), 16 ); ListView_SetImageList(hListView, g_windowIcons, LVSIL_SMALL); LVCOLUMNW col{}; col.mask = LVCF_TEXT | LVCF_WIDTH; col.pszText = const_cast<LPWSTR>(L"Windows"); col.cx = 850; if (Header_GetItemCount(ListView_GetHeader(hListView)) == 0) ListView_InsertColumn(hListView, 0, &col); else ListView_SetColumn(hListView, 0, &col); for (int i = 0; i < (int)g_windowList.size(); ++i) { HICON srcIcon = GetWindowSmallIcon(g_windowList[i].hwnd); HICON copyIcon = (HICON)CopyImage( srcIcon, IMAGE_ICON, 16, 16, LR_COPYFROMRESOURCE ); int imageIndex = -1; if (copyIcon) { imageIndex = ImageList_AddIcon(g_windowIcons, copyIcon); DestroyIcon(copyIcon); } else if (srcIcon) { imageIndex = ImageList_AddIcon(g_windowIcons, srcIcon); } LVITEMW item{}; item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; item.iItem = i; item.iSubItem = 0; item.pszText = const_cast<LPWSTR>(g_windowList[i].title.c_str()); item.lParam = reinterpret_cast<LPARAM>(g_windowList[i].hwnd); item.iImage = imageIndex; ListView_InsertItem(hListView, &item); } { std::lock_guard<std::mutex> lock(g_windowsMutex); g_selectedWindows.clear(); } UpdateAppliedText(); }
В WM_CREATE удали:
cppRegisterHotKey(hwnd, ID_HOTKEY_F7, 0, VK_F7);
В WM_HOTKEY замени блок на:
cppcase WM_HOTKEY: { if (wParam == ID_HOTKEY_F6) { ToggleClicking(); } return 0; }
В WM_DESTROY удали:
cppUnregisterHotKey(hwnd, ID_HOTKEY_F7);
Функции CreateVirtualMouse, DestroyVirtualMouse, MoveVirtualMouseWindow можешь оставить, но больше не вызывай их.
Замени SetVirtualMouseFromCursor() на это:
cppvoid SetVirtualMouseFromCursor() { POINT p{}; GetCursorPos(&p); g_virtualX.store(p.x); g_virtualY.store(p.y); }
В WM_CREATE замени создание hChkVirtual на два элемента: маленький checkbox и отдельный текст.
Было:
cpphChkVirtual = CreateWindowW( L"BUTTON", L" Виртуальная мышь", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 28, 694, 260, 30, hwnd, (HMENU)ID_CHK_VIRTUAL, GetModuleHandleW(nullptr), nullptr );
Сделай так:
cpphChkVirtual = CreateWindowW( L"BUTTON", L"", WS_CHILD | WS_VISIBLE | BS_CHECKBOX, 28, 694, 24, 24, hwnd, (HMENU)ID_CHK_VIRTUAL, GetModuleHandleW(nullptr), nullptr ); hStaticVirtualText = CreateWindowW( L"STATIC", L"Virtual mouse", WS_CHILD | WS_VISIBLE, 62, 696, 220, 28, hwnd, (HMENU)ID_STATIC_VIRTUALTEXT, GetModuleHandleW(nullptr), nullptr );
И в массив controls[] добавь hStaticVirtualText.
В WM_CREATE, например после кнопок закрытия окна, добавь:
cpphRadioScreen = CreateWindowW( L"BUTTON", L"Screen", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 780, 100, 100, 26, hwnd, (HMENU)ID_RADIO_SCREEN, GetModuleHandleW(nullptr), nullptr ); hRadioWindows = CreateWindowW( L"BUTTON", L"Windows", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 880, 100, 110, 26, hwnd, (HMENU)ID_RADIO_WINDOWS, GetModuleHandleW(nullptr), nullptr ); CheckDlgButton(hwnd, ID_RADIO_SCREEN, BST_CHECKED);
И в массив controls[] добавь:
cpphRadioScreen, hRadioWindows,
Добавь функцию:
cppvoid UpdateModeUi() { bool windowsMode = g_clickMode.load() == (int)ClickMode::Windows; ShowWindow(hBtnRefresh, windowsMode ? SW_SHOW : SW_HIDE); ShowWindow(hBtnApplyWindows, windowsMode ? SW_SHOW : SW_HIDE); ShowWindow(hStaticApplied, windowsMode ? SW_SHOW : SW_HIDE); ShowWindow(hListView, windowsMode ? SW_SHOW : SW_HIDE); if (g_mainHwnd) InvalidateRect(g_mainHwnd, nullptr, TRUE); }
В конце WM_CREATE, после RefreshWindowList();, добавь:
cppUpdateModeUi();
WM_COMMANDВнутри case WM_COMMAND: добавь обработку режимов:
cppcase ID_RADIO_SCREEN: g_clickMode.store((int)ClickMode::Screen); UpdateModeUi(); break; case ID_RADIO_WINDOWS: g_clickMode.store((int)ClickMode::Windows); UpdateModeUi(); break;
И замени обработку ID_CHK_VIRTUAL на эту:
cppcase ID_CHK_VIRTUAL: { LRESULT state = SendMessageW(hChkVirtual, BM_GETCHECK, 0, 0); bool enabled = state != BST_CHECKED; SendMessageW(hChkVirtual, BM_SETCHECK, enabled ? BST_CHECKED : BST_UNCHECKED, 0); g_useVirtualMouse.store(enabled); if (enabled) { SetVirtualMouseFromCursor(); } else { DestroyVirtualMouse(); } break; }
Теперь текст рядом с галочкой не нажимается, а виртуальная точка ставится только когда ты включаешь checkbox.
В CreateFontW замени "Segoe UI" на:
cppL"Segoe UI Variable Text"
А для заголовка:
cppL"Segoe UI Variable Display"
Например:
cppg_fontTitle = CreateFontW( 28, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI Variable Display" );
В DrawTitleLogo() замени текст:
cppSetTextColor(hdc, CLR_ACCENT); TextOutW(hdc, 84, 27, L"Red", 3); SetTextColor(hdc, CLR_TEXT); TextOutW(hdc, 130, 27, L"Clicker", 7);
В wWinMain замени title:
cppL"Red Clicker",
Кнопки лучше сделать так:
cppL"Refresh windows" L"Apply windows" L"Apply CPS"
hStaticHint сделай без F7:
cpphStaticHint = CreateWindowW( L"STATIC", L"F6: start / stop", WS_CHILD | WS_VISIBLE, 320, 696, 500, 28, hwnd, nullptr, GetModuleHandleW(nullptr), nullptr );
И в ToggleClicking() замени тексты на:
cppif (now) { std::thread(ClickLoop).detach(); SetWindowTextW(hStaticHint, L"F6: stop"); } else { SetWindowTextW(hStaticHint, L"F6: start / stop"); }
Замени PostWindowClick() на батчевую функцию:
cppvoid PostWindowClickBatch(HWND hwnd, POINT screenPoint, int amount) { if (!IsWindow(hwnd) || amount <= 0) return; POINT clientPoint = screenPoint; ScreenToClient(hwnd, &clientPoint); LPARAM lp = MakeMouseLParam(clientPoint); PostMessageW(hwnd, WM_MOUSEMOVE, 0, lp); for (int i = 0; i < amount; ++i) { PostMessageW(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lp); PostMessageW(hwnd, WM_LBUTTONUP, 0, lp); } }
Теперь замени SendGlobalClickBatch() на это:
cppvoid SendGlobalClickBatch(int clicks) { const int MAX_BATCH = 512; while (clicks > 0) { int batch = std::min(clicks, MAX_BATCH); std::vector<INPUT> inputs(batch * 2); ZeroMemory(inputs.data(), inputs.size() * sizeof(INPUT)); for (int i = 0; i < batch; ++i) { inputs[i * 2].type = INPUT_MOUSE; inputs[i * 2].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[i * 2 + 1].type = INPUT_MOUSE; inputs[i * 2 + 1].mi.dwFlags = MOUSEEVENTF_LEFTUP; } SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT)); clicks -= batch; } }
Замени SendVirtualClickBatch() на это:
cppvoid SendVirtualClickBatch(POINT p, int clicks) { const int MAX_BATCH = 512; int vx = GetSystemMetrics(SM_XVIRTUALSCREEN); int vy = GetSystemMetrics(SM_YVIRTUALSCREEN); int vw = GetSystemMetrics(SM_CXVIRTUALSCREEN); int vh = GetSystemMetrics(SM_CYVIRTUALSCREEN); if (vw <= 1 || vh <= 1) return; LONG xAbs = (LONG)(((double)(p.x - vx) * 65535.0) / (double)(vw - 1)); LONG yAbs = (LONG)(((double)(p.y - vy) * 65535.0) / (double)(vh - 1)); bool firstBatch = true; while (clicks > 0) { int batch = std::min(clicks, MAX_BATCH); int offset = firstBatch ? 1 : 0; std::vector<INPUT> inputs(offset + batch * 2); ZeroMemory(inputs.data(), inputs.size() * sizeof(INPUT)); if (firstBatch) { inputs[0].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK; inputs[0].mi.dx = xAbs; inputs[0].mi.dy = yAbs; } for (int i = 0; i < batch; ++i) { inputs[offset + i * 2].type = INPUT_MOUSE; inputs[offset + i * 2].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; inputs[offset + i * 2 + 1].type = INPUT_MOUSE; inputs[offset + i * 2 + 1].mi.dwFlags = MOUSEEVENTF_LEFTUP; } SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT)); firstBatch = false; clicks -= batch; } }
Теперь замени DoClickBurst() полностью:
cppvoid DoClickBurst(int amount) { if (amount <= 0) return; bool virtualMode = g_useVirtualMouse.load(); int mode = g_clickMode.load(); POINT realPos{}; GetCursorPos(&realPos); POINT clickPos = virtualMode ? GetVirtualPoint() : realPos; if (mode == (int)ClickMode::Windows) { std::vector<HWND> targets; { std::lock_guard<std::mutex> lock(g_windowsMutex); targets = g_selectedWindows; } if (targets.empty()) return; for (HWND hwnd : targets) { PostWindowClickBatch(hwnd, clickPos, amount); } return; } if (virtualMode) SendVirtualClickBatch(clickPos, amount); else SendGlobalClickBatch(amount); }
И замени ClickLoop() на это:
cppvoid ClickLoop() { timeBeginPeriod(1); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); LARGE_INTEGER freq{}; LARGE_INTEGER last{}; LARGE_INTEGER now{}; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&last); double accumulator = 0.0; while (g_clicking.load(std::memory_order_relaxed)) { QueryPerformanceCounter(&now); double elapsed = double(now.QuadPart - last.QuadPart) / double(freq.QuadPart); last = now; int localCps = g_cps.load(std::memory_order_relaxed); if (localCps < 1) localCps = 1; accumulator += elapsed * double(localCps); int due = (int)accumulator; if (due > 0) { int maxBurst = std::clamp(localCps / 10, 64, 4096); if (due > maxBurst) due = maxBurst; accumulator -= due; DoClickBurst(due); } else { if (localCps >= 5000) { YieldProcessor(); } else { Sleep(0); } } } timeEndPeriod(1); }
После этих правок код будет пытаться реально выдавать 10k+ событий, но есть ограничение: не каждое приложение примет 10 000 кликов в секунду.
Особенно:
PostMessage;SendInput может отправлять много событий, но целевая программа может обработать только часть.То есть если твой старый прототип показывал 30k, скорее всего он либо работал через оконные сообщения, либо счётчик мерил “отправленные клики”, а не реально обработанные целевой программой.
Для максимального CPS лучше использовать: