上代码,编译方式在代码中已经提示。
cpp
/**
* 后台自动点击工具 v3 - 增加坐标拾取功能
* 基于 Win32 API + Interception 驱动
*
* 编译(MSVC):
* cl autoclicker_v3.cpp interception.lib user32.lib gdi32.lib comctl32.lib /subsystem:windows
*
* 编译(MinGW):
* g++ autoclicker_v3.cpp -L. -linterception -luser32 -lgdi32 -lcomctl32 -mwindows -o AutoClicker.exe
*/
#define UNICODE
#define _UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#include <string>
#include <atomic>
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "interception.h"
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
// ─── 控件 ID ──────────────────────────────────────────────────────────────────
#define ID_BTN_START 101
#define ID_BTN_STOP 102
#define ID_BTN_ADD 103
#define ID_BTN_CLEAR 104
#define ID_BTN_SAVE 105
#define ID_BTN_PICK 106 // 坐标拾取按钮
#define ID_EDIT_X 201
#define ID_EDIT_Y 202
#define ID_EDIT_INTERVAL 203
#define ID_EDIT_REPEAT 204
#define ID_COMBO_BUTTON 205
#define ID_EDIT_SCHED_H 206
#define ID_EDIT_SCHED_M 207
#define ID_EDIT_SCHED_S 208
#define ID_CHECK_SCHED 209
#define ID_LIST_TASKS 301
#define ID_STATUS 401
#define ID_TIMER_COUNTDOWN 1001
#define ID_TIMER_PICK 1002 // 拾取时的轮询定时器
#define WM_UPDATE_STATUS (WM_USER + 100) // 自定义消息:更新状态栏
#define WM_TASK_COMPLETED (WM_USER + 101) // 自定义消息:任务完成
// ─── 颜色 ────────────────────────────────────────────────────────────────────
#define CLR_BG RGB(18, 18, 24)
#define CLR_PANEL RGB(28, 28, 38)
#define CLR_ACCENT RGB(99, 179, 237)
#define CLR_TEXT RGB(220, 220, 235)
#define CLR_SUBTEXT RGB(120, 120, 145)
#define CLR_BORDER RGB(50, 50, 68)
#define CLR_YELLOW RGB(255, 214, 90)
#define CLR_GREEN RGB(72, 199, 142)
#define CONFIG_FILE L"autoclicker.cfg"
// ─── 任务结构 ─────────────────────────────────────────────────────────────────
struct ClickTask {
int x, y;
int interval_ms;
int repeat;
bool right_btn;
};
// ─── 全局状态 ─────────────────────────────────────────────────────────────────
HWND g_hwnd;
HWND g_hList, g_hStatus;
HWND g_hEditX, g_hEditY, g_hEditInterval, g_hEditRepeat, g_hComboBtn;
HWND g_hBtnStart, g_hBtnStop, g_hBtnSave, g_hBtnPick;
HWND g_hSchedH, g_hSchedM, g_hSchedS, g_hCheckSched;
std::vector<ClickTask> g_tasks;
std::atomic<bool> g_running(false);
std::atomic<bool> g_picking(false); // 是否处于坐标拾取模式
HANDLE g_worker = nullptr;
InterceptionContext g_ctx = NULL;
InterceptionDevice g_device = INTERCEPTION_MOUSE(0);
HFONT g_fontMain, g_fontMono, g_fontTitle, g_fontSmall;
HBRUSH g_brBg, g_brPanel;
// ─── Interception ─────────────────────────────────────────────────────────────
void ic_click(bool right_btn) {
if (!g_ctx) return;
InterceptionMouseStroke s = {};
s.state = right_btn ? INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN : INTERCEPTION_MOUSE_LEFT_BUTTON_DOWN;
interception_send(g_ctx, g_device, (InterceptionStroke*)&s, 1);
Sleep(8);
s.state = right_btn ? INTERCEPTION_MOUSE_RIGHT_BUTTON_UP : INTERCEPTION_MOUSE_LEFT_BUTTON_UP;
interception_send(g_ctx, g_device, (InterceptionStroke*)&s, 1);
}
void ic_move(int x, int y) {
if (!g_ctx) return;
int sw = GetSystemMetrics(SM_CXSCREEN);
int sh = GetSystemMetrics(SM_CYSCREEN);
InterceptionMouseStroke s = {};
s.state = INTERCEPTION_MOUSE_MOVE_ABSOLUTE;
s.x = (x * 65535) / sw;
s.y = (y * 65535) / sh;
interception_send(g_ctx, g_device, (InterceptionStroke*)&s, 1);
}
// ─── 工具函数 ─────────────────────────────────────────────────────────────────
int GetEditInt(HWND h, int def = 0) {
wchar_t buf[32]; GetWindowTextW(h, buf, 32);
try { return std::stoi(buf); } catch (...) { return def; }
}
void SetEditInt(HWND h, int val) {
SetWindowTextW(h, std::to_wstring(val).c_str());
}
// ─── 保存 / 加载配置 ──────────────────────────────────────────────────────────
static std::string wstring_to_string(const std::wstring& wstr) {
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL);
std::string str(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, NULL, NULL);
return str;
}
void SaveConfig() {
std::string filename = wstring_to_string(CONFIG_FILE);
std::wofstream f(filename.c_str());
if (!f) return;
f << L"sched_enabled=" << (IsDlgButtonChecked(g_hwnd, ID_CHECK_SCHED) ? 1 : 0) << L"\n";
f << L"sched_h=" << GetEditInt(g_hSchedH) << L"\n";
f << L"sched_m=" << GetEditInt(g_hSchedM) << L"\n";
f << L"sched_s=" << GetEditInt(g_hSchedS) << L"\n";
f << L"task_count=" << g_tasks.size() << L"\n";
for (int i = 0; i < (int)g_tasks.size(); i++) {
auto& t = g_tasks[i];
f << L"task_" << i << L"="
<< t.x << L"," << t.y << L","
<< t.interval_ms << L","
<< t.repeat << L","
<< (t.right_btn ? 1 : 0) << L"\n";
}
SetWindowTextW(g_hStatus, L"✓ 配置已保存");
}
void LoadConfig() {
std::string filename = wstring_to_string(CONFIG_FILE);
std::wifstream f(filename.c_str());
if (!f) return;
std::wstring line;
g_tasks.clear();
while (std::getline(f, line)) {
auto sep = line.find(L'=');
if (sep == std::wstring::npos) continue;
std::wstring key = line.substr(0, sep);
std::wstring val = line.substr(sep + 1);
auto wi = [](const std::wstring& v, int d = 0) -> int {
try { return std::stoi(v); } catch (...) { return d; }
};
if (key == L"sched_enabled") CheckDlgButton(g_hwnd, ID_CHECK_SCHED, wi(val) ? BST_CHECKED : BST_UNCHECKED);
else if (key == L"sched_h") SetWindowTextW(g_hSchedH, val.c_str());
else if (key == L"sched_m") SetWindowTextW(g_hSchedM, val.c_str());
else if (key == L"sched_s") SetWindowTextW(g_hSchedS, val.c_str());
else if (key.substr(0, 5) == L"task_") {
std::wistringstream ss(val); std::wstring tok;
std::vector<int> nums;
while (std::getline(ss, tok, L',')) nums.push_back(wi(tok));
if (nums.size() >= 5) {
ClickTask t;
t.x = nums[0]; t.y = nums[1];
t.interval_ms = nums[2]; t.repeat = nums[3];
t.right_btn = nums[4] != 0;
g_tasks.push_back(t);
}
}
}
}
// ─── 刷新任务列表 ─────────────────────────────────────────────────────────────
void RefreshList() {
SendMessageW(g_hList, LB_RESETCONTENT, 0, 0);
for (int i = 0; i < (int)g_tasks.size(); i++) {
auto& t = g_tasks[i];
std::wstring line =
L"#" + std::to_wstring(i + 1) +
L" (" + std::to_wstring(t.x) + L", " + std::to_wstring(t.y) + L")" +
L" " + std::to_wstring(t.interval_ms) + L"ms" +
L" × " + (t.repeat == -1 ? L"∞" : std::to_wstring(t.repeat)) +
L" " + (t.right_btn ? L"右键" : L"左键");
SendMessageW(g_hList, LB_ADDSTRING, 0, (LPARAM)line.c_str());
}
}
// ─── 坐标拾取:开始 ───────────────────────────────────────────────────────────
void StartPicking() {
g_picking = true;
SetWindowTextW(g_hBtnPick, L"🔴 拾取中...");
SetWindowTextW(g_hStatus, L"▶ 请点击目标位置(左键或右键均可) ESC = 取消");
// 每 50ms 轮询一次鼠标按键状态
SetTimer(g_hwnd, ID_TIMER_PICK, 50, NULL);
}
// ─── 坐标拾取:结束 ───────────────────────────────────────────────────────────
void StopPicking(bool cancelled = false) {
g_picking = false;
KillTimer(g_hwnd, ID_TIMER_PICK);
SetWindowTextW(g_hBtnPick, L"🎯 拾取坐标");
if (cancelled)
SetWindowTextW(g_hStatus, L"拾取已取消");
}
// ─── 倒计时显示 ───────────────────────────────────────────────────────────────
void UpdateCountdown() {
if (g_running || g_picking) return;
if (!IsDlgButtonChecked(g_hwnd, ID_CHECK_SCHED)) return;
SYSTEMTIME now; GetLocalTime(&now);
int target = GetEditInt(g_hSchedH) * 3600 + GetEditInt(g_hSchedM) * 60 + GetEditInt(g_hSchedS);
int cur = now.wHour * 3600 + now.wMinute * 60 + now.wSecond;
int diff = target - cur; if (diff < 0) diff += 86400;
wchar_t buf[64];
swprintf_s(buf, L"等待定时启动 还剩 %02d:%02d:%02d", diff/3600, (diff%3600)/60, diff%60);
SetWindowTextW(g_hStatus, buf);
}
// ─── 工作线程 ─────────────────────────────────────────────────────────────────
DWORD WINAPI worker_thread(LPVOID) {
if (IsDlgButtonChecked(g_hwnd, ID_CHECK_SCHED)) {
while (g_running) {
SYSTEMTIME now; GetLocalTime(&now);
if (now.wHour == GetEditInt(g_hSchedH) &&
now.wMinute == GetEditInt(g_hSchedM) &&
now.wSecond >= GetEditInt(g_hSchedS)) break;
Sleep(500);
}
}
if (!g_running) return 0;
int total = 0;
for (auto& task : g_tasks) {
if (!g_running) break;
int count = 0;
while (g_running && (task.repeat == -1 || count < task.repeat)) {
ic_move(task.x, task.y);
Sleep(15);
ic_click(task.right_btn);
count++;
total++;
// 减少状态栏更新频率,通过消息队列更新
static int last_update = 0;
if (total - last_update >= 10) {
PostMessageW(g_hwnd, WM_UPDATE_STATUS, total, 0);
last_update = total;
}
for (int i = 0; i < task.interval_ms && g_running; i += 50)
Sleep(std::min(50, task.interval_ms - i));
}
}
g_running = false;
PostMessageW(g_hwnd, WM_TASK_COMPLETED, 0, 0);
return 0;
}
// ─── 窗口过程 ─────────────────────────────────────────────────────────────────
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_CREATE: {
g_fontTitle = CreateFontW(20, 0, 0, 0, FW_BOLD, 0,0,0, DEFAULT_CHARSET,0,0, CLEARTYPE_QUALITY,0, L"Segoe UI");
g_fontMain = CreateFontW(15, 0, 0, 0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,0,0, CLEARTYPE_QUALITY,0, L"Segoe UI");
g_fontSmall = CreateFontW(13, 0, 0, 0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,0,0, CLEARTYPE_QUALITY,0, L"Segoe UI");
g_fontMono = CreateFontW(14, 0, 0, 0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,0,0, CLEARTYPE_QUALITY,0, L"Consolas");
g_brBg = CreateSolidBrush(CLR_BG);
g_brPanel = CreateSolidBrush(CLR_PANEL);
auto MkLabel = [&](const wchar_t* t, int x, int y, int w, HFONT f = NULL) {
HWND h = CreateWindowW(L"STATIC", t, WS_CHILD|WS_VISIBLE, x, y, w, 18, hwnd, NULL, NULL, NULL);
SendMessageW(h, WM_SETFONT, (WPARAM)(f?f:g_fontSmall), TRUE); return h;
};
auto MkEdit = [&](int id, int x, int y, int w, const wchar_t* def, DWORD extra = ES_NUMBER) {
HWND h = CreateWindowW(L"EDIT", def, WS_CHILD|WS_VISIBLE|WS_BORDER|extra,
x, y, w, 24, hwnd, (HMENU)(INT_PTR)id, NULL, NULL);
SendMessageW(h, WM_SETFONT, (WPARAM)g_fontMain, TRUE); return h;
};
auto MkBtn = [&](const wchar_t* t, int id, int x, int y, int w, int h_=28) {
HWND h = CreateWindowW(L"BUTTON", t, WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
x, y, w, h_, hwnd, (HMENU)(INT_PTR)id, NULL, NULL);
SendMessageW(h, WM_SETFONT, (WPARAM)g_fontMain, TRUE); return h;
};
// ── 定时区 ──────────────────────────────────────────────────────
MkLabel(L"定时启动", 20, 52, 70, g_fontMain);
g_hCheckSched = CreateWindowW(L"BUTTON", L"启用定时",
WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 100, 51, 80, 18, hwnd, (HMENU)ID_CHECK_SCHED, NULL, NULL);
SendMessageW(g_hCheckSched, WM_SETFONT, (WPARAM)g_fontSmall, TRUE);
g_hSchedH = MkEdit(ID_EDIT_SCHED_H, 185, 52, 38, L"12");
MkLabel(L"时", 185+38+5, 52, 14);
g_hSchedM = MkEdit(ID_EDIT_SCHED_M, 185+38+5+14, 52, 38, L"00");
MkLabel(L"分", 185+38+5+14+38+5, 52, 14);
g_hSchedS = MkEdit(ID_EDIT_SCHED_S, 185+38+5+14+38+5+14+5, 52, 38, L"00");
MkLabel(L"秒", 185+38+5+14+38+5+14+5+38+5, 52, 14);
MkLabel(L"( 24小时制 )", 185+38+5+14+38+5+14+5+38+5+14+5, 52, 100, g_fontSmall);
// ── 坐标拾取区 ──────────────────────────────────────────────────
MkLabel(L"X 坐标", 20, 108, 55);
MkLabel(L"Y 坐标", 95, 108, 55);
g_hEditX = MkEdit(ID_EDIT_X, 20, 126, 65, L"500");
g_hEditY = MkEdit(ID_EDIT_Y, 95, 126, 65, L"400");
// 拾取按钮放在 XY 旁边
g_hBtnPick = MkBtn(L"🎯 拾取坐标", ID_BTN_PICK, 172, 124, 110, 28);
MkLabel(L"间隔ms", 295, 108, 65);
MkLabel(L"次数(0=∞)", 370, 108, 80);
MkLabel(L"按键", 458, 108, 45);
g_hEditInterval = MkEdit(ID_EDIT_INTERVAL, 295, 126, 65, L"1000");
g_hEditRepeat = MkEdit(ID_EDIT_REPEAT, 370, 126, 75, L"10");
g_hComboBtn = CreateWindowW(L"COMBOBOX", NULL, WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
458, 124, 60, 80, hwnd, (HMENU)ID_COMBO_BUTTON, NULL, NULL);
SendMessageW(g_hComboBtn, WM_SETFONT, (WPARAM)g_fontMain, TRUE);
SendMessageW(g_hComboBtn, CB_ADDSTRING, 0, (LPARAM)L"左键");
SendMessageW(g_hComboBtn, CB_ADDSTRING, 0, (LPARAM)L"右键");
SendMessageW(g_hComboBtn, CB_SETCURSEL, 0, 0);
// ── 按钮行 ──────────────────────────────────────────────────────
MkBtn(L"➕ 添加", ID_BTN_ADD, 20, 166, 85);
MkBtn(L"🗑 清空", ID_BTN_CLEAR, 115, 166, 85);
g_hBtnSave = MkBtn(L"💾 保存", ID_BTN_SAVE, 210, 166, 85);
g_hBtnStart = MkBtn(L"▶ 开始", ID_BTN_START, 320, 166, 80);
g_hBtnStop = MkBtn(L"■ 停止", ID_BTN_STOP, 410, 166, 80);
EnableWindow(g_hBtnStop, FALSE);
// ── 任务列表 ────────────────────────────────────────────────────
g_hList = CreateWindowW(L"LISTBOX", NULL,
WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOSEL,
20, 206, 500, 130, hwnd, (HMENU)ID_LIST_TASKS, NULL, NULL);
SendMessageW(g_hList, WM_SETFONT, (WPARAM)g_fontMono, TRUE);
// ── 状态栏 ──────────────────────────────────────────────────────
g_hStatus = CreateWindowW(L"STATIC", L"初始化中...",
WS_CHILD|WS_VISIBLE|SS_LEFT,
20, 350, 500, 18, hwnd, (HMENU)ID_STATUS, NULL, NULL);
SendMessageW(g_hStatus, WM_SETFONT, (WPARAM)g_fontSmall, TRUE);
// ── 初始化驱动 ──────────────────────────────────────────────────
g_ctx = interception_create_context();
SetWindowTextW(g_hStatus, g_ctx
? L"✓ Interception 驱动已连接 | F8 = 紧急停止"
: L"⚠ 驱动未找到,请安装 Interception 后重启");
LoadConfig(); RefreshList();
SetTimer(hwnd, ID_TIMER_COUNTDOWN, 1000, NULL);
RegisterHotKey(hwnd, 1, 0, VK_F8);
break;
}
case WM_TIMER: {
if (wp == ID_TIMER_COUNTDOWN) {
UpdateCountdown();
}
else if (wp == ID_TIMER_PICK && g_picking) {
// 实时显示当前鼠标坐标
POINT pt; GetCursorPos(&pt);
wchar_t buf[80];
swprintf_s(buf, L"🎯 拾取中 当前坐标: (%d, %d) --- 点击任意键确认 ESC=取消", pt.x, pt.y);
SetWindowTextW(g_hStatus, buf);
// 检测左键或右键按下
bool left_down = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
bool right_down = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
bool esc = (GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0;
if (esc) {
StopPicking(true);
}
else if (left_down || right_down) {
// 捕获坐标,写入输入框
SetEditInt(g_hEditX, pt.x);
SetEditInt(g_hEditY, pt.y);
// 同步设置按键下拉
SendMessageW(g_hComboBtn, CB_SETCURSEL, right_down ? 1 : 0, 0);
StopPicking(false);
wchar_t done[64];
swprintf_s(done, L"✓ 已拾取坐标 (%d, %d) %s", pt.x, pt.y, right_down ? L"右键" : L"左键");
SetWindowTextW(g_hStatus, done);
// 短暂等待,避免立刻再次触发
Sleep(300);
}
}
break;
}
case WM_UPDATE_STATUS: {
wchar_t buf[64];
swprintf_s(buf, L"运行中 已点击: %d 次", (int)wp);
SetWindowTextW(g_hStatus, buf);
break;
}
case WM_TASK_COMPLETED: {
SetWindowTextW(g_hStatus, L"✓ 已完成");
EnableWindow(g_hBtnStart, TRUE);
EnableWindow(g_hBtnStop, FALSE);
break;
}
case WM_COMMAND: {
int id = LOWORD(wp);
if (id == ID_BTN_PICK) {
if (!g_picking) StartPicking();
else StopPicking(true);
}
if (id == ID_BTN_ADD) {
int rep = GetEditInt(g_hEditRepeat, 10);
if (rep < 0) rep = 0; if (rep > 100) rep = 100;
ClickTask t;
t.x = GetEditInt(g_hEditX, 500);
t.y = GetEditInt(g_hEditY, 400);
t.interval_ms = GetEditInt(g_hEditInterval, 1000);
t.repeat = (rep == 0) ? -1 : rep;
t.right_btn = SendMessageW(g_hComboBtn, CB_GETCURSEL, 0, 0) == 1;
g_tasks.push_back(t);
RefreshList();
}
if (id == ID_BTN_CLEAR) { g_tasks.clear(); RefreshList(); }
if (id == ID_BTN_SAVE) { SaveConfig(); }
if (id == ID_BTN_START && !g_running && !g_tasks.empty()) {
g_running = true;
EnableWindow(g_hBtnStart, FALSE);
EnableWindow(g_hBtnStop, TRUE);
if (!IsDlgButtonChecked(g_hwnd, ID_CHECK_SCHED))
SetWindowTextW(g_hStatus, L"运行中...");
if (g_worker) { WaitForSingleObject(g_worker, INFINITE); CloseHandle(g_worker); g_worker = nullptr; }
g_worker = CreateThread(NULL, 0, worker_thread, NULL, 0, NULL);
}
if (id == ID_BTN_STOP) {
g_running = false;
EnableWindow(g_hBtnStart, TRUE);
EnableWindow(g_hBtnStop, FALSE);
SetWindowTextW(g_hStatus, L"已停止");
}
break;
}
case WM_HOTKEY:
if (wp == 1) {
if (g_picking) { StopPicking(true); }
else if (g_running) {
g_running = false;
EnableWindow(g_hBtnStart, TRUE);
EnableWindow(g_hBtnStop, FALSE);
SetWindowTextW(g_hStatus, L"已停止(F8)");
}
}
break;
case WM_CTLCOLORSTATIC:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX: {
HDC hdc = (HDC)wp;
SetTextColor(hdc, CLR_TEXT); SetBkColor(hdc, CLR_PANEL);
return (LRESULT)g_brPanel;
}
case WM_CTLCOLORBTN: {
HDC hdc = (HDC)wp;
SetTextColor(hdc, CLR_TEXT); SetBkColor(hdc, CLR_PANEL);
return (LRESULT)g_brPanel;
}
case WM_PAINT: {
PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps);
RECT rc; GetClientRect(hwnd, &rc);
FillRect(hdc, &rc, g_brBg);
RECT title = {0, 0, rc.right, 40}; FillRect(hdc, &title, g_brPanel);
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdc, g_fontTitle); SetTextColor(hdc, CLR_ACCENT);
TextOutW(hdc, 16, 10, L"⚡ AutoClicker v3", 17);
SelectObject(hdc, g_fontSmall); SetTextColor(hdc, CLR_SUBTEXT);
TextOutW(hdc, 210, 14, L"坐标拾取 · 定时启动 · 配置保存", 17);
HPEN pen = CreatePen(PS_SOLID, 1, CLR_BORDER);
HPEN old = (HPEN)SelectObject(hdc, pen);
auto Line = [&](int y){ MoveToEx(hdc,0,y,NULL); LineTo(hdc,rc.right,y); };
Line(40); Line(96); Line(160); Line(200); Line(340);
SelectObject(hdc, old); DeleteObject(pen);
SetTextColor(hdc, CLR_SUBTEXT); SelectObject(hdc, g_fontSmall);
TextOutW(hdc, 20, 344, L"F8 = 紧急停止 / 取消拾取 | 配置保存至 autoclicker.cfg", 28);
EndPaint(hwnd, &ps); break;
}
case WM_ERASEBKGND: return 1;
case WM_DESTROY:
g_running = false; g_picking = false;
KillTimer(hwnd, ID_TIMER_COUNTDOWN);
KillTimer(hwnd, ID_TIMER_PICK);
if (g_worker) { WaitForSingleObject(g_worker, INFINITE); CloseHandle(g_worker); g_worker = nullptr; }
if (g_ctx) interception_destroy_context(g_ctx);
UnregisterHotKey(hwnd, 1);
DeleteObject(g_fontMain); DeleteObject(g_fontMono);
DeleteObject(g_fontTitle); DeleteObject(g_fontSmall);
DeleteObject(g_brBg); DeleteObject(g_brPanel);
PostQuitMessage(0); break;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
// ─── 入口 ─────────────────────────────────────────────────────────────────────
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) {
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&icex);
WNDCLASSW wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.lpszClassName = L"AutoClickerV3";
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(CLR_BG);
RegisterClassW(&wc);
g_hwnd = CreateWindowExW(WS_EX_APPWINDOW,
L"AutoClickerV3", L"AutoClicker v3",
WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 545, 390,
NULL, NULL, hInst, NULL);
ShowWindow(g_hwnd, SW_SHOW);
UpdateWindow(g_hwnd);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}