在window上使用c++控制鼠标点击,实现的exe

上代码,编译方式在代码中已经提示。

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;
}
相关推荐
@我漫长的孤独流浪1 小时前
计算机系统核心概念与性能优化全解析
算法·计算机外设
June`2 小时前
高并发内存池如何实现
c++·tcmalloc·内存池
ComputerInBook2 小时前
C++ 关键字 constexpr 和 consteval 之注意事项
开发语言·c++·constexpr·consteval
米啦啦.2 小时前
STL(标准模板库)
开发语言·c++·stl
咩咦2 小时前
C++学习笔记08:指针和引用的区别
c++·学习笔记·指针·引用·指针和引用
洛水水3 小时前
【力扣100题】34.二叉搜索树中第K小的元素
c++·算法·leetcode
许长安3 小时前
gRPC Keepalive 机制
c++·经验分享·笔记·rpc
wangjialelele3 小时前
Linux SystemV 消息队列 + 责任链模式:实现客户端消息处理流水线
linux·服务器·c语言·网络·c++·责任链模式
黑白园3 小时前
STM32F103ZET6移植-电机2804-驱动板SimpleFOC Mini实现速度开环_位置开环控制(一、硬件介绍及接线)
stm32·单片机·嵌入式硬件