Dev-cpp需要链接
-mwindows -static -lcomctl32 -lwinmm -lshell32 -lgdiplus -luxtheme -ld2d1 -lole32 -luuid -luser32 -lgdi32 -lkernel32 -std=c++14
代码(V2.0)
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#include<windows.h>
#include<commctrl.h>
#include<atomic>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<random>
#include<vector>
#include<string>
#include<sstream>
#include<mmsystem.h>
#include<algorithm>
#include<cctype>
#include<mutex>
#include<chrono>
#include<shellapi.h>
#include<gdiplus.h>
#include<uxtheme.h>
#include<d2d1.h>
#include<d2d1helper.h>
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"shell32.lib")
#pragma comment(lib,"gdiplus.lib")
#pragma comment(lib,"uxtheme.lib")
#pragma comment(lib,"d2d1.lib")
#ifdef _MSC_VER
#pragma comment(lib,"winmm.lib")
#endif
using namespace std;
void SetDPIAwareness(){
HMODULE hUser32=GetModuleHandleW(L"user32.dll");
if(hUser32){
typedef BOOL(WINAPI*PFN)(PVOID);
PFN pV2=(PFN)GetProcAddress(hUser32,"SetProcessDPIAwarenessContext");
if(pV2&&pV2((PVOID)-4))return;
}
HMODULE hShcore=LoadLibraryW(L"Shcore.dll");
if(hShcore){
typedef HRESULT(WINAPI*PFN2)(int);
PFN2 p=(PFN2)GetProcAddress(hShcore,"SetProcessDpiAwareness");
if(p&&SUCCEEDED(p(2))){
FreeLibrary(hShcore);
return;
}
FreeLibrary(hShcore);
}
if(hUser32){
typedef BOOL(WINAPI*PFN3)();
PFN3 p=(PFN3)GetProcAddress(hUser32,"SetProcessDPIAware");
if(p)p();
}
}
atomic<bool>isClicking(false);
atomic<bool>shouldExit(false);
atomic<int>clickDelay(100);
atomic<int>clickCount(0);
atomic<double>currentCPS(0.0);
atomic<int>clickButton(0);
atomic<bool>isExtremeMode(false);
atomic<bool>jitterEnabled(false);
atomic<int>jitterRange(10);
atomic<bool>sequenceEnabled(false);
vector<pair<int,int>>clickPoints;
atomic<bool>isRecording(false);
atomic<bool>isPlaying(false);
atomic<int>playbackSpeed(50);
struct RecordedEvent{
int type,x,y,data;
DWORD time;
bool isRelative;
};
vector<RecordedEvent>recordedEvents;
HHOOK mouseHook=nullptr;
HANDLE hPlaybackThread=nullptr;
int lastMoveTime=0;
HHOOK keyboardHook=nullptr;
HWND hwndMain=nullptr;
HWND hwndStatusBar=nullptr;
HWND hwndDelayEdit=nullptr;
HWND hwndDelayLabel=nullptr;
HWND hwndButtonCombo=nullptr;
HWND hwndJitterCheck=nullptr;
HWND hwndJitterEdit=nullptr;
HWND hwndSeqCheck=nullptr;
HWND hwndSeqEdit=nullptr;
HWND hwndRecBtn=nullptr;
HWND hwndPlayBtn=nullptr;
HWND hwndSpeedSlider=nullptr;
HWND hwndStartBtn=nullptr;
HWND hwndStopBtn=nullptr;
HWND hwndExitBtn=nullptr;
HWND hwndRelativeCheck=nullptr;
HWND hwndMacroBtn=nullptr;
HWND hwndTitle1=nullptr,hwndTitle2=nullptr,hwndTitle3=nullptr,hwndTitle4=nullptr;
HWND hwndLblDelay=nullptr,hwndLblButton=nullptr,hwndLblJitter=nullptr,hwndLblCoord=nullptr;
HWND hwndLblSpeed=nullptr,hwndLblMacroHint=nullptr,hwndLblHotkey=nullptr;
#define BASE_W 1080
#define BASE_H 700
#define MIN_SCALE 0.75
#define MAX_FONT_SCALE 1.6
#define EDIT_INSET_X 8
#define EDIT_INSET_Y 2
#define WINUI_RADIUS 4
struct FontSlot{
int baseSize;
int weight;
HFONT hFont;
};
static FontSlot g_fontSlots[7]={
{28,FW_SEMIBOLD,nullptr},{19,FW_NORMAL,nullptr},
{22,FW_BOLD,nullptr}, {19,FW_NORMAL,nullptr},
{21,FW_BOLD,nullptr}, {19,FW_NORMAL,nullptr},
{16,FW_NORMAL,nullptr}
};
#define F_STATUS g_fontSlots[0].hFont
#define F_STATUSINFO g_fontSlots[1].hFont
#define F_DOT g_fontSlots[2].hFont
#define F_NORMAL g_fontSlots[3].hFont
#define F_TITLE g_fontSlots[4].hFont
#define F_BUTTON g_fontSlots[5].hFont
#define F_INPUT g_fontSlots[6].hFont
vector<HWND>g_fontGroups[7];
double g_scale=-1.0;
int g_lastMainFontSize =-1;
int g_lastMacroFontSize=-1;
#define MACRO_BASE_W 900
#define MACRO_BASE_H 500
struct MacroFontSlot{
int baseSize;
int weight;
HFONT hFont;
};
static MacroFontSlot g_macroFontSlots[2]={
{19,FW_NORMAL,nullptr},{19,FW_NORMAL,nullptr}
};
#define MF_NORMAL g_macroFontSlots[0].hFont
#define MF_BUTTON g_macroFontSlots[1].hFont
double g_macroScale=-1.0;
bool g_bRelativeMode=true;
bool g_bRecordingPending=false;
int g_countdownSeconds=3;
UINT g_countdownTimerId=1004;
int g_currentCountdown=0;
HWND hwndMacro=nullptr;
bool g_bMacroRecording=false;
bool g_bMacroPlaying=false;
vector<RecordedEvent>macroEvents;
HHOOK g_macroMouseHook=nullptr;
HHOOK g_macroKeyboardHook=nullptr;
HANDLE hMacroPlayThread=nullptr;
int g_macroPlaySpeed=50;
bool g_bMacroLoop=false;
bool g_bMacroRecordMove=false;
HWND hwndMacroList=nullptr;
HWND hwndMacroPlayBtn=nullptr;
HWND hwndMacroRecBtn=nullptr;
HWND hwndMacroStopRecBtn=nullptr;
HWND hwndMacroStopPlayBtn=nullptr;
HWND hwndMacroLoopCheck=nullptr;
HWND hwndMacroSpeedLabel=nullptr;
HWND hwndMacroSpeedEdit=nullptr;
HWND hwndMacroMoveCheck=nullptr;
atomic<bool>g_bMacroStopPlay(false);
mutex g_macroMutex;
#define TIMER_CPS 1003
#define WM_TRAYICON (WM_USER+100)
#define WM_ADD_COORD (WM_USER+2)
#define WM_MACRO_UPDATE (WM_USER+4)
#define WM_EXECUTE_CMD (WM_USER+5)
#define WM_MACRO_ADD_EVENT (WM_USER+10)
#define TRAY_SHOW 5001
#define TRAY_HIDE 5002
#define TRAY_TOGGLE 5003
#define TRAY_EXIT 5004
#define ID_STATUS_BAR 9001
#define TOGGLE_ANIM_TIMER_ID 0xA100
HBRUSH hBrushWhite=nullptr;
HBRUSH hBrushBg=nullptr;
bool g_bInitDone=false;
bool g_bTrayCreated=false;
NOTIFYICONDATAA g_nid={};
HICON g_hAppIcon=nullptr;
HICON g_hBlankIcon=nullptr;
ULONG_PTR g_gdiplusToken=0;
ID2D1Factory*g_pD2DFactory=nullptr;
bool g_bD2DReady=false;
thread_local mt19937 rng((unsigned int)time(nullptr)^(unsigned int)GetCurrentThreadId());
namespace WinUI{
const Gdiplus::Color Accent (255, 0,103,192);
const Gdiplus::Color AccentPressed(255, 0, 90,158);
const Gdiplus::Color ControlBg (255,253,253,253);
const Gdiplus::Color ControlPress (255,249,249,249);
const Gdiplus::Color ControlHover (255,246,246,246);
const Gdiplus::Color Border (255,229,229,229);
const Gdiplus::Color BorderStrong (255,199,199,199);
const Gdiplus::Color TextPrimary (255, 26, 26, 26);
const Gdiplus::Color TextSecondary(255, 92, 92, 92);
const Gdiplus::Color TextDisabled (255,158,158,158);
const Gdiplus::Color DisabledBg (255,245,245,245);
const Gdiplus::Color DisabledBorder(255,232,232,232);
const Gdiplus::Color StatusBg (255, 32, 60,110);
}
void InitDirect2D(){
if(g_pD2DFactory)return;
HRESULT hr=D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&g_pD2DFactory);
if(SUCCEEDED(hr)&&g_pD2DFactory){
g_bD2DReady=true;
}else{
g_pD2DFactory=nullptr;
g_bD2DReady=false;
}
}
void ShutdownDirect2D(){
if(g_pD2DFactory){
g_pD2DFactory->Release();
g_pD2DFactory=nullptr;
}
g_bD2DReady=false;
}
static bool D2DFillRoundRect(HDC hdc,const RECT&rc,float radius,
COLORREF fill,bool hasFill,
COLORREF border,bool hasBorder,float borderW){
if(!g_bD2DReady||!g_pD2DFactory)return false;
int w=rc.right-rc.left;
int h=rc.bottom-rc.top;
if(w<=0||h<=0)return false;
ID2D1DCRenderTarget*pRT=nullptr;
D2D1_RENDER_TARGET_PROPERTIES props=D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE),
0,0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT);
HRESULT hr=g_pD2DFactory->CreateDCRenderTarget(&props,&pRT);
if(FAILED(hr)||!pRT)return false;
RECT bindRect={rc.left,rc.top,rc.right,rc.bottom};
hr=pRT->BindDC(hdc,&bindRect);
if(FAILED(hr)){
pRT->Release();
return false;
}
pRT->BeginDraw();
pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
float maxR=((float)w<(float)h?(float)w:(float)h)*0.5f;
float r=radius;
if(r>maxR)r=maxR;
if(r<0.0f)r=0.0f;
if(hasFill){
D2D1_ROUNDED_RECT rr=D2D1::RoundedRect(
D2D1::RectF(0.0f,0.0f,(float)w,(float)h),r,r);
ID2D1SolidColorBrush*pBrush=nullptr;
D2D1_COLOR_F c=D2D1::ColorF(
GetRValue(fill)/255.0f,GetGValue(fill)/255.0f,
GetBValue(fill)/255.0f,1.0f);
if(SUCCEEDED(pRT->CreateSolidColorBrush(c,&pBrush))&&pBrush){
pRT->FillRoundedRectangle(rr,pBrush);
pBrush->Release();
}
}
if(hasBorder&&borderW>0.0f){
float halfBw=borderW*0.5f;
float innerW=(float)w-borderW;
float innerH=(float)h-borderW;
float innerR=r-halfBw;
if(innerW<1.0f)innerW=1.0f;
if(innerH<1.0f)innerH=1.0f;
if(innerR<0.5f)innerR=0.5f;
D2D1_ROUNDED_RECT rr=D2D1::RoundedRect(
D2D1::RectF(halfBw,halfBw,halfBw+innerW,halfBw+innerH),
innerR,innerR);
ID2D1SolidColorBrush*pBrush=nullptr;
D2D1_COLOR_F c=D2D1::ColorF(
GetRValue(border)/255.0f,GetGValue(border)/255.0f,
GetBValue(border)/255.0f,1.0f);
if(SUCCEEDED(pRT->CreateSolidColorBrush(c,&pBrush))&&pBrush){
pRT->DrawRoundedRectangle(rr,pBrush,borderW);
pBrush->Release();
}
}
hr=pRT->EndDraw();
pRT->Release();
return SUCCEEDED(hr);
}
static Gdiplus::GraphicsPath*MakeRoundRectPath(Gdiplus::REAL x,Gdiplus::REAL y,
Gdiplus::REAL w,Gdiplus::REAL h,
Gdiplus::REAL radius){
Gdiplus::GraphicsPath*p=new Gdiplus::GraphicsPath();
if(radius<0.5f){
p->AddRectangle(Gdiplus::RectF(x,y,w,h));
return p;
}
Gdiplus::REAL maxR=(w<h?w:h)*0.5f;
if(radius>maxR)radius=maxR;
Gdiplus::REAL d=radius*2.0f;
p->AddArc(x,y,d,d,180.0f,90.0f);
p->AddArc(x+w-d,y,d,d,270.0f,90.0f);
p->AddArc(x+w-d,y+h-d,d,d,0.0f,90.0f);
p->AddArc(x,y+h-d,d,d,90.0f,90.0f);
p->CloseFigure();
return p;
}
static void GdiPlusFillRoundRect(HDC hdc,const RECT&rc,Gdiplus::REAL radius,
Gdiplus::Color fill,Gdiplus::Color border,Gdiplus::REAL borderW){
bool hasFill =(fill.GetA()>0);
bool hasBorder=(border.GetA()>0&&borderW>0);
if(hasFill||hasBorder){
COLORREF fillC =RGB(fill.GetR(), fill.GetG(), fill.GetB());
COLORREF borderC=RGB(border.GetR(),border.GetG(),border.GetB());
if(D2DFillRoundRect(hdc,rc,(float)radius,
fillC,hasFill,
borderC,hasBorder,(float)borderW)){
return;
}
}
Gdiplus::Graphics gfx(hdc);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
gfx.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHalf);
Gdiplus::REAL px=(Gdiplus::REAL)rc.left;
Gdiplus::REAL py=(Gdiplus::REAL)rc.top;
Gdiplus::REAL w =(Gdiplus::REAL)(rc.right-rc.left);
Gdiplus::REAL h =(Gdiplus::REAL)(rc.bottom-rc.top);
if(w<1.0f||h<1.0f)return;
if(hasFill){
Gdiplus::GraphicsPath*pathFill=MakeRoundRectPath(px,py,w,h,radius);
Gdiplus::SolidBrush br(fill);
gfx.FillPath(&br,pathFill);
delete pathFill;
}
if(hasBorder){
Gdiplus::REAL halfBw=borderW*0.5f;
Gdiplus::REAL innerW=w-borderW;
Gdiplus::REAL innerH=h-borderW;
Gdiplus::REAL innerR=radius-halfBw;
if(innerW<1.0f)innerW=1.0f;
if(innerH<1.0f)innerH=1.0f;
if(innerR<0.5f)innerR=0.5f;
Gdiplus::GraphicsPath*pathBorder=MakeRoundRectPath(
px+halfBw,py+halfBw,innerW,innerH,innerR);
Gdiplus::Pen pen(border,borderW);
pen.SetLineJoin(Gdiplus::LineJoinRound);
pen.SetStartCap(Gdiplus::LineCapRound);
pen.SetEndCap(Gdiplus::LineCapRound);
gfx.DrawPath(&pen,pathBorder);
delete pathBorder;
}
}
HICON LoadCustomIcon(){
HINSTANCE hInst=GetModuleHandleA(nullptr);
HICON h=(HICON)LoadImageA(nullptr,"clicker.ico",IMAGE_ICON,
0,0,LR_LOADFROMFILE|LR_DEFAULTCOLOR);
if(h)return h;
h=LoadIconA(hInst,"CLICKER");
if(h)return h;
return LoadIconA(nullptr,IDI_APPLICATION);
}
void RecreateFonts(double s){
for(int i=0;i<7;i++){
if(g_fontSlots[i].hFont){
DeleteObject(g_fontSlots[i].hFont);
g_fontSlots[i].hFont=nullptr;
}
int size=(int)(g_fontSlots[i].baseSize*s+0.5);
if(size<9)size=9;
if(size>80)size=80;
g_fontSlots[i].hFont=CreateFontW(size,0,0,0,g_fontSlots[i].weight,
FALSE,FALSE,FALSE,DEFAULT_CHARSET,
OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,
DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
}
}
void ApplyFonts(){
for(int i=0;i<7;i++){
for(size_t k=0;k<g_fontGroups[i].size();k++){
HWND h=g_fontGroups[i][k];
if(h&&IsWindow(h))SendMessage(h,WM_SETFONT,(WPARAM)g_fontSlots[i].hFont,TRUE);
}
}
}
void RecreateMacroFonts(double s){
for(int i=0;i<2;i++){
if(g_macroFontSlots[i].hFont){
DeleteObject(g_macroFontSlots[i].hFont);
g_macroFontSlots[i].hFont=nullptr;
}
int size=(int)(g_macroFontSlots[i].baseSize*s+0.5);
if(size<9)size=9;
if(size>60)size=60;
g_macroFontSlots[i].hFont=CreateFontW(size,0,0,0,g_macroFontSlots[i].weight,
FALSE,FALSE,FALSE,DEFAULT_CHARSET,
OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,
DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
}
}
void ApplyMacroFonts(){
if(hwndMacroList) SendMessage(hwndMacroList, WM_SETFONT,(WPARAM)MF_NORMAL,TRUE);
if(hwndMacroRecBtn) SendMessage(hwndMacroRecBtn, WM_SETFONT,(WPARAM)MF_BUTTON,TRUE);
if(hwndMacroStopRecBtn) SendMessage(hwndMacroStopRecBtn, WM_SETFONT,(WPARAM)MF_BUTTON,TRUE);
if(hwndMacroPlayBtn) SendMessage(hwndMacroPlayBtn, WM_SETFONT,(WPARAM)MF_BUTTON,TRUE);
if(hwndMacroStopPlayBtn)SendMessage(hwndMacroStopPlayBtn,WM_SETFONT,(WPARAM)MF_BUTTON,TRUE);
if(hwndMacroLoopCheck) SendMessage(hwndMacroLoopCheck, WM_SETFONT,(WPARAM)MF_NORMAL,TRUE);
if(hwndMacroSpeedLabel) SendMessage(hwndMacroSpeedLabel, WM_SETFONT,(WPARAM)MF_NORMAL,TRUE);
if(hwndMacroSpeedEdit) SendMessage(hwndMacroSpeedEdit, WM_SETFONT,(WPARAM)MF_NORMAL,TRUE);
if(hwndMacroMoveCheck) SendMessage(hwndMacroMoveCheck, WM_SETFONT,(WPARAM)MF_NORMAL,TRUE);
}
void ApplyRoundedRegion(HWND h){
(void)h;
}
LRESULT CALLBACK ModernInputProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam,
UINT_PTR uIdSubclass,DWORD_PTR dwRefData){
switch(msg){
case WM_SIZE:
ApplyRoundedRegion(hwnd);
break;
case WM_SETFOCUS:
case WM_KILLFOCUS:
InvalidateRect(GetParent(hwnd),nullptr,FALSE);
break;
case WM_CHAR:
case WM_KEYDOWN:
case WM_PASTE:
case WM_CUT:
if(isClicking.load())return 0;
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd,ModernInputProc,uIdSubclass);
break;
}
return DefSubclassProc(hwnd,msg,wParam,lParam);
}
struct ToggleAnimState{
double pos;
double target;
bool timerRunning;
DWORD animStart;
double animFrom;
double knobScale;
double knobScaleTarget;
bool pressed;
};
static double EaseOutCubic(double t){
if(t<0.0)t=0.0;
if(t>1.0)t=1.0;
double u=1.0-t;
return 1.0-u*u*u;
}
static const DWORD TOGGLE_ANIM_DURATION_MS=167;
static const UINT TOGGLE_ANIM_TIMER_MS =10;
LRESULT CALLBACK RoundCheckBoxProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam,
UINT_PTR uIdSubclass,DWORD_PTR dwRefData){
ToggleAnimState*st=(ToggleAnimState*)dwRefData;
switch(msg){
case WM_LBUTTONDOWN:{
if(!IsWindowEnabled(hwnd)||isClicking.load())return 0;
if(st){
st->pressed=true;
st->knobScaleTarget=1.12;
if(!st->timerRunning){
st->timerRunning=true;
SetTimer(hwnd,TOGGLE_ANIM_TIMER_ID,TOGGLE_ANIM_TIMER_MS,nullptr);
}
}
SetCapture(hwnd);
return 0;
}
case WM_LBUTTONUP:{
if(!IsWindowEnabled(hwnd)||isClicking.load()){
ReleaseCapture();
return 0;
}
ReleaseCapture();
if(st){
st->pressed=false;
st->knobScaleTarget=1.0;
}
POINT pt={LOWORD(lParam),HIWORD(lParam)};
RECT rc;
GetClientRect(hwnd,&rc);
if(PtInRect(&rc,pt)){
bool checked=(SendMessage(hwnd,BM_GETCHECK,0,0)==BST_CHECKED);
SendMessage(hwnd,BM_SETCHECK,checked?BST_UNCHECKED:BST_CHECKED,0);
HWND parent=GetParent(hwnd);
if(parent)SendMessage(parent,WM_COMMAND,
MAKEWPARAM(GetDlgCtrlID(hwnd),BN_CLICKED),(LPARAM)hwnd);
}
return 0;
}
case WM_CAPTURECHANGED:{
if(st&&st->pressed){
st->pressed=false;
st->knobScaleTarget=1.0;
if(!st->timerRunning){
st->timerRunning=true;
SetTimer(hwnd,TOGGLE_ANIM_TIMER_ID,TOGGLE_ANIM_TIMER_MS,nullptr);
}
}
break;
}
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
RECT rc;
GetClientRect(hwnd,&rc);
if(rc.right<=0||rc.bottom<=0||!st){
EndPaint(hwnd,&ps);
return 0;
}
bool disabled=!IsWindowEnabled(hwnd);
HWND parent=GetParent(hwnd);
bool isMacro=(hwndMacro&&parent==hwndMacro);
HBRUSH hBgBr=isMacro?(HBRUSH)GetStockObject(WHITE_BRUSH)
:(hBrushBg?hBrushBg:(HBRUSH)GetStockObject(WHITE_BRUSH));
int h=rc.bottom-rc.top;
int trackH=h-8;
if(trackH<14)trackH=14;
if(trackH>50)trackH=50;
int trackW=(int)(trackH*1.8);
if(trackW<26)trackW=26;
int trackX=rc.left+2;
int trackY=rc.top+(h-trackH)/2;
double p=st->pos;
if(p<0.0)p=0.0;
if(p>1.0)p=1.0;
int r,g,b;
if(disabled){
r=(int)(224+(196-224)*p);
g=(int)(224+(196-224)*p);
b=(int)(224+(196-224)*p);
}else{
r=(int)(156+( 0-156)*p);
g=(int)(160+(103-160)*p);
b=(int)(164+(192-164)*p);
}
HDC memDC=CreateCompatibleDC(hdc);
HBITMAP memBmp=CreateCompatibleBitmap(hdc,rc.right,rc.bottom);
HBITMAP oldBmp=(HBITMAP)SelectObject(memDC,memBmp);
FillRect(memDC,&rc,hBgBr);
{
Gdiplus::Graphics gfx(memDC);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
gfx.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHalf);
int d=trackH;
Gdiplus::GraphicsPath path;
path.AddArc((Gdiplus::REAL)trackX,(Gdiplus::REAL)trackY,
(Gdiplus::REAL)d,(Gdiplus::REAL)d,90.0f,180.0f);
path.AddArc((Gdiplus::REAL)(trackX+trackW-d),(Gdiplus::REAL)trackY,
(Gdiplus::REAL)d,(Gdiplus::REAL)d,270.0f,180.0f);
path.CloseFigure();
Gdiplus::SolidBrush trackBrush(Gdiplus::Color(255,r,g,b));
gfx.FillPath(&trackBrush,&path);
int knobBase=trackH-4;
double scale=st->knobScale;
if(scale<1.0)scale=1.0;
if(scale>1.25)scale=1.25;
int knobSize=(int)(knobBase*scale+0.5);
int travel=trackW-knobBase-4;
int knobCenterBaseX=trackX+2+(int)(travel*p+0.5)+knobBase/2;
int knobCenterBaseY=trackY+2+knobBase/2;
int knobX=knobCenterBaseX-knobSize/2;
int knobY=knobCenterBaseY-knobSize/2;
if(!disabled&&p>0.001){
Gdiplus::SolidBrush shadowBrush(Gdiplus::Color(30,0,0,0));
gfx.FillEllipse(&shadowBrush,
(Gdiplus::REAL)knobX,(Gdiplus::REAL)(knobY+1),
(Gdiplus::REAL)knobSize,(Gdiplus::REAL)knobSize);
}
Gdiplus::Color knobColor=disabled?Gdiplus::Color(255,245,245,245)
:Gdiplus::Color(255,255,255,255);
Gdiplus::SolidBrush knobBrush(knobColor);
gfx.FillEllipse(&knobBrush,
(Gdiplus::REAL)knobX,(Gdiplus::REAL)knobY,
(Gdiplus::REAL)knobSize,(Gdiplus::REAL)knobSize);
}
wchar_t text[256];
GetWindowTextW(hwnd,text,256);
if(text[0]){
SetBkMode(memDC,TRANSPARENT);
SetTextColor(memDC,disabled?RGB(158,158,158):RGB(26,26,26));
HFONT hF=(HFONT)SendMessage(hwnd,WM_GETFONT,0,0);
HFONT hOldF=(HFONT)SelectObject(memDC,hF);
RECT textRc=rc;
textRc.left=trackX+trackW+8;
DrawTextW(memDC,text,-1,&textRc,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
SelectObject(memDC,hOldF);
}
BitBlt(hdc,0,0,rc.right,rc.bottom,memDC,0,0,SRCCOPY);
SelectObject(memDC,oldBmp);
DeleteObject(memBmp);
DeleteDC(memDC);
EndPaint(hwnd,&ps);
return 0;
}
case WM_ERASEBKGND:
return 1;
case WM_TIMER:{
if(wParam==TOGGLE_ANIM_TIMER_ID&&st){
DWORD now=GetTickCount();
DWORD elapsed=now-st->animStart;
double t=(double)elapsed/(double)TOGGLE_ANIM_DURATION_MS;
bool posDone=false;
if(t>=1.0){
st->pos=st->target;
posDone=true;
}else{
st->pos=st->animFrom+(st->target-st->animFrom)*EaseOutCubic(t);
}
double ds=st->knobScaleTarget-st->knobScale;
bool scaleDone;
if(fabs(ds)<0.005){
st->knobScale=st->knobScaleTarget;
scaleDone=true;
}else{
st->knobScale+=ds*0.4;
scaleDone=false;
}
InvalidateRect(hwnd,nullptr,FALSE);
if(posDone&&scaleDone&&!st->pressed){
KillTimer(hwnd,TOGGLE_ANIM_TIMER_ID);
st->timerRunning=false;
}
return 0;
}
break;
}
case BM_SETCHECK:{
LRESULT r=DefSubclassProc(hwnd,msg,wParam,lParam);
if(st){
bool checked=(wParam==BST_CHECKED);
double newTarget=checked?1.0:0.0;
if(fabs(newTarget-st->target)>0.001){
st->animFrom=st->pos;
st->animStart=GetTickCount();
st->target=newTarget;
if(!st->timerRunning){
st->timerRunning=true;
SetTimer(hwnd,TOGGLE_ANIM_TIMER_ID,TOGGLE_ANIM_TIMER_MS,nullptr);
}
}
InvalidateRect(hwnd,nullptr,FALSE);
}
return r;
}
case WM_SETFOCUS:
case WM_KILLFOCUS:
InvalidateRect(hwnd,nullptr,FALSE);
break;
case WM_NCDESTROY:
if(st){
if(st->timerRunning)KillTimer(hwnd,TOGGLE_ANIM_TIMER_ID);
delete st;
}
RemoveWindowSubclass(hwnd,RoundCheckBoxProc,uIdSubclass);
break;
}
return DefSubclassProc(hwnd,msg,wParam,lParam);
}
static void AttachToggle(HWND h,bool initialChecked){
if(initialChecked)SendMessage(h,BM_SETCHECK,BST_CHECKED,0);
ToggleAnimState*st=new ToggleAnimState;
st->pos=initialChecked?1.0:0.0;
st->target=st->pos;
st->timerRunning=false;
st->animStart=0;
st->animFrom=st->pos;
st->knobScale=1.0;
st->knobScaleTarget=1.0;
st->pressed=false;
SetWindowSubclass(h,RoundCheckBoxProc,0,(DWORD_PTR)st);
}
LRESULT CALLBACK ComboBoxProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam,
UINT_PTR uIdSubclass,DWORD_PTR dwRefData){
switch(msg){
case WM_NCCALCSIZE:{
if(wParam==TRUE)return 0;
break;
}
case WM_ERASEBKGND:
return 1;
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
RECT rc;
GetClientRect(hwnd,&rc);
if(rc.right<=0||rc.bottom<=0){
EndPaint(hwnd,&ps);
return 0;
}
bool disabled=!IsWindowEnabled(hwnd);
bool focused =(GetFocus()==hwnd);
HDC memDC=CreateCompatibleDC(hdc);
HBITMAP memBmp=CreateCompatibleBitmap(hdc,rc.right,rc.bottom);
HBITMAP oldBmp=(HBITMAP)SelectObject(memDC,memBmp);
HBRUSH hBgBr=hBrushBg?hBrushBg:(HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(memDC,&rc,hBgBr);
Gdiplus::Color fill=disabled?WinUI::DisabledBg:WinUI::ControlBg;
Gdiplus::Color border=disabled?WinUI::DisabledBorder
:(focused?WinUI::Accent:WinUI::BorderStrong);
Gdiplus::REAL borderW=focused?2.0f:1.0f;
Gdiplus::REAL radius=WINUI_RADIUS;
GdiPlusFillRoundRect(memDC,rc,radius,fill,border,borderW);
if(focused&&!disabled){
Gdiplus::Graphics gfx(memDC);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen underlinePen(WinUI::Accent,2.0f);
gfx.DrawLine(&underlinePen,
(Gdiplus::REAL)(rc.left+(int)radius+2),
(Gdiplus::REAL)(rc.bottom-2),
(Gdiplus::REAL)(rc.right-(int)radius-2),
(Gdiplus::REAL)(rc.bottom-2));
}
int sel=(int)SendMessage(hwnd,CB_GETCURSEL,0,0);
char text[256]={0};
if(sel!=CB_ERR)SendMessageA(hwnd,CB_GETLBTEXT,sel,(LPARAM)text);
SetBkMode(memDC,TRANSPARENT);
SetTextColor(memDC,disabled?RGB(158,158,158):RGB(26,26,26));
HFONT hF=(HFONT)SendMessage(hwnd,WM_GETFONT,0,0);
HFONT hOldF=(HFONT)SelectObject(memDC,hF);
int padLeft=12;
int padRight=32;
RECT textRc=rc;
textRc.left+=padLeft;
textRc.right-=padRight;
DrawTextA(memDC,text,-1,&textRc,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
SelectObject(memDC,hOldF);
int aw=4,ah=3;
int arrowCX=rc.right-16;
int arrowCY=(rc.top+rc.bottom)/2;
COLORREF arrowColor=disabled?RGB(158,158,158):RGB(90,90,90);
HPEN hPen=CreatePen(PS_SOLID,2,arrowColor);
HPEN hOldPen=(HPEN)SelectObject(memDC,hPen);
MoveToEx(memDC,arrowCX-aw,arrowCY-ah,NULL);
LineTo(memDC,arrowCX,arrowCY+ah);
LineTo(memDC,arrowCX+aw,arrowCY-ah);
SelectObject(memDC,hOldPen);
DeleteObject(hPen);
BitBlt(hdc,0,0,rc.right,rc.bottom,memDC,0,0,SRCCOPY);
SelectObject(memDC,oldBmp);
DeleteObject(memBmp);
DeleteDC(memDC);
EndPaint(hwnd,&ps);
return 0;
}
case WM_SIZE:
ApplyRoundedRegion(hwnd);
break;
case WM_SETFOCUS:
case WM_KILLFOCUS:
InvalidateRect(hwnd,nullptr,FALSE);
InvalidateRect(GetParent(hwnd),nullptr,FALSE);
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd,ComboBoxProc,uIdSubclass);
break;
}
return DefSubclassProc(hwnd,msg,wParam,lParam);
}
void DrawComboListItem(LPDRAWITEMSTRUCT dis){
HDC hdc=dis->hDC;
RECT rc=dis->rcItem;
bool selected=(dis->itemState&ODS_SELECTED)!=0;
bool comboEdit=(dis->itemState&ODS_COMBOBOXEDIT)!=0;
int itemID=(int)dis->itemID;
if(itemID==-1){
itemID=(int)SendMessage(dis->hwndItem,CB_GETCURSEL,0,0);
if(itemID==CB_ERR){
HBRUSH hWhite=(HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(hdc,&rc,hWhite);
return;
}
}
int w=rc.right-rc.left;
int h=rc.bottom-rc.top;
if(w<=0||h<=0)return;
HDC memDC=CreateCompatibleDC(hdc);
HBITMAP memBmp=CreateCompatibleBitmap(hdc,w,h);
HBITMAP oldBmp=(HBITMAP)SelectObject(memDC,memBmp);
RECT local={0,0,w,h};
{
Gdiplus::Graphics gfx(memDC);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::SolidBrush bg(selected?Gdiplus::Color(255,245,245,245)
:Gdiplus::Color(255,255,255,255));
gfx.FillRectangle(&bg,0,0,w,h);
if(selected&&!comboEdit){
Gdiplus::SolidBrush accent(WinUI::Accent);
gfx.FillRectangle(&accent,0,6,3,h-12);
}
}
wchar_t text[256]={0};
SendMessageW(dis->hwndItem,CB_GETLBTEXT,itemID,(LPARAM)text);
SetBkMode(memDC,TRANSPARENT);
SetTextColor(memDC,RGB(26,26,26));
HFONT hF=(HFONT)SendMessage(dis->hwndItem,WM_GETFONT,0,0);
HFONT hOldF=(HFONT)SelectObject(memDC,hF);
RECT textRc=local;
textRc.left+=comboEdit?12:16;
DrawTextW(memDC,text,-1,&textRc,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
SelectObject(memDC,hOldF);
BitBlt(hdc,rc.left,rc.top,w,h,memDC,0,0,SRCCOPY);
SelectObject(memDC,oldBmp);
DeleteObject(memBmp);
DeleteDC(memDC);
}
void DrawStatusBar(LPDRAWITEMSTRUCT dis){
HDC hdc=dis->hDC;
RECT rc=dis->rcItem;
HDC memDC=CreateCompatibleDC(hdc);
HBITMAP memBmp=CreateCompatibleBitmap(hdc,rc.right-rc.left,rc.bottom-rc.top);
HBITMAP oldBmp=(HBITMAP)SelectObject(memDC,memBmp);
RECT local={0,0,rc.right-rc.left,rc.bottom-rc.top};
{
Gdiplus::Graphics gfx(memDC);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::SolidBrush br(WinUI::StatusBg);
gfx.FillRectangle(&br,0,0,local.right,local.bottom);
}
double s=g_scale>0?g_scale:1.0;
SetBkMode(memDC,TRANSPARENT);
HFONT hOld=(HFONT)SelectObject(memDC,F_DOT);
SetTextColor(memDC,isClicking.load()?RGB(80,220,130):RGB(150,160,180));
RECT rcDot={(int)(24*s),0,(int)(50*s),local.bottom};
DrawTextW(memDC,L"●",-1,&rcDot,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
SelectObject(memDC,F_STATUS);
SetTextColor(memDC,RGB(255,255,255));
RECT rcText={(int)(52*s),0,(int)(240*s),local.bottom};
DrawTextW(memDC,isClicking.load()?L"运行中":L"已停止",-1,&rcText,
DT_LEFT|DT_VCENTER|DT_SINGLELINE);
SelectObject(memDC,F_STATUSINFO);
SetTextColor(memDC,RGB(190,205,235));
wchar_t info[128];
if(isExtremeMode.load())wcscpy(info,L"极速模式 · 无统计");
else swprintf(info,128,L"%d 次点击 · %.1f CPS",clickCount.load(),currentCPS.load());
RECT rcInfo={(int)(240*s),0,local.right-(int)(24*s),local.bottom};
DrawTextW(memDC,info,-1,&rcInfo,DT_RIGHT|DT_VCENTER|DT_SINGLELINE);
SelectObject(memDC,hOld);
BitBlt(hdc,0,0,local.right,local.bottom,memDC,0,0,SRCCOPY);
SelectObject(memDC,oldBmp);
DeleteObject(memBmp);
DeleteDC(memDC);
}
void DrawRoundButton(LPDRAWITEMSTRUCT dis){
HDC hdc=dis->hDC;
RECT rc=dis->rcItem;
UINT state=dis->itemState;
bool pressed =(state&ODS_SELECTED)!=0;
bool disabled=(state&ODS_DISABLED)!=0;
bool focused =(state&ODS_FOCUS)!=0;
HWND parent=GetParent(dis->hwndItem);
bool isMacro=(hwndMacro&&parent==hwndMacro);
char textBuf[128]={0};
GetWindowTextA(dis->hwndItem,textBuf,128);
bool isPrimary=(strstr(textBuf,"开始")!=nullptr)||
(strstr(textBuf,"播放")!=nullptr&&strstr(textBuf,"停止")==nullptr);
Gdiplus::Color fill,border,txt;
if(disabled){
fill=WinUI::DisabledBg;
border=WinUI::DisabledBorder;
txt=WinUI::TextDisabled;
}else if(isPrimary){
fill=pressed?WinUI::AccentPressed:WinUI::Accent;
border=Gdiplus::Color(0,0,0,0);
txt=Gdiplus::Color(255,255,255,255);
}else{
fill=pressed?WinUI::ControlPress:WinUI::ControlBg;
border=WinUI::BorderStrong;
txt=WinUI::TextPrimary;
}
int w=rc.right-rc.left;
int h=rc.bottom-rc.top;
RECT local={0,0,w,h};
HDC memDC=CreateCompatibleDC(hdc);
HBITMAP memBmp=CreateCompatibleBitmap(hdc,w,h);
HBITMAP oldBmp=(HBITMAP)SelectObject(memDC,memBmp);
HBRUSH hBgBr=isMacro?(HBRUSH)GetStockObject(WHITE_BRUSH)
:(hBrushBg?hBrushBg:(HBRUSH)GetStockObject(WHITE_BRUSH));
FillRect(memDC,&local,hBgBr);
Gdiplus::REAL radius=WINUI_RADIUS;
GdiPlusFillRoundRect(memDC,local,radius,fill,border,1.0f);
if(focused&&!disabled){
Gdiplus::Graphics gfx(memDC);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen focusPen(Gdiplus::Color(255,26,26,26),2.0f);
gfx.DrawRectangle(&focusPen,1.0f,1.0f,(Gdiplus::REAL)(w-2),(Gdiplus::REAL)(h-2));
}
wchar_t wtext[256];
GetWindowTextW(dis->hwndItem,wtext,256);
SetBkMode(memDC,TRANSPARENT);
SetTextColor(memDC,RGB(txt.GetR(),txt.GetG(),txt.GetB()));
HFONT hF=(HFONT)SendMessage(dis->hwndItem,WM_GETFONT,0,0);
HFONT hOldF=(HFONT)SelectObject(memDC,hF);
RECT textRc=local;
DrawTextW(memDC,wtext,-1,&textRc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
SelectObject(memDC,hOldF);
BitBlt(hdc,rc.left,rc.top,w,h,memDC,0,0,SRCCOPY);
SelectObject(memDC,oldBmp);
DeleteObject(memBmp);
DeleteDC(memDC);
}
void PaintInputBorders(HDC hdc){
HWND inputs[]={hwndDelayEdit,hwndJitterEdit,hwndSpeedSlider,hwndSeqEdit};
HWND focus=GetFocus();
RECT clientRc;
GetClientRect(hwndMain,&clientRc);
double sx=(double)clientRc.right/BASE_W;
double sy=(double)clientRc.bottom/BASE_H;
if(sx<MIN_SCALE)sx=MIN_SCALE;
if(sx>3.0)sx=3.0;
if(sy<MIN_SCALE)sy=MIN_SCALE;
if(sy>3.0)sy=3.0;
int insetX=(int)(EDIT_INSET_X*sx+0.5);
int insetY=(int)(EDIT_INSET_Y*sy+0.5);
if(insetX<3)insetX=3;
if(insetY<1)insetY=1;
for(int i=0;i<4;i++){
HWND h=inputs[i];
if(!h||!IsWindowVisible(h))continue;
RECT rc;
GetWindowRect(h,&rc);
MapWindowPoints(HWND_DESKTOP,hwndMain,(LPPOINT)&rc,2);
rc.left -=insetX;
rc.right +=insetX;
rc.top -=insetY;
rc.bottom+=insetY;
HBRUSH hBgBr=hBrushBg?hBrushBg:(HBRUSH)GetStockObject(WHITE_BRUSH);
FillRect(hdc,&rc,hBgBr);
bool hasFocus=(h==focus);
Gdiplus::Color border=hasFocus?WinUI::Accent:WinUI::BorderStrong;
Gdiplus::REAL borderW=hasFocus?2.0f:1.0f;
Gdiplus::REAL radius=WINUI_RADIUS;
GdiPlusFillRoundRect(hdc,rc,radius,WinUI::ControlBg,border,borderW);
if(hasFocus){
Gdiplus::Graphics gfx(hdc);
gfx.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen underlinePen(WinUI::Accent,2.0f);
gfx.DrawLine(&underlinePen,
(Gdiplus::REAL)(rc.left+(int)radius+2),
(Gdiplus::REAL)(rc.bottom-2),
(Gdiplus::REAL)(rc.right-(int)radius-2),
(Gdiplus::REAL)(rc.bottom-2));
}
}
}
void ApplyLayout(int clientW,int clientH){
if(clientW<=0)clientW=BASE_W;
if(clientH<=0)clientH=BASE_H;
double sx=(double)clientW/BASE_W;
double sy=(double)clientH/BASE_H;
if(sx<MIN_SCALE)sx=MIN_SCALE;
if(sx>3.0)sx=3.0;
if(sy<MIN_SCALE)sy=MIN_SCALE;
if(sy>3.0)sy=3.0;
double sFont=sqrt(sx*sy);
if(sFont<MIN_SCALE) sFont=MIN_SCALE;
if(sFont>MAX_FONT_SCALE)sFont=MAX_FONT_SCALE;
int newKey=0;
for(int i=0;i<7;i++){
int sz=(int)(g_fontSlots[i].baseSize*sFont+0.5);
if(sz<9)sz=9;
if(sz>80)sz=80;
newKey=newKey*131+sz;
}
bool fontChanged=(newKey!=g_lastMainFontSize);
if(fontChanged)g_lastMainFontSize=newKey;
g_scale=sFont;
SendMessage(hwndMain,WM_SETREDRAW,FALSE,0);
if(fontChanged){
RecreateFonts(sFont);
ApplyFonts();
}
auto LX=[sx](double v){
return(int)(v*sx+0.5);
};
auto LY=[sy](double v){
return(int)(v*sy+0.5);
};
auto LW=[sx](double v){
return(int)(v*sx+0.5);
};
auto LH=[sy](double v){
return(int)(v*sy+0.5);
};
int baseX=24,baseY=20,statusHBase=52;
int marginX=LX(baseX);
int statusW=clientW-marginX*2;
if(statusW<400)statusW=400;
int gap=LW(30);
int colW=(statusW-gap*2)/3;
if(colW<120)colW=120;
int x1=marginX;
int x2=x1+colW+gap;
int x3=x2+colW+gap;
int y=baseY+statusHBase+24;
int insetX=(int)(EDIT_INSET_X*sx+0.5);
int insetY=(int)(EDIT_INSET_Y*sy+0.5);
if(insetX<3)insetX=3;
if(insetY<1)insetY=1;
HDWP hdwp=BeginDeferWindowPos(40);
#define DEFER(h,xx,yy,ww,hh)do{\
if((h)&&hdwp)hdwp=DeferWindowPos(hdwp,(h),nullptr,(xx),(yy),(ww),(hh),\
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS);\
}while(0)
DEFER(hwndStatusBar,marginX,LY(baseY),statusW,LH(statusHBase));
DEFER(hwndTitle1,x1,LY(y),colW,LH(26));
DEFER(hwndTitle2,x2,LY(y),colW,LH(26));
DEFER(hwndTitle3,x3,LY(y),colW,LH(26));
DEFER(hwndLblDelay, x1, LY(y+42), LW(80), LH(24));
DEFER(hwndDelayEdit, x1+insetX,LY(y+68)+insetY,
LW(80)-insetX*2,LH(28)-insetY*2);
DEFER(hwndDelayLabel, x1+LX(95), LY(y+74), LW(190),LH(24));
DEFER(hwndLblButton, x1, LY(y+118),LW(80), LH(24));
DEFER(hwndButtonCombo, x1, LY(y+144),LW(140),LH(300));
DEFER(hwndJitterCheck, x1, LY(y+188),LW(90), LH(28));
DEFER(hwndLblJitter, x1+LX(98), LY(y+194),LW(36), LH(18));
DEFER(hwndJitterEdit, x1+LX(138)+insetX,LY(y+190)+insetY,
LW(50)-insetX*2,LH(24)-insetY*2);
DEFER(hwndRelativeCheck,x1, LY(y+224),LW(140),LH(28));
DEFER(hwndSeqCheck,x2,LY(y+42), LW(150),LH(28));
DEFER(hwndLblCoord,x2,LY(y+82), LW(180),LH(24));
DEFER(hwndSeqEdit, x2+insetX,LY(y+108)+insetY,
colW-LW(10)-insetX*2,LH(30)-insetY*2);
DEFER(hwndRecBtn, x3, LY(y+42), LW(140),LH(44));
DEFER(hwndPlayBtn, x3+LX(150),LY(y+42), LW(140),LH(44));
DEFER(hwndLblSpeed, x3, LY(y+100),LW(80), LH(24));
DEFER(hwndSpeedSlider,x3+insetX,LY(y+126)+insetY,
LW(80)-insetX*2,LH(28)-insetY*2);
y+=280;
DEFER(hwndTitle4, x1,LY(y), colW, LH(26));
DEFER(hwndMacroBtn, x1,LY(y+42), LW(180),LH(46));
DEFER(hwndLblMacroHint,x1,LY(y+98), colW, LH(26));
y+=140;
DEFER(hwndStartBtn,x1, LY(y),LW(160),LH(50));
DEFER(hwndStopBtn, x1+LX(180),LY(y),LW(160),LH(50));
DEFER(hwndExitBtn, x1+LX(360),LY(y),LW(160),LH(50));
y+=70;
DEFER(hwndLblHotkey,x1,LY(y),statusW,LH(26));
#undef DEFER
if(hdwp)EndDeferWindowPos(hdwp);
if(hwndButtonCombo){
int editH=(int)(30*sy+0.5);
if(editH<22)editH=22;
if(editH>70)editH=70;
SendMessage(hwndButtonCombo,CB_SETITEMHEIGHT,(WPARAM)-1,(LPARAM)editH);
int itemH=(int)(28*sFont+0.5);
if(itemH<20)itemH=20;
if(itemH>48)itemH=48;
SendMessage(hwndButtonCombo,CB_SETITEMHEIGHT,(WPARAM)0,(LPARAM)itemH);
}
SendMessage(hwndMain,WM_SETREDRAW,TRUE,0);
RedrawWindow(hwndMain,nullptr,nullptr,
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_ERASE|RDW_UPDATENOW);
}
void ApplyMacroLayout(int cw,int ch){
if(!hwndMacro)return;
if(cw<=0)cw=MACRO_BASE_W;
if(ch<=0)ch=MACRO_BASE_H;
double sx=(double)cw/MACRO_BASE_W;
double sy=(double)ch/MACRO_BASE_H;
if(sx<0.75)sx=0.75;
if(sx>2.5)sx=2.5;
if(sy<0.75)sy=0.75;
if(sy>2.5)sy=2.5;
double sFont=sqrt(sx*sy);
if(sFont<0.75)sFont=0.75;
if(sFont>2.0) sFont=2.0;
int newKey=0;
for(int i=0;i<2;i++){
int sz=(int)(g_macroFontSlots[i].baseSize*sFont+0.5);
if(sz<9)sz=9;
if(sz>60)sz=60;
newKey=newKey*131+sz;
}
bool macroFontChanged=(newKey!=g_lastMacroFontSize);
if(macroFontChanged)g_lastMacroFontSize=newKey;
if(macroFontChanged){
g_macroScale=sFont;
RecreateMacroFonts(sFont);
ApplyMacroFonts();
}
const int pad=15,btnH=42,btnW=120,btnGap=8,bottomMargin=15;
int listY=pad;
int listH=ch-pad-btnH-bottomMargin*2;
if(listH<60)listH=60;
int btnY=ch-btnH-bottomMargin;
if(btnY<listY+listH+5)btnY=listY+listH+5;
SetWindowPos(hwndMacroList,nullptr,pad,listY,cw-pad*2,listH,
SWP_NOZORDER|SWP_NOACTIVATE);
int x=pad;
SetWindowPos(hwndMacroRecBtn,nullptr,x,btnY,btnW,btnH,SWP_NOZORDER|SWP_NOACTIVATE);
x+=btnW+btnGap;
SetWindowPos(hwndMacroStopRecBtn,nullptr,x,btnY,btnW,btnH,SWP_NOZORDER|SWP_NOACTIVATE);
x+=btnW+btnGap;
SetWindowPos(hwndMacroPlayBtn,nullptr,x,btnY,btnW,btnH,SWP_NOZORDER|SWP_NOACTIVATE);
x+=btnW+btnGap;
SetWindowPos(hwndMacroStopPlayBtn,nullptr,x,btnY,btnW,btnH,SWP_NOZORDER|SWP_NOACTIVATE);
x+=btnW+btnGap+16;
SetWindowPos(hwndMacroLoopCheck,nullptr,x,btnY+10,90,24,SWP_NOZORDER|SWP_NOACTIVATE);
x+=90+10;
SetWindowPos(hwndMacroSpeedLabel,nullptr,x,btnY+12,75,22,SWP_NOZORDER|SWP_NOACTIVATE);
x+=80;
SetWindowPos(hwndMacroSpeedEdit,nullptr,x,btnY+8,60,28,SWP_NOZORDER|SWP_NOACTIVATE);
x+=75;
SetWindowPos(hwndMacroMoveCheck,nullptr,x,btnY+10,160,24,SWP_NOZORDER|SWP_NOACTIVATE);
RedrawWindow(hwndMacro,nullptr,nullptr,
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_ERASE|RDW_UPDATENOW);
}
int GetJitterOffset(int range){
if(range<=0)return 0;
uniform_int_distribution<int>dist(-range,range);
return dist(rng);
}
void UpdateDelayDisplay(){
char buf[32];
sprintf(buf,"当前: %d ms",clickDelay.load());
SetWindowTextA(hwndDelayLabel,buf);
}
void UpdateStatusInfo(){
if(hwndStatusBar)InvalidateRect(hwndStatusBar,nullptr,TRUE);
}
void UpdateCount(){
UpdateStatusInfo();
}
void UpdateUI(){
bool running=isClicking.load();
if(hwndStatusBar)InvalidateRect(hwndStatusBar,nullptr,TRUE);
EnableWindow(hwndStartBtn,!running);
EnableWindow(hwndStopBtn,running);
UpdateDelayDisplay();
if(hwndSeqEdit)SendMessage(hwndSeqEdit,EM_SETREADONLY,running?TRUE:FALSE,0);
if(hwndSeqCheck)InvalidateRect(hwndSeqCheck,nullptr,TRUE);
if(hwndSeqEdit) InvalidateRect(hwndSeqEdit,nullptr,TRUE);
if(hwndStartBtn)InvalidateRect(hwndStartBtn,nullptr,TRUE);
if(hwndStopBtn) InvalidateRect(hwndStopBtn,nullptr,TRUE);
}
void usSleep(long long us){
if(us<=0)return;
if(us>2000){
Sleep((DWORD)(us/1000));
return;
}
LARGE_INTEGER freq,start,now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
long long elapsed=0;
while(elapsed<us){
QueryPerformanceCounter(&now);
elapsed=(now.QuadPart-start.QuadPart)*1000000/freq.QuadPart;
if(elapsed>=us)break;
if(us-elapsed>500)SwitchToThread();
}
}
void SimulateClick(int x,int y,int button){
SetCursorPos(x,y);
INPUT input[2]={};
input[0].type=INPUT_MOUSE;
input[1].type=INPUT_MOUSE;
switch(button){
case 0:
input[0].mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
input[1].mi.dwFlags=MOUSEEVENTF_LEFTUP;
break;
case 1:
input[0].mi.dwFlags=MOUSEEVENTF_MIDDLEDOWN;
input[1].mi.dwFlags=MOUSEEVENTF_MIDDLEUP;
break;
case 2:
input[0].mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
input[1].mi.dwFlags=MOUSEEVENTF_RIGHTUP;
break;
}
SendInput(2,input,sizeof(INPUT));
}
void ParseCoordinates(const string&str){
clickPoints.clear();
string s=str;
s.erase(remove_if(s.begin(),s.end(),::isspace),s.end());
size_t pos=0;
while((pos=s.find(';'))!=string::npos){
string token=s.substr(0,pos);
s.erase(0,pos+1);
int comma=(int)token.find(',');
if(comma!=string::npos)
clickPoints.push_back({atoi(token.substr(0,comma).c_str()),atoi(token.substr(comma+1).c_str())});
}
if(!s.empty()){
int comma=(int)s.find(',');
if(comma!=string::npos)
clickPoints.push_back({atoi(s.substr(0,comma).c_str()),atoi(s.substr(comma+1).c_str())});
}
}
void ToggleRecording();
void StartRecordingActual();
void StopRecording();
void PrintCommandHelp(){
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
if(hConsole==INVALID_HANDLE_VALUE){
AllocConsole();
hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
}
if(hConsole==INVALID_HANDLE_VALUE)return;
const char*help=
"自动连点器命令行选项:\n"
" --start, -s 启动连点\n"
" --stop, -t 停止连点\n"
" --toggle, -tg 切换连点状态\n"
" --delay <ms>, -d <ms> 设置点击间隔\n"
" --button <0|1|2>, -b 按键(0左,1中,2右)\n"
" --sequence <x,y;x,y> 序列坐标\n"
" --enable-seq / --disable-seq\n"
" --jitter <range>, -j 抖动范围\n"
" --relative / --absolute\n"
" --show / --hide\n"
" --exit, -e 退出\n"
" --help, -h 帮助\n";
DWORD written;
WriteConsoleA(hConsole,help,(DWORD)strlen(help),&written,NULL);
}
void ExecuteCommand(const string&cmd){
vector<string>tokens;
string token;
bool inQuotes=false;
for(char c:cmd){
if(c=='"'){
inQuotes=!inQuotes;
continue;
}
if(isspace((unsigned char)c)&&!inQuotes){
if(!token.empty()){
tokens.push_back(token);
token.clear();
}
}else token+=c;
}
if(!token.empty())tokens.push_back(token);
for(size_t i=0;i<tokens.size();++i){
string arg=tokens[i];
transform(arg.begin(),arg.end(),arg.begin(),::tolower);
if(arg=="--start"||arg=="-s"){
isClicking=true;
UpdateUI();
}else if(arg=="--stop"||arg=="-t"){
isClicking=false;
UpdateUI();
UpdateCount();
}else if(arg=="--toggle"||arg=="-tg"){
isClicking=!isClicking.load();
UpdateUI();
UpdateCount();
}else if((arg=="--delay"||arg=="-d")&&i+1<tokens.size()){
int val=atoi(tokens[++i].c_str());
if(val<0)val=0;
if(val>10000)val=10000;
clickDelay=val;
isExtremeMode=(val==0);
char tmp[16];
sprintf(tmp,"%d",val);
SetWindowTextA(hwndDelayEdit,tmp);
UpdateDelayDisplay();
UpdateCount();
}else if((arg=="--button"||arg=="-b")&&i+1<tokens.size()){
int val=atoi(tokens[++i].c_str());
if(val>=0&&val<=2){
clickButton=val;
SendMessage(hwndButtonCombo,CB_SETCURSEL,val,0);
InvalidateRect(hwndButtonCombo,nullptr,TRUE);
}
}else if((arg=="--sequence"||arg=="-seq")&&i+1<tokens.size()){
string seq=tokens[++i];
SetWindowTextA(hwndSeqEdit,seq.c_str());
ParseCoordinates(seq);
}else if(arg=="--enable-seq") {
sequenceEnabled=true;
SendMessage(hwndSeqCheck,BM_SETCHECK,BST_CHECKED,0);
}else if(arg=="--disable-seq"){
sequenceEnabled=false;
SendMessage(hwndSeqCheck,BM_SETCHECK,BST_UNCHECKED,0);
}else if((arg=="--jitter"||arg=="-j")&&i+1<tokens.size()){
int range=atoi(tokens[++i].c_str());
if(range<0)range=0;
if(range>100)range=100;
jitterRange=range;
jitterEnabled=(range>0);
SendMessage(hwndJitterCheck,BM_SETCHECK,jitterEnabled.load()?BST_CHECKED:BST_UNCHECKED,0);
char tmp[16];
sprintf(tmp,"%d",range);
SetWindowTextA(hwndJitterEdit,tmp);
}else if(arg=="--relative"){
g_bRelativeMode=true;
SendMessage(hwndRelativeCheck,BM_SETCHECK,BST_CHECKED,0);
}else if(arg=="--absolute"){
g_bRelativeMode=false;
SendMessage(hwndRelativeCheck,BM_SETCHECK,BST_UNCHECKED,0);
}else if(arg=="--show"){
ShowWindow(hwndMain,SW_SHOW);
SetForegroundWindow(hwndMain);
}else if(arg=="--hide"){
ShowWindow(hwndMain,SW_HIDE);
}else if(arg=="--exit"||arg=="-e"){
PostMessage(hwndMain,WM_DESTROY,0,0);
}else if(arg=="--help"||arg=="-h"){
PrintCommandHelp();
}
}
}
void PostMacroEventToUI(const char*text){
if(hwndMacroList)PostMessage(hwndMacroList,WM_MACRO_ADD_EVENT,0,(LPARAM)text);
}
LRESULT CALLBACK MacroMouseHookProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode>=0&&g_bMacroRecording){
MSLLHOOKSTRUCT*p=(MSLLHOOKSTRUCT*)lParam;
int type=-1,data=0;
if(wParam==WM_LBUTTONDOWN)type=1;
else if(wParam==WM_LBUTTONUP)type=2;
else if(wParam==WM_RBUTTONDOWN)type=3;
else if(wParam==WM_RBUTTONUP)type=4;
else if(wParam==WM_MBUTTONDOWN)type=5;
else if(wParam==WM_MBUTTONUP)type=6;
else if(wParam==WM_MOUSEWHEEL){
type=9;
data=(short)HIWORD(p->mouseData);
}else if(wParam==WM_MOUSEMOVE&&g_bMacroRecordMove){
static int lastTick=0;
int now=GetTickCount();
if(now-lastTick<10)return CallNextHookEx(g_macroMouseHook,nCode,wParam,lParam);
lastTick=now;
type=0;
}
if(type!=-1){
lock_guard<mutex>lock(g_macroMutex);
macroEvents.push_back({type,p->pt.x,p->pt.y,data,GetTickCount(),false});
const char*names[]={"移动","左键按下","左键释放","右键按下","右键释放","中键按下","中键释放","","","滚轮"};
char buf[128];
sprintf(buf,"%s (%d,%d)",(type>=0&&type<=9)?names[type]:"未知",p->pt.x,p->pt.y);
PostMacroEventToUI(buf);
}
}
return CallNextHookEx(g_macroMouseHook,nCode,wParam,lParam);
}
LRESULT CALLBACK MacroKeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode>=0&&g_bMacroRecording){
KBDLLHOOKSTRUCT*p=(KBDLLHOOKSTRUCT*)lParam;
int type=(wParam==WM_KEYDOWN||wParam==WM_SYSKEYDOWN)?7:8;
lock_guard<mutex>lock(g_macroMutex);
macroEvents.push_back({type,0,0,(int)p->vkCode,GetTickCount(),false});
char buf[64];
sprintf(buf,"按键 VK=%d",p->vkCode);
PostMacroEventToUI(buf);
}
return CallNextHookEx(g_macroKeyboardHook,nCode,wParam,lParam);
}
DWORD WINAPI MacroPlayThread(LPVOID){
int speed=g_macroPlaySpeed;
if(speed<1)speed=1;
bool loop=g_bMacroLoop;
g_bMacroStopPlay=false;
do{
vector<RecordedEvent>eventsCopy;
{
lock_guard<mutex>lock(g_macroMutex);
eventsCopy=macroEvents;
}
for(auto&evt:eventsCopy){
if(g_bMacroStopPlay.load()||!g_bMacroPlaying)break;
int x=evt.x,y=evt.y;
switch(evt.type){
case 0:
SetCursorPos(x,y);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:{
SetCursorPos(x,y);
INPUT inp={};
inp.type=INPUT_MOUSE;
switch(evt.type){
case 1:
inp.mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
break;
case 2:
inp.mi.dwFlags=MOUSEEVENTF_LEFTUP;
break;
case 3:
inp.mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
break;
case 4:
inp.mi.dwFlags=MOUSEEVENTF_RIGHTUP;
break;
case 5:
inp.mi.dwFlags=MOUSEEVENTF_MIDDLEDOWN;
break;
case 6:
inp.mi.dwFlags=MOUSEEVENTF_MIDDLEUP;
break;
}
SendInput(1,&inp,sizeof(INPUT));
break;
}
case 7:
case 8:{
INPUT inp={};
inp.type=INPUT_KEYBOARD;
inp.ki.wVk=(WORD)evt.data;
if(evt.type==8)inp.ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&inp,sizeof(INPUT));
break;
}
case 9:{
SetCursorPos(x,y);
INPUT inp={};
inp.type=INPUT_MOUSE;
inp.mi.dwFlags=MOUSEEVENTF_WHEEL;
inp.mi.mouseData=evt.data;
SendInput(1,&inp,sizeof(INPUT));
break;
}
}
auto start=chrono::steady_clock::now();
while(chrono::duration_cast<chrono::milliseconds>(
chrono::steady_clock::now()-start).count()<speed){
if(g_bMacroStopPlay.load()||!g_bMacroPlaying)break;
Sleep(5);
}
}
}while(loop&&g_bMacroPlaying&&!g_bMacroStopPlay.load());
g_bMacroPlaying=false;
if(hwndMacroPlayBtn){
SetWindowTextA(hwndMacroPlayBtn,"播放");
InvalidateRect(hwndMacroPlayBtn,nullptr,TRUE);
}
PostMessageA(hwndMacro,WM_MACRO_UPDATE,0,0);
return 0;
}
LRESULT CALLBACK MacroWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch(msg){
case WM_CREATE:{
hwndMacro=hwnd;
HINSTANCE hInst=((LPCREATESTRUCT)lParam)->hInstance;
hwndMacroList=CreateWindowA("LISTBOX","",
WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY,
15,15,870,380,hwnd,nullptr,hInst,nullptr);
int btnY=410,btnH=42,btnW=120,btnGap=8,x=15;
hwndMacroRecBtn=CreateWindowA("BUTTON","开始录制",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x,btnY,btnW,btnH,hwnd,(HMENU)4001,hInst,nullptr);
x+=btnW+btnGap;
hwndMacroStopRecBtn=CreateWindowA("BUTTON","停止录制",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x,btnY,btnW,btnH,hwnd,(HMENU)4002,hInst,nullptr);
x+=btnW+btnGap;
hwndMacroPlayBtn=CreateWindowA("BUTTON","播放",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x,btnY,btnW,btnH,hwnd,(HMENU)4003,hInst,nullptr);
x+=btnW+btnGap;
hwndMacroStopPlayBtn=CreateWindowA("BUTTON","停止播放",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x,btnY,btnW,btnH,hwnd,(HMENU)4004,hInst,nullptr);
x+=btnW+btnGap+16;
hwndMacroLoopCheck=CreateWindowA("BUTTON","循环",WS_CHILD|WS_VISIBLE|BS_CHECKBOX,
x,btnY+10,90,24,hwnd,(HMENU)4005,hInst,nullptr);
AttachToggle(hwndMacroLoopCheck,false);
x+=90+10;
hwndMacroSpeedLabel=CreateWindowA("STATIC","速度(ms):",WS_CHILD|WS_VISIBLE,
x,btnY+12,75,22,hwnd,nullptr,hInst,nullptr);
x+=80;
hwndMacroSpeedEdit=CreateWindowA("EDIT","50",WS_CHILD|WS_VISIBLE|ES_NUMBER,
x,btnY+8,60,28,hwnd,(HMENU)4006,hInst,nullptr);
SendMessage(hwndMacroSpeedEdit,EM_SETMARGINS,EC_LEFTMARGIN|EC_RIGHTMARGIN,MAKELPARAM(6,6));
SetWindowSubclass(hwndMacroSpeedEdit,ModernInputProc,0,0);
x+=75;
hwndMacroMoveCheck=CreateWindowA("BUTTON","记录鼠标移动",WS_CHILD|WS_VISIBLE|BS_CHECKBOX,
x,btnY+10,160,24,hwnd,(HMENU)4008,hInst,nullptr);
AttachToggle(hwndMacroMoveCheck,true);
g_bMacroRecordMove=true;
RECT rc;
GetClientRect(hwnd,&rc);
ApplyMacroLayout(rc.right,rc.bottom);
break;
}
case WM_SIZE:{
int cw=LOWORD(lParam),ch=HIWORD(lParam);
if(cw>0&&ch>0&&hwndMacro)ApplyMacroLayout(cw,ch);
break;
}
case WM_GETMINMAXINFO:{
MINMAXINFO*mmi=(MINMAXINFO*)lParam;
RECT rc={0,0,MACRO_BASE_W,(int)(MACRO_BASE_H*0.85)};
AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);
mmi->ptMinTrackSize.x=rc.right-rc.left;
mmi->ptMinTrackSize.y=rc.bottom-rc.top;
break;
}
case WM_ERASEBKGND:{
HDC hdc=(HDC)wParam;
RECT rc;
GetClientRect(hwnd,&rc);
FillRect(hdc,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH));
return 1;
}
case WM_DRAWITEM:{
DrawRoundButton((LPDRAWITEMSTRUCT)lParam);
return TRUE;
}
case WM_CTLCOLORSTATIC:{
HDC hdc=(HDC)wParam;
SetBkMode(hdc,TRANSPARENT);
SetTextColor(hdc,RGB(26,26,26));
return(LRESULT)GetStockObject(WHITE_BRUSH);
}
case WM_CTLCOLOREDIT:{
HDC hdc=(HDC)wParam;
SetTextColor(hdc,RGB(26,26,26));
SetBkColor(hdc,RGB(255,255,255));
return(LRESULT)GetStockObject(WHITE_BRUSH);
}
case WM_MACRO_ADD_EVENT:{
const char*text=(const char*)lParam;
if(text&&hwndMacroList){
SendMessageA(hwndMacroList,LB_ADDSTRING,0,(LPARAM)text);
SendMessageA(hwndMacroList,LB_SETCURSEL,
SendMessage(hwndMacroList,LB_GETCOUNT,0,0)-1,0);
delete[]text;
}
break;
}
case WM_COMMAND:{
int id=LOWORD(wParam);
if(id==4001){
if(g_bMacroPlaying){
MessageBoxA(hwnd,"请先停止播放","提示",MB_OK);
break;
}
{
lock_guard<mutex>lock(g_macroMutex);
macroEvents.clear();
}
if(hwndMacroList)SendMessage(hwndMacroList,LB_RESETCONTENT,0,0);
g_bMacroRecording=true;
SetWindowTextA(hwndMacroRecBtn,"录制中...");
g_macroMouseHook=SetWindowsHookExA(WH_MOUSE_LL,MacroMouseHookProc,GetModuleHandleA(nullptr),0);
g_macroKeyboardHook=SetWindowsHookExA(WH_KEYBOARD_LL,MacroKeyboardHookProc,GetModuleHandleA(nullptr),0);
if(!g_macroMouseHook||!g_macroKeyboardHook){
MessageBoxA(hwnd,"钩子安装失败","错误",MB_OK);
g_bMacroRecording=false;
SetWindowTextA(hwndMacroRecBtn,"开始录制");
if(g_macroMouseHook){
UnhookWindowsHookEx(g_macroMouseHook);
g_macroMouseHook=nullptr;
}
if(g_macroKeyboardHook){
UnhookWindowsHookEx(g_macroKeyboardHook);
g_macroKeyboardHook=nullptr;
}
}
break;
}
if(id==4002){
g_bMacroRecording=false;
SetWindowTextA(hwndMacroRecBtn,"开始录制");
if(g_macroMouseHook){
UnhookWindowsHookEx(g_macroMouseHook);
g_macroMouseHook=nullptr;
}
if(g_macroKeyboardHook){
UnhookWindowsHookEx(g_macroKeyboardHook);
g_macroKeyboardHook=nullptr;
}
{
lock_guard<mutex>lock(g_macroMutex);
MessageBoxA(hwnd,("录制完成,共 "+to_string(macroEvents.size())+" 个事件").c_str(),"提示",MB_OK);
}
break;
}
if(id==4003){
{
lock_guard<mutex>lock(g_macroMutex);
if(macroEvents.empty()){
MessageBoxA(hwnd,"宏为空","提示",MB_OK);
break;
}
}
if(g_bMacroPlaying)break;
g_bMacroStopPlay=false;
g_bMacroPlaying=true;
SetWindowTextA(hwndMacroPlayBtn,"播放中...");
InvalidateRect(hwndMacroPlayBtn,nullptr,TRUE);
char buf[16];
GetWindowTextA(hwndMacroSpeedEdit,buf,sizeof(buf));
g_macroPlaySpeed=atoi(buf);
if(g_macroPlaySpeed<1)g_macroPlaySpeed=1;
g_bMacroLoop=(SendMessage(hwndMacroLoopCheck,BM_GETCHECK,0,0)==BST_CHECKED);
hMacroPlayThread=CreateThread(nullptr,0,MacroPlayThread,nullptr,0,nullptr);
if(!hMacroPlayThread){
g_bMacroPlaying=false;
SetWindowTextA(hwndMacroPlayBtn,"播放");
MessageBoxA(hwnd,"线程创建失败","错误",MB_OK);
}
break;
}
if(id==4004){
g_bMacroStopPlay=true;
g_bMacroPlaying=false;
SetWindowTextA(hwndMacroPlayBtn,"播放");
InvalidateRect(hwndMacroPlayBtn,nullptr,TRUE);
if(hMacroPlayThread){
WaitForSingleObject(hMacroPlayThread,1000);
CloseHandle(hMacroPlayThread);
hMacroPlayThread=nullptr;
}
break;
}
if(id==4008){
g_bMacroRecordMove=(SendMessage(hwndMacroMoveCheck,BM_GETCHECK,0,0)==BST_CHECKED);
break;
}
break;
}
case WM_MACRO_UPDATE:
SetWindowTextA(hwndMacroPlayBtn,"播放");
InvalidateRect(hwndMacroPlayBtn,nullptr,TRUE);
break;
case WM_CLOSE:{
g_bMacroRecording=false;
g_bMacroPlaying=false;
g_bMacroStopPlay=true;
if(g_macroMouseHook){
UnhookWindowsHookEx(g_macroMouseHook);
g_macroMouseHook=nullptr;
}
if(g_macroKeyboardHook){
UnhookWindowsHookEx(g_macroKeyboardHook);
g_macroKeyboardHook=nullptr;
}
if(hMacroPlayThread){
WaitForSingleObject(hMacroPlayThread,500);
CloseHandle(hMacroPlayThread);
hMacroPlayThread=nullptr;
}
DestroyWindow(hwnd);
break;
}
case WM_DESTROY:
hwndMacro=nullptr;
hwndMacroList=nullptr;
hwndMacroRecBtn=nullptr;
hwndMacroStopRecBtn=nullptr;
hwndMacroPlayBtn=nullptr;
hwndMacroStopPlayBtn=nullptr;
hwndMacroLoopCheck=nullptr;
hwndMacroSpeedLabel=nullptr;
hwndMacroSpeedEdit=nullptr;
hwndMacroMoveCheck=nullptr;
for(int i=0;i<2;i++)if(g_macroFontSlots[i].hFont){
DeleteObject(g_macroFontSlots[i].hFont);
g_macroFontSlots[i].hFont=nullptr;
}
g_macroScale=-1.0;
g_lastMacroFontSize=-1;
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
void CreateMacroWindow(){
if(hwndMacro){
ShowWindow(hwndMacro,SW_SHOW);
SetForegroundWindow(hwndMacro);
return;
}
HINSTANCE hInst=GetModuleHandle(nullptr);
WNDCLASSA wc={};
wc.lpfnWndProc=MacroWndProc;
wc.hInstance=hInst;
wc.lpszClassName="MacroWindowClass";
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wc.hCursor=LoadCursor(nullptr,IDC_ARROW);
RegisterClassA(&wc);
RECT rc={0,0,MACRO_BASE_W,MACRO_BASE_H};
AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);
hwndMacro=CreateWindowA("MacroWindowClass","鼠标宏编辑器",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,
rc.right-rc.left,rc.bottom-rc.top,
hwndMain,nullptr,hInst,nullptr);
SendMessage(hwndMacro,WM_SETICON,ICON_BIG,(LPARAM)g_hAppIcon);
}
DWORD WINAPI ClickerThread(LPVOID){
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);
SetThreadAffinityMask(GetCurrentThread(),1);
int localDelay=clickDelay.load(),localButton=clickButton.load(),localCount=0;
while(!shouldExit){
if(isClicking.load()){
if(sequenceEnabled.load()&&clickDelay.load()>0){
vector<pair<int,int>>pts=clickPoints;
if(pts.empty()){
isClicking=false;
UpdateUI();
continue;
}
for(auto&p:pts){
if(!isClicking.load())break;
SimulateClick(p.first,p.second,clickButton.load());
clickCount.fetch_add(1);
PostMessageA(hwndMain,WM_USER+1,0,0);
int fd=localDelay;
if(jitterEnabled.load()&&localDelay>0){
int r=jitterRange.load();
if(r>0){
fd+=GetJitterOffset(r);
if(fd<1)fd=1;
}
}
if(fd==1)usSleep(1000);
else Sleep(fd);
}
continue;
}
if(isExtremeMode.load()){
while(!shouldExit&&isClicking.load()&&isExtremeMode.load()){
int btn=clickButton.load();
if(btn!=localButton)localButton=btn;
INPUT in[2]={};
in[0].type=INPUT_MOUSE;
in[1].type=INPUT_MOUSE;
switch(localButton){
case 0:
in[0].mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_LEFTUP;
break;
case 1:
in[0].mi.dwFlags=MOUSEEVENTF_MIDDLEDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_MIDDLEUP;
break;
case 2:
in[0].mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_RIGHTUP;
break;
}
SendInput(2,in,sizeof(INPUT));
++localCount;
}
if(localCount>0){
clickCount.fetch_add(localCount);
localCount=0;
PostMessageA(hwndMain,WM_USER+1,0,0);
}
localDelay=clickDelay.load();
continue;
}
int btn=clickButton.load();
if(btn!=localButton)localButton=btn;
INPUT in[2]={};
in[0].type=INPUT_MOUSE;
in[1].type=INPUT_MOUSE;
switch(localButton){
case 0:
in[0].mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_LEFTUP;
break;
case 1:
in[0].mi.dwFlags=MOUSEEVENTF_MIDDLEDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_MIDDLEUP;
break;
case 2:
in[0].mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
in[1].mi.dwFlags=MOUSEEVENTF_RIGHTUP;
break;
}
SendInput(2,in,sizeof(INPUT));
clickCount.fetch_add(1);
static int counter=0;
if(++counter>=10){
counter=0;
PostMessageA(hwndMain,WM_USER+1,0,0);
}
int fd=localDelay;
if(jitterEnabled.load()&&localDelay>0){
int r=jitterRange.load();
if(r>0){
fd+=GetJitterOffset(r);
if(fd<1)fd=1;
}
}
if(fd==1)usSleep(1000);
else Sleep(fd);
localDelay=clickDelay.load();
}else{
if(localCount>0){
clickCount.fetch_add(localCount);
localCount=0;
UpdateCount();
}
Sleep(10);
}
}
return 0;
}
LRESULT CALLBACK KeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode>=0){
KBDLLHOOKSTRUCT*p=(KBDLLHOOKSTRUCT*)lParam;
if((p->vkCode==VK_OEM_PLUS||p->vkCode==VK_ADD)&&(GetKeyState(VK_CAPITAL)&1)){
if(!isClicking.load()){
POINT pt;
GetCursorPos(&pt);
PostMessageA(hwndMain,WM_ADD_COORD,(WPARAM)pt.x,(LPARAM)pt.y);
MessageBeep(MB_OK);
}
}
if(isRecording.load()){
if(p->vkCode>=VK_F1&&p->vkCode<=VK_F12)
return CallNextHookEx(keyboardHook,nCode,wParam,lParam);
int type=(wParam==WM_KEYDOWN||wParam==WM_SYSKEYDOWN)?7:8;
recordedEvents.push_back({type,0,0,(int)p->vkCode,GetTickCount(),false});
}
static bool f6Down=false,f7Down=false,escDown=false;
if(wParam==WM_KEYDOWN||wParam==WM_SYSKEYDOWN){
if(p->vkCode==VK_F6&&!f6Down){
f6Down=true;
isClicking=!isClicking.load();
UpdateUI();
UpdateCount();
return 1;
}
if(p->vkCode==VK_F7&&!f7Down){
f7Down=true;
ToggleRecording();
return 1;
}
if(p->vkCode==VK_ESCAPE&&!escDown){
escDown=true;
if(IsWindowVisible(hwndMain))DestroyWindow(hwndMain);
}
}else if(wParam==WM_KEYUP||wParam==WM_SYSKEYUP){
if(p->vkCode==VK_F6)f6Down=false;
if(p->vkCode==VK_F7)f7Down=false;
if(p->vkCode==VK_ESCAPE)escDown=false;
}
}
return CallNextHookEx(keyboardHook,nCode,wParam,lParam);
}
LRESULT CALLBACK MouseHookProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode>=0&&isRecording.load()){
MSLLHOOKSTRUCT*p=(MSLLHOOKSTRUCT*)lParam;
int cur=GetTickCount(),sx=p->pt.x,sy=p->pt.y,stx,sty;
bool isRel=g_bRelativeMode;
if(g_bRelativeMode){
POINT tl={0,0};
ClientToScreen(hwndMain,&tl);
stx=sx-tl.x;
sty=sy-tl.y;
}else{
stx=sx;
sty=sy;
}
int type=-1,data=0;
if(wParam==WM_MOUSEMOVE)type=0;
else if(wParam==WM_LBUTTONDOWN)type=1;
else if(wParam==WM_LBUTTONUP)type=2;
else if(wParam==WM_RBUTTONDOWN)type=3;
else if(wParam==WM_RBUTTONUP)type=4;
else if(wParam==WM_MBUTTONDOWN)type=5;
else if(wParam==WM_MBUTTONUP)type=6;
else if(wParam==WM_MOUSEWHEEL){
type=9;
data=(short)HIWORD(p->mouseData);
}else if(wParam==WM_MOUSEHWHEEL){
type=10;
data=(short)HIWORD(p->mouseData);
}
if(type!=-1){
if(type==0&&cur-lastMoveTime<5)return CallNextHookEx(mouseHook,nCode,wParam,lParam);
if(type==0)lastMoveTime=cur;
recordedEvents.push_back({type,stx,sty,data,cur,isRel});
}
}
return CallNextHookEx(mouseHook,nCode,wParam,lParam);
}
DWORD WINAPI PlaybackThread(LPVOID){
if(recordedEvents.empty()){
MessageBoxA(hwndMain,"没有录制数据","提示",MB_OK);
return 0;
}
isPlaying=true;
double speedFactor=50.0/(double)playbackSpeed.load();
if(speedFactor<0.01)speedFactor=0.01;
if(speedFactor>100.0)speedFactor=100.0;
DWORD lastTime=0;
bool firstEvent=true;
for(auto&evt:recordedEvents){
if(!isPlaying)break;
if(!firstEvent&&lastTime!=0&&evt.time>=lastTime){
DWORD gap=evt.time-lastTime;
DWORD delayMs=(DWORD)(gap*speedFactor);
if(delayMs>0){
DWORD elapsed=0;
while(elapsed<delayMs&&isPlaying.load()){
DWORD chunk=30;
if(delayMs-elapsed<30)chunk=delayMs-elapsed;
Sleep(chunk);
elapsed+=chunk;
}
}
}
firstEvent=false;
lastTime=evt.time;
int sx,sy;
if(evt.isRelative){
POINT tl={0,0};
ClientToScreen(hwndMain,&tl);
sx=tl.x+evt.x;
sy=tl.y+evt.y;
}else{
sx=evt.x;
sy=evt.y;
}
switch(evt.type){
case 0:
SetCursorPos(sx,sy);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:{
SetCursorPos(sx,sy);
INPUT inp={};
inp.type=INPUT_MOUSE;
switch(evt.type){
case 1:
inp.mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
break;
case 2:
inp.mi.dwFlags=MOUSEEVENTF_LEFTUP;
break;
case 3:
inp.mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
break;
case 4:
inp.mi.dwFlags=MOUSEEVENTF_RIGHTUP;
break;
case 5:
inp.mi.dwFlags=MOUSEEVENTF_MIDDLEDOWN;
break;
case 6:
inp.mi.dwFlags=MOUSEEVENTF_MIDDLEUP;
break;
}
SendInput(1,&inp,sizeof(INPUT));
break;
}
case 7:
case 8:{
INPUT inp={};
inp.type=INPUT_KEYBOARD;
inp.ki.wVk=(WORD)evt.data;
if(evt.type==8)inp.ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&inp,sizeof(INPUT));
break;
}
case 9:
case 10:{
SetCursorPos(sx,sy);
INPUT inp={};
inp.type=INPUT_MOUSE;
inp.mi.dwFlags=(evt.type==9)?MOUSEEVENTF_WHEEL:MOUSEEVENTF_HWHEEL;
inp.mi.mouseData=evt.data;
SendInput(1,&inp,sizeof(INPUT));
break;
}
}
}
isPlaying=false;
PostMessageA(hwndMain,WM_USER+3,0,0);
return 0;
}
void CreateTrayIcon(){
ZeroMemory(&g_nid,sizeof(g_nid));
g_nid.cbSize=sizeof(g_nid);
g_nid.hWnd=hwndMain;
g_nid.uID=1;
g_nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP;
g_nid.uCallbackMessage=WM_TRAYICON;
g_nid.hIcon=g_hAppIcon?g_hAppIcon:LoadIconA(nullptr,IDI_APPLICATION);
strcpy(g_nid.szTip,"自动连点器");
if(!Shell_NotifyIconA(NIM_ADD,&g_nid)){
Sleep(100);
Shell_NotifyIconA(NIM_ADD,&g_nid);
}
g_bTrayCreated=true;
}
void DeleteTrayIcon(){
if(g_bTrayCreated){
Shell_NotifyIconA(NIM_DELETE,&g_nid);
g_bTrayCreated=false;
}
}
void ShowMainWindow(){
ShowWindow(hwndMain,SW_SHOW);
SetForegroundWindow(hwndMain);
}
void StartRecordingActual(){
if(isClicking.load()||sequenceEnabled.load()){
MessageBoxA(hwndMain,"请先停止连点或关闭序列。","提示",MB_OK);
return;
}
recordedEvents.clear();
lastMoveTime=0;
isRecording=true;
SetWindowTextA(hwndRecBtn,"停止录制");
InvalidateRect(hwndRecBtn,nullptr,TRUE);
mouseHook=SetWindowsHookExA(WH_MOUSE_LL,MouseHookProc,GetModuleHandleA(nullptr),0);
if(!mouseHook){
MessageBoxA(hwndMain,"设置鼠标钩子失败!","错误",MB_OK);
isRecording=false;
SetWindowTextA(hwndRecBtn,"开始录制");
}
}
void StopRecording(){
isRecording=false;
SetWindowTextA(hwndRecBtn,"开始录制");
InvalidateRect(hwndRecBtn,nullptr,TRUE);
if(mouseHook){
UnhookWindowsHookEx(mouseHook);
mouseHook=nullptr;
}
MessageBoxA(hwndMain,("录制完成,共 "+to_string(recordedEvents.size())+" 个事件。").c_str(),"提示",MB_OK);
}
void ToggleRecording(){
if(isRecording.load()){
StopRecording();
return;
}
if(g_bRecordingPending){
KillTimer(hwndMain,g_countdownTimerId);
g_bRecordingPending=false;
SetWindowTextA(hwndRecBtn,"开始录制");
return;
}
if(isClicking.load()||sequenceEnabled.load()){
MessageBoxA(hwndMain,"请先停止连点或关闭序列。","提示",MB_OK);
return;
}
g_bRecordingPending=true;
g_currentCountdown=g_countdownSeconds;
char buf[32];
sprintf(buf,"倒计时 %ds",g_currentCountdown);
SetWindowTextA(hwndRecBtn,buf);
InvalidateRect(hwndRecBtn,nullptr,TRUE);
SetTimer(hwndMain,g_countdownTimerId,1000,nullptr);
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch(msg){
case WM_CREATE:{
HINSTANCE hInst=((LPCREATESTRUCT)lParam)->hInstance;
INITCOMMONCONTROLSEX icex={sizeof(icex),ICC_STANDARD_CLASSES};
InitCommonControlsEx(&icex);
hBrushBg=CreateSolidBrush(RGB(243,243,243));
hBrushWhite=CreateSolidBrush(RGB(255,255,255));
int baseX=24,baseY=20,colW=320,gap=30;
int statusW=colW*3+gap*2,statusH=52;
hwndStatusBar=CreateWindowA("STATIC","",WS_CHILD|WS_VISIBLE|SS_OWNERDRAW,
baseX,baseY,statusW,statusH,hwnd,(HMENU)ID_STATUS_BAR,hInst,nullptr);
int y=baseY+statusH+24;
int x1=baseX,x2=baseX+colW+gap,x3=baseX+(colW+gap)*2;
hwndTitle1=CreateWindowA("STATIC","基础设置",WS_CHILD|WS_VISIBLE,x1,y,colW,26,hwnd,nullptr,hInst,nullptr);
hwndTitle2=CreateWindowA("STATIC","序列点击",WS_CHILD|WS_VISIBLE,x2,y,colW,26,hwnd,nullptr,hInst,nullptr);
hwndTitle3=CreateWindowA("STATIC","录制回放",WS_CHILD|WS_VISIBLE,x3,y,colW,26,hwnd,nullptr,hInst,nullptr);
hwndLblDelay=CreateWindowA("STATIC","间隔(ms)",WS_CHILD|WS_VISIBLE,x1,y+42,80,24,hwnd,nullptr,hInst,nullptr);
hwndDelayEdit=CreateWindowA("EDIT","100",WS_CHILD|WS_VISIBLE|ES_NUMBER,
x1+8,y+70,64,24,hwnd,(HMENU)1001,hInst,nullptr);
SendMessage(hwndDelayEdit,EM_SETMARGINS,EC_LEFTMARGIN|EC_RIGHTMARGIN,MAKELPARAM(6,6));
SetWindowSubclass(hwndDelayEdit,ModernInputProc,0,0);
hwndDelayLabel=CreateWindowA("STATIC","当前: 100 ms",WS_CHILD|WS_VISIBLE|SS_LEFT,
x1+95,y+74,190,24,hwnd,nullptr,hInst,nullptr);
hwndLblButton=CreateWindowA("STATIC","按键",WS_CHILD|WS_VISIBLE,x1,y+118,80,24,hwnd,nullptr,hInst,nullptr);
hwndButtonCombo=CreateWindowA("COMBOBOX","",
WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST|CBS_OWNERDRAWFIXED|CBS_HASSTRINGS,
x1,y+144,140,300,hwnd,(HMENU)1002,hInst,nullptr);
SendMessageA(hwndButtonCombo,CB_ADDSTRING,0,(LPARAM)"左键");
SendMessageA(hwndButtonCombo,CB_ADDSTRING,0,(LPARAM)"中键");
SendMessageA(hwndButtonCombo,CB_ADDSTRING,0,(LPARAM)"右键");
SendMessage(hwndButtonCombo,CB_SETCURSEL,0,0);
SetWindowTheme(hwndButtonCombo,L"",L"");
SetWindowSubclass(hwndButtonCombo,ComboBoxProc,0,0);
hwndJitterCheck=CreateWindowA("BUTTON","抖动",WS_CHILD|WS_VISIBLE|BS_CHECKBOX,
x1,y+188,90,28,hwnd,(HMENU)1003,hInst,nullptr);
AttachToggle(hwndJitterCheck,false);
hwndLblJitter=CreateWindowA("STATIC","范围",WS_CHILD|WS_VISIBLE,
x1+98,y+194,36,18,hwnd,nullptr,hInst,nullptr);
hwndJitterEdit=CreateWindowA("EDIT","10",WS_CHILD|WS_VISIBLE|ES_NUMBER,
x1+146,y+192,34,20,hwnd,(HMENU)1004,hInst,nullptr);
SendMessage(hwndJitterEdit,EM_SETMARGINS,EC_LEFTMARGIN|EC_RIGHTMARGIN,MAKELPARAM(4,4));
SetWindowSubclass(hwndJitterEdit,ModernInputProc,0,0);
hwndRelativeCheck=CreateWindowA("BUTTON","相对窗口",WS_CHILD|WS_VISIBLE|BS_CHECKBOX,
x1,y+224,140,28,hwnd,(HMENU)1010,hInst,nullptr);
AttachToggle(hwndRelativeCheck,true);
hwndSeqCheck=CreateWindowA("BUTTON","启用序列",WS_CHILD|WS_VISIBLE|BS_CHECKBOX,
x2,y+42,150,28,hwnd,(HMENU)1005,hInst,nullptr);
AttachToggle(hwndSeqCheck,false);
hwndLblCoord=CreateWindowA("STATIC","坐标 (x,y;x,y)",WS_CHILD|WS_VISIBLE,x2,y+82,180,24,hwnd,nullptr,hInst,nullptr);
hwndSeqEdit=CreateWindowA("EDIT","100,200;300,400",WS_CHILD|WS_VISIBLE,
x2+8,y+110,colW-26,26,hwnd,(HMENU)1006,hInst,nullptr);
SendMessage(hwndSeqEdit,EM_SETMARGINS,EC_LEFTMARGIN|EC_RIGHTMARGIN,MAKELPARAM(6,6));
SetWindowSubclass(hwndSeqEdit,ModernInputProc,0,0);
char initBuf[512];
GetWindowTextA(hwndSeqEdit,initBuf,sizeof(initBuf));
ParseCoordinates(initBuf);
hwndRecBtn=CreateWindowA("BUTTON","开始录制",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x3,y+42,140,44,hwnd,(HMENU)1007,hInst,nullptr);
hwndPlayBtn=CreateWindowA("BUTTON","回放",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x3+150,y+42,140,44,hwnd,(HMENU)1008,hInst,nullptr);
hwndLblSpeed=CreateWindowA("STATIC","速度(ms)",WS_CHILD|WS_VISIBLE,x3,y+100,80,24,hwnd,nullptr,hInst,nullptr);
hwndSpeedSlider=CreateWindowA("EDIT","50",WS_CHILD|WS_VISIBLE|ES_NUMBER,
x3+8,y+128,64,24,hwnd,(HMENU)1009,hInst,nullptr);
SendMessage(hwndSpeedSlider,EM_SETMARGINS,EC_LEFTMARGIN|EC_RIGHTMARGIN,MAKELPARAM(6,6));
SetWindowSubclass(hwndSpeedSlider,ModernInputProc,0,0);
y+=280;
hwndTitle4=CreateWindowA("STATIC","鼠标宏",WS_CHILD|WS_VISIBLE,x1,y,colW,26,hwnd,nullptr,hInst,nullptr);
hwndMacroBtn=CreateWindowA("BUTTON","打开宏编辑器",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
x1,y+42,180,46,hwnd,(HMENU)2005,hInst,nullptr);
hwndLblMacroHint=CreateWindowA("STATIC","录制鼠标/键盘动作,支持循环播放",WS_CHILD|WS_VISIBLE|SS_LEFT,
x1,y+98,colW,26,hwnd,nullptr,hInst,nullptr);
y+=140;
hwndStartBtn=CreateWindowA("BUTTON","开始 (F6)",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
baseX,y,160,50,hwnd,(HMENU)1,hInst,nullptr);
hwndStopBtn=CreateWindowA("BUTTON","停止 (F6)",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
baseX+180,y,160,50,hwnd,(HMENU)2,hInst,nullptr);
hwndExitBtn=CreateWindowA("BUTTON","退出",WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
baseX+360,y,160,50,hwnd,(HMENU)3,hInst,nullptr);
y+=70;
hwndLblHotkey=CreateWindowA("STATIC",
"F6 切换 | F7 录制 | ESC 退出 | CapsLock + [+] 记录坐标",
WS_CHILD|WS_VISIBLE|SS_CENTER,baseX,y,statusW,26,hwnd,nullptr,hInst,nullptr);
g_fontGroups[3].push_back(hwndDelayLabel);
g_fontGroups[3].push_back(hwndLblDelay);
g_fontGroups[3].push_back(hwndLblButton);
g_fontGroups[3].push_back(hwndLblJitter);
g_fontGroups[3].push_back(hwndLblCoord);
g_fontGroups[3].push_back(hwndLblSpeed);
g_fontGroups[3].push_back(hwndLblMacroHint);
g_fontGroups[3].push_back(hwndLblHotkey);
g_fontGroups[4].push_back(hwndTitle1);
g_fontGroups[4].push_back(hwndTitle2);
g_fontGroups[4].push_back(hwndTitle3);
g_fontGroups[4].push_back(hwndTitle4);
g_fontGroups[5].push_back(hwndRecBtn);
g_fontGroups[5].push_back(hwndPlayBtn);
g_fontGroups[5].push_back(hwndStartBtn);
g_fontGroups[5].push_back(hwndStopBtn);
g_fontGroups[5].push_back(hwndExitBtn);
g_fontGroups[5].push_back(hwndMacroBtn);
g_fontGroups[5].push_back(hwndJitterCheck);
g_fontGroups[5].push_back(hwndSeqCheck);
g_fontGroups[5].push_back(hwndRelativeCheck);
g_fontGroups[6].push_back(hwndDelayEdit);
g_fontGroups[6].push_back(hwndJitterEdit);
g_fontGroups[6].push_back(hwndSpeedSlider);
g_fontGroups[6].push_back(hwndSeqEdit);
g_fontGroups[6].push_back(hwndButtonCombo);
keyboardHook=SetWindowsHookExA(WH_KEYBOARD_LL,KeyboardHookProc,GetModuleHandleA(nullptr),0);
if(!keyboardHook)MessageBoxA(hwnd,"键盘钩子安装失败,热键将不可用!","错误",MB_OK);
g_bInitDone=true;
clickDelay=100;
clickButton=0;
isExtremeMode=false;
RECT rc;
GetClientRect(hwnd,&rc);
ApplyLayout(rc.right,rc.bottom);
UpdateUI();
UpdateCount();
break;
}
case WM_SIZE:{
int cw=LOWORD(lParam),ch=HIWORD(lParam);
if(cw>0&&ch>0)ApplyLayout(cw,ch);
break;
}
case WM_GETMINMAXINFO:{
MINMAXINFO*mmi=(MINMAXINFO*)lParam;
RECT rc={0,0,(int)(BASE_W*MIN_SCALE),(int)(BASE_H*MIN_SCALE)};
AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);
mmi->ptMinTrackSize.x=rc.right-rc.left;
mmi->ptMinTrackSize.y=rc.bottom-rc.top;
break;
}
case WM_MEASUREITEM:{
LPMEASUREITEMSTRUCT mis=(LPMEASUREITEMSTRUCT)lParam;
if(mis->CtlType==ODT_COMBOBOX){
double s=g_scale>0?g_scale:1.0;
int h=(int)(28*s);
if(h<20)h=20;
if(h>48)h=48;
mis->itemHeight=h;
return TRUE;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
PaintInputBorders(hdc);
EndPaint(hwnd,&ps);
return 0;
}
case WM_ERASEBKGND:{
HDC hdc=(HDC)wParam;
RECT rc;
GetClientRect(hwnd,&rc);
FillRect(hdc,&rc,hBrushBg);
return 1;
}
case WM_DRAWITEM:{
LPDRAWITEMSTRUCT dis=(LPDRAWITEMSTRUCT)lParam;
if(dis->hwndItem==hwndStatusBar){
DrawStatusBar(dis);
}else if(dis->CtlType==ODT_COMBOBOX){
DrawComboListItem(dis);
}else{
DrawRoundButton(dis);
}
return TRUE;
}
case WM_CTLCOLORSTATIC:{
HDC hdc=(HDC)wParam;
SetBkMode(hdc,TRANSPARENT);
SetTextColor(hdc,RGB(26,26,26));
return(LRESULT)hBrushBg;
}
case WM_CTLCOLOREDIT:{
HDC hdc=(HDC)wParam;
SetTextColor(hdc,RGB(26,26,26));
SetBkColor(hdc,RGB(255,255,255));
return(LRESULT)hBrushWhite;
}
case WM_CTLCOLORLISTBOX:{
HDC hdc=(HDC)wParam;
SetTextColor(hdc,RGB(26,26,26));
SetBkColor(hdc,RGB(255,255,255));
return(LRESULT)hBrushWhite;
}
case WM_EXECUTE_CMD:{
char*cmd=(char*)wParam;
if(cmd){
ExecuteCommand(string(cmd));
delete[]cmd;
}
break;
}
case WM_COPYDATA:{
COPYDATASTRUCT*pcds=(COPYDATASTRUCT*)lParam;
if(pcds->dwData==1){
string cmd=(char*)pcds->lpData;
char*c=new char[cmd.size()+1];
strcpy(c,cmd.c_str());
PostMessage(hwnd,WM_EXECUTE_CMD,(WPARAM)c,0);
ShowMainWindow();
return TRUE;
}
break;
}
case WM_ADD_COORD:{
if(isClicking.load())break;
int x=(int)wParam,y=(int)lParam;
char buf[512]={0};
GetWindowTextA(hwndSeqEdit,buf,sizeof(buf)-1);
string str=buf;
if(!str.empty())str+=";";
char c[32];
sprintf(c,"%d,%d",x,y);
str+=c;
SetWindowTextA(hwndSeqEdit,str.c_str());
clickPoints.push_back({x,y});
break;
}
case WM_USER+3:
SetWindowTextA(hwndPlayBtn,"回放");
InvalidateRect(hwndPlayBtn,nullptr,TRUE);
break;
case WM_SHOWWINDOW:
if(wParam==TRUE){
if(!g_bTrayCreated)CreateTrayIcon();
SetTimer(hwnd,TIMER_CPS,500,nullptr);
}
break;
case WM_TRAYICON:{
if(lParam==WM_LBUTTONDBLCLK)ShowMainWindow();
else if(lParam==WM_RBUTTONUP){
POINT pt;
GetCursorPos(&pt);
HMENU hMenu=CreatePopupMenu();
AppendMenuA(hMenu,MF_STRING,TRAY_SHOW,"显示窗口");
AppendMenuA(hMenu,MF_STRING,TRAY_HIDE,"隐藏窗口");
AppendMenuA(hMenu,MF_SEPARATOR,0,NULL);
AppendMenuA(hMenu,MF_STRING,TRAY_TOGGLE,isClicking.load()?"暂停连点":"继续连点");
AppendMenuA(hMenu,MF_SEPARATOR,0,NULL);
AppendMenuA(hMenu,MF_STRING,TRAY_EXIT,"退出程序");
SetForegroundWindow(hwnd);
TrackPopupMenu(hMenu,TPM_RIGHTBUTTON,pt.x,pt.y,0,hwnd,NULL);
PostMessageA(hwnd,WM_NULL,0,0);
DestroyMenu(hMenu);
}
break;
}
case WM_TIMER:{
if(wParam==TIMER_CPS){
if(!isExtremeMode.load()){
int total=clickCount.load();
static int last=0;
static double lastTime=0.0;
LARGE_INTEGER freq,now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&now);
double t=(double)now.QuadPart/freq.QuadPart;
if(lastTime!=0.0&&(t-lastTime)>0.01)
currentCPS.store((total-last)/(t-lastTime));
last=total;
lastTime=t;
}
UpdateCount();
}else if(wParam==g_countdownTimerId){
if(g_bRecordingPending){
g_currentCountdown--;
if(g_currentCountdown<=0){
KillTimer(hwnd,g_countdownTimerId);
g_bRecordingPending=false;
SetWindowTextA(hwndRecBtn,"开始录制");
StartRecordingActual();
}else{
char buf[32];
sprintf(buf,"倒计时 %ds",g_currentCountdown);
SetWindowTextA(hwndRecBtn,buf);
}
}else KillTimer(hwnd,g_countdownTimerId);
}
break;
}
case WM_COMMAND:{
int id=LOWORD(wParam),code=HIWORD(wParam);
if(id==TRAY_SHOW){
ShowMainWindow();
break;
}
if(id==TRAY_HIDE){
ShowWindow(hwndMain,SW_HIDE);
break;
}
if(id==TRAY_TOGGLE){
isClicking=!isClicking.load();
UpdateUI();
UpdateCount();
break;
}
if(id==TRAY_EXIT){
DestroyWindow(hwndMain);
break;
}
if(id==2005){
CreateMacroWindow();
break;
}
if(id==1010&&code==BN_CLICKED){
g_bRelativeMode=(SendMessage(hwndRelativeCheck,BM_GETCHECK,0,0)==BST_CHECKED);
break;
}
if(id==1003&&code==BN_CLICKED){
jitterEnabled.store(SendMessage(hwndJitterCheck,BM_GETCHECK,0,0)==BST_CHECKED);
break;
}
if(id==1004&&code==EN_CHANGE&&g_bInitDone){
char buf[16]={0};
GetWindowTextA(hwndJitterEdit,buf,sizeof(buf));
if(buf[0]){
int v=atoi(buf);
if(v<0)v=0;
if(v>100)v=100;
jitterRange.store(v);
}
break;
}
if(id==1005&&code==BN_CLICKED){
if(isClicking.load()){
SendMessage(hwndSeqCheck,BM_SETCHECK,sequenceEnabled.load()?BST_CHECKED:BST_UNCHECKED,0);
break;
}
sequenceEnabled.store(SendMessage(hwndSeqCheck,BM_GETCHECK,0,0)==BST_CHECKED);
break;
}
if(id==1006&&code==EN_KILLFOCUS){
if(isClicking.load())break;
char buf[512]={0};
GetWindowTextA(hwndSeqEdit,buf,sizeof(buf)-1);
ParseCoordinates(buf);
break;
}
if(id==1007){
ToggleRecording();
break;
}
if(id==1008){
if(isPlaying.load()){
isPlaying=false;
SetWindowTextA(hwndPlayBtn,"回放");
InvalidateRect(hwndPlayBtn,nullptr,TRUE);
break;
}
if(isClicking.load()||sequenceEnabled.load()){
MessageBoxA(hwnd,"请先停止连点或关闭序列。","提示",MB_OK);
break;
}
if(recordedEvents.empty()){
MessageBoxA(hwnd,"没有录制数据。","提示",MB_OK);
break;
}
SetWindowTextA(hwndPlayBtn,"停止");
InvalidateRect(hwndPlayBtn,nullptr,TRUE);
hPlaybackThread=CreateThread(nullptr,0,PlaybackThread,nullptr,0,nullptr);
if(!hPlaybackThread){
MessageBoxA(hwnd,"创建回放线程失败。","错误",MB_OK);
SetWindowTextA(hwndPlayBtn,"回放");
}
break;
}
if(id==1009&&code==EN_CHANGE){
char buf[16]={0};
GetWindowTextA(hwndSpeedSlider,buf,sizeof(buf));
if(buf[0]){
int v=atoi(buf);
if(v<1)v=1;
if(v>1000)v=1000;
playbackSpeed.store(v);
}
break;
}
if(id==1002){
if(code==CBN_SELCHANGE){
int sel=(int)SendMessage(hwndButtonCombo,CB_GETCURSEL,0,0);
if(sel>=0&&sel<=2){
clickButton.store(sel);
InvalidateRect(hwndButtonCombo,nullptr,TRUE);
}
}
break;
}
if(id==1001&&code==EN_CHANGE&&g_bInitDone){
char buf[16]={0};
GetWindowTextA(hwndDelayEdit,buf,sizeof(buf));
if(buf[0]=='\0')break;
int v=atoi(buf);
if(v<0)v=0;
if(v>10000)v=10000;
if(v==0){
int ret=MessageBoxA(hwnd,"进入极速模式(延迟0),CPU占用略高。\n确定继续?","提示",MB_OKCANCEL);
if(ret==IDCANCEL){
v=100;
char tmp[16];
sprintf(tmp,"%d",v);
SetWindowTextA(hwndDelayEdit,tmp);
}
}
clickDelay.store(v);
isExtremeMode.store(v==0);
UpdateDelayDisplay();
UpdateCount();
break;
}
switch(id){
case 1:{
char buf[512]={0};
GetWindowTextA(hwndSeqEdit,buf,sizeof(buf)-1);
ParseCoordinates(buf);
if(sequenceEnabled.load()&&clickPoints.empty()){
MessageBoxA(hwnd,"序列已启用但坐标为空,已自动禁用。","提示",MB_OK);
sequenceEnabled=false;
SendMessage(hwndSeqCheck,BM_SETCHECK,BST_UNCHECKED,0);
}
isClicking=true;
UpdateUI();
break;
}
case 2:
isClicking=false;
UpdateUI();
UpdateCount();
break;
case 3:
DestroyWindow(hwnd);
break;
case 1001:
ShowMainWindow();
break;
}
break;
}
case WM_USER+1:
UpdateCount();
break;
case WM_CLOSE:
ShowWindow(hwnd,SW_HIDE);
return 0;
case WM_DESTROY:{
KillTimer(hwnd,TIMER_CPS);
if(g_bRecordingPending){
KillTimer(hwnd,g_countdownTimerId);
g_bRecordingPending=false;
}
if(hwndMacro)DestroyWindow(hwndMacro);
DeleteTrayIcon();
if(mouseHook)UnhookWindowsHookEx(mouseHook);
if(keyboardHook)UnhookWindowsHookEx(keyboardHook);
if(hPlaybackThread){
WaitForSingleObject(hPlaybackThread,500);
CloseHandle(hPlaybackThread);
hPlaybackThread=nullptr;
}
if(g_hBlankIcon){
DestroyIcon(g_hBlankIcon);
g_hBlankIcon=nullptr;
}
g_hAppIcon=nullptr;
for(int i=0;i<7;i++)if(g_fontSlots[i].hFont){
DeleteObject(g_fontSlots[i].hFont);
g_fontSlots[i].hFont=nullptr;
}
for(int i=0;i<2;i++)if(g_macroFontSlots[i].hFont){
DeleteObject(g_macroFontSlots[i].hFont);
g_macroFontSlots[i].hFont=nullptr;
}
if(hBrushWhite){
DeleteObject(hBrushWhite);
hBrushWhite=nullptr;
}
if(hBrushBg) {
DeleteObject(hBrushBg);
hBrushBg =nullptr;
}
g_bInitDone=false;
shouldExit=true;
g_lastMainFontSize=-1;
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpCmd,int nShow){
(void)hPrev;
(void)nShow;
SetDPIAwareness();
Gdiplus::GdiplusStartupInput gpsi;
Gdiplus::GdiplusStartup(&g_gdiplusToken,&gpsi,nullptr);
InitDirect2D();
bool onlyHelp=false;
if(lpCmd&&*lpCmd){
string cmd=lpCmd;
size_t s=cmd.find_first_not_of(" \t");
if(s!=string::npos){
cmd=cmd.substr(s);
size_t e=cmd.find_last_not_of(" \t");
if(e!=string::npos)cmd=cmd.substr(0,e+1);
if(cmd=="--help"||cmd=="-h")onlyHelp=true;
}
}
if(onlyHelp){
PrintCommandHelp();
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 0;
}
HANDLE hMutex=CreateMutexA(NULL,FALSE,"AutoClickerMutex");
if(GetLastError()==ERROR_ALREADY_EXISTS){
HWND hExist=FindWindowA("AutoClickerClass",NULL);
if(hExist){
if(lpCmd&&*lpCmd){
COPYDATASTRUCT cds;
cds.dwData=1;
cds.cbData=(DWORD)strlen(lpCmd)+1;
cds.lpData=lpCmd;
SendMessage(hExist,WM_COPYDATA,0,(LPARAM)&cds);
}
ShowWindow(hExist,SW_SHOW);
SetForegroundWindow(hExist);
}
CloseHandle(hMutex);
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 0;
}
timeBeginPeriod(1);
g_hAppIcon=LoadCustomIcon();
WNDCLASSA wc={};
wc.lpfnWndProc=WndProc;
wc.hInstance=hInst;
wc.lpszClassName="AutoClickerClass";
wc.hCursor=LoadCursor(nullptr,IDC_ARROW);
wc.hbrBackground=NULL;
wc.hIcon=g_hAppIcon;
if(!RegisterClassA(&wc)){
MessageBoxA(nullptr,"注册窗口类失败","错误",MB_OK);
CloseHandle(hMutex);
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 1;
}
RECT rc={0,0,BASE_W,BASE_H};
AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);
hwndMain=CreateWindowA("AutoClickerClass","自动连点器",
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
rc.right-rc.left,rc.bottom-rc.top,
nullptr,nullptr,hInst,nullptr);
if(!hwndMain){
MessageBoxA(nullptr,"创建窗口失败","错误",MB_OK);
CloseHandle(hMutex);
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 1;
}
BYTE andMask[1]={0xFF},xorMask[1]={0x00};
g_hBlankIcon=CreateIcon(hInst,1,1,1,1,andMask,xorMask);
SendMessageA(hwndMain,WM_SETICON,ICON_SMALL,(LPARAM)g_hBlankIcon);
SendMessageA(hwndMain,WM_SETICON,ICON_BIG, (LPARAM)g_hAppIcon);
SetWindowPos(hwndMain,nullptr,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
if(lpCmd&&*lpCmd){
char*c=new char[strlen(lpCmd)+1];
strcpy(c,lpCmd);
PostMessage(hwndMain,WM_EXECUTE_CMD,(WPARAM)c,0);
}
ShowWindow(hwndMain,SW_SHOW);
UpdateWindow(hwndMain);
HANDLE hClicker=CreateThread(nullptr,0,ClickerThread,nullptr,0,nullptr);
if(!hClicker){
MessageBoxA(nullptr,"创建线程失败","错误",MB_OK);
CloseHandle(hMutex);
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 1;
}
MSG msg;
while(GetMessageA(&msg,nullptr,0,0)){
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
shouldExit=true;
WaitForSingleObject(hClicker,INFINITE);
CloseHandle(hClicker);
timeEndPeriod(1);
CloseHandle(hMutex);
ShutdownDirect2D();
Gdiplus::GdiplusShutdown(g_gdiplusToken);
return 0;
}
代码(V1.0)
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#include <windows.h>
#include <commctrl.h>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <random>
#include <vector>
#include <string>
#include <sstream>
#include <tuple>
#include <mmsystem.h>
#include <algorithm>
#include <cctype>
#include <mutex>
#include <chrono>
#include <gdiplus.h>
#include <shellapi.h>
#include <iostream>
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "gdiplus.lib")
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib")
#endif
using namespace std;
using namespace Gdiplus;
void SetDPIAwareness() {
HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
if (hUser32) {
typedef BOOL (WINAPI *SetDPIAwarenessContextFunc)(PVOID);
SetDPIAwarenessContextFunc pV2 = (SetDPIAwarenessContextFunc)GetProcAddress(hUser32, "SetProcessDPIAwarenessContext");
if (pV2) { if (pV2((PVOID)-4)) return; }
}
HMODULE hShcore = LoadLibraryW(L"Shcore.dll");
if (hShcore) {
typedef HRESULT (WINAPI *SetDPIAwarenessFunc)(int);
SetDPIAwarenessFunc pPerMonitor = (SetDPIAwarenessFunc)GetProcAddress(hShcore, "SetProcessDpiAwareness");
if (pPerMonitor) { if (SUCCEEDED(pPerMonitor(2))) { FreeLibrary(hShcore); return; } }
FreeLibrary(hShcore);
}
if (hUser32) {
typedef BOOL (WINAPI *SetDPIAwareFunc)();
SetDPIAwareFunc pOld = (SetDPIAwareFunc)GetProcAddress(hUser32, "SetProcessDPIAware");
if (pOld) pOld();
}
}
atomic<bool> g_ic(false);
atomic<bool> g_se(false);
atomic<int> g_cd(100);
atomic<int> g_cc(0);
atomic<double> g_cps(0.0);
atomic<int> g_cb(0);
atomic<bool> g_em(false);
atomic<bool> g_je(false);
atomic<int> g_jr(10);
atomic<bool> g_seq(false);
vector<pair<int,int>> g_pts;
atomic<bool> g_rec(false);
atomic<bool> g_ply(false);
atomic<int> g_ps(50);
struct RecordedEvent {
int type, x, y, data, time;
bool isRelative;
};
vector<RecordedEvent> g_evt;
HHOOK g_mh = nullptr;
HANDLE g_hPlay = nullptr;
int g_lmt = 0;
HHOOK g_kh = nullptr;
HWND g_hM = nullptr;
HWND g_hS = nullptr;
HWND g_hC = nullptr;
HWND g_hP = nullptr;
HWND g_hDE = nullptr;
HWND g_hDL = nullptr;
HWND g_hBC = nullptr;
HWND g_hJC = nullptr;
HWND g_hJE = nullptr;
HWND g_hSC = nullptr;
HWND g_hSE = nullptr;
HWND g_hRB = nullptr;
HWND g_hPB = nullptr;
HWND g_hSS = nullptr;
HWND g_hSB = nullptr;
HWND g_hTB = nullptr;
HWND g_hEB = nullptr;
HWND g_hRC = nullptr;
HWND g_hME = nullptr;
HWND g_hMC = nullptr;
HWND g_hMI = nullptr;
HWND g_hMB = nullptr;
HWND g_hMS = nullptr;
HWND g_hMac = nullptr;
HFONT g_fS = nullptr;
HFONT g_fN = nullptr;
HBRUSH g_bW = nullptr;
HBRUSH g_bS = nullptr;
bool g_rel = true;
bool g_recPend = false;
int g_cdSec = 3;
UINT g_cdTimer = 1004;
int g_curCd = 0;
bool g_mpAct = false;
int g_mpCnt = 5;
int g_mpInt = 100;
int g_mpBtn = 0;
HHOOK g_mpHook = nullptr;
HWND g_hOv = nullptr;
atomic<bool> g_mpStop(false);
mutex g_mpMtx;
bool g_mpBusy = false;
HWND g_hMacW = nullptr;
bool g_macRec = false;
bool g_macPlay = false;
vector<RecordedEvent> g_macEvt;
HHOOK g_macMH = nullptr;
HHOOK g_macKH = nullptr;
HANDLE g_macThr = nullptr;
int g_macSpeed = 50;
bool g_macLoop = false;
bool g_macMove = false;
HWND g_hMacL = nullptr;
HWND g_hMacPB = nullptr;
atomic<bool> g_macStop(false);
mutex g_macMtx;
#define HOTKEY_F6 1
#define HOTKEY_ESC 2
#define HOTKEY_F7 3
#define WM_TRAYICON (WM_USER + 100)
#define TIMER_CPS 1003
#define WM_ADD_COORD (WM_USER + 2)
#define WM_MACRO_UPDATE (WM_USER + 4)
#define WM_EXECUTE_CMD (WM_USER + 5)
#define TRAY_SHOW 5001
#define TRAY_HIDE 5002
#define TRAY_TOGGLE 5003
#define TRAY_EXIT 5004
bool g_init = false;
bool g_tray = false;
NOTIFYICONDATAA g_nid = {};
HICON g_icon = nullptr;
bool g_hookHot = false;
thread_local mt19937 rng((unsigned int)time(nullptr) ^ (unsigned int)GetCurrentThreadId());
int GetJitterOffset(int range) {
if (range <= 0) return 0;
uniform_int_distribution<int> dist(-range, range);
return dist(rng);
}
HICON LoadCustomIcon() {
HICON hIcon = (HICON)LoadImageA(GetModuleHandleA(nullptr), "clicker.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
if (hIcon) return hIcon;
hIcon = (HICON)LoadImageA(GetModuleHandleA(nullptr), MAKEINTRESOURCEA(101), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
if (hIcon) return hIcon;
return LoadIconA(nullptr, IDI_APPLICATION);
}
void UpdateDelayDisplay() {
char buf[32]; sprintf(buf, "当前: %d ms", g_cd.load());
SetWindowTextA(g_hDL, buf);
}
void UpdateUI() {
bool running = g_ic.load();
SetWindowTextA(g_hS, running ? "● 运行中" : "● 已停止");
InvalidateRect(g_hS, nullptr, TRUE);
EnableWindow(g_hSB, !running);
EnableWindow(g_hTB, running);
UpdateDelayDisplay();
EnableWindow(g_hSC, !running);
EnableWindow(g_hSE, !running);
}
void UpdateCount() {
if (g_em.load()) {
SetWindowTextA(g_hC, "极速模式");
SetWindowTextA(g_hP, "无统计");
return;
}
char buf[64]; sprintf(buf, "已点击: %d 次", g_cc.load());
SetWindowTextA(g_hC, buf);
}
void UpdateCPSDisplay() {
if (g_em.load()) {
SetWindowTextA(g_hP, "无统计");
return;
}
char buf[32]; sprintf(buf, "CPS: %.1f", g_cps.load());
SetWindowTextA(g_hP, buf);
}
void usSleep(long long us) {
if (us <= 0) return;
if (us > 2000) { Sleep((DWORD)(us / 1000)); return; }
LARGE_INTEGER freq, start, now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
long long elapsed = 0;
while (elapsed < us) {
QueryPerformanceCounter(&now);
elapsed = (now.QuadPart - start.QuadPart) * 1000000 / freq.QuadPart;
if (elapsed >= us) break;
long long remaining = us - elapsed;
if (remaining > 500) SwitchToThread();
}
}
void SimulateClick(int x, int y, int button) {
SetCursorPos(x, y);
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[1].type = INPUT_MOUSE;
switch (button) {
case 0: input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 1: input[0].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; input[1].mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
case 2: input[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
}
SendInput(2, input, sizeof(INPUT));
}
void ParseCoordinates(const string& str) {
g_pts.clear();
string s = str;
s.erase(remove_if(s.begin(), s.end(), ::isspace), s.end());
size_t pos = 0;
while ((pos = s.find(';')) != string::npos) {
string token = s.substr(0, pos);
s.erase(0, pos + 1);
int comma = token.find(',');
if (comma != string::npos) {
g_pts.push_back({atoi(token.substr(0, comma).c_str()), atoi(token.substr(comma+1).c_str())});
}
}
if (!s.empty()) {
int comma = s.find(',');
if (comma != string::npos) {
g_pts.push_back({atoi(s.substr(0, comma).c_str()), atoi(s.substr(comma+1).c_str())});
}
}
}
void ToggleRecording();
void StartRecordingActual();
void StopRecording();
void PrintCommandHelp() {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE) {
AllocConsole();
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (hConsole != INVALID_HANDLE_VALUE) {
const char* help =
"自动连点器命令行选项:\n"
" --start, -s 启动连点\n"
" --stop, -t 停止连点\n"
" --toggle, -tg 切换连点状态\n"
" --delay <ms>, -d <ms> 设置点击间隔(毫秒)\n"
" --button <0|1|2>, -b 设置按键(0左,1中,2右)\n"
" --sequence <x,y;x,y> 设置序列坐标(分号分隔)\n"
" --enable-seq 启用序列模式\n"
" --disable-seq 禁用序列模式\n"
" --jitter <range>, -j 启用抖动并设置范围(像素)\n"
" --relative 使用相对窗口坐标\n"
" --absolute 使用绝对屏幕坐标\n"
" --show 显示主窗口\n"
" --hide 隐藏主窗口到托盘\n"
" --exit, -e 退出程序\n"
" --help, -h 显示此帮助(并退出)\n"
"\n示例:\n"
" clicker.exe --start --delay 50 --button 1\n"
" clicker.exe --sequence \"100,200;300,400\" --enable-seq\n"
" clicker.exe --exit\n";
DWORD written;
WriteConsoleA(hConsole, help, strlen(help), &written, NULL);
}
}
void ExecuteCommand(const string& cmd) {
vector<string> tokens;
string token;
bool inQuotes = false;
for (char c : cmd) {
if (c == '"') { inQuotes = !inQuotes; continue; }
if (isspace(c) && !inQuotes) {
if (!token.empty()) { tokens.push_back(token); token.clear(); }
} else {
token += c;
}
}
if (!token.empty()) tokens.push_back(token);
for (size_t i = 0; i < tokens.size(); ++i) {
string arg = tokens[i];
transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
if (arg == "--start" || arg == "-s") {
g_ic = true;
UpdateUI();
strcpy(g_nid.szTip, "自动连点器 (运行中)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
}
else if (arg == "--stop" || arg == "-t") {
g_ic = false;
UpdateUI();
UpdateCount();
strcpy(g_nid.szTip, "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
}
else if (arg == "--toggle" || arg == "-tg") {
bool running = g_ic.load();
g_ic = !running;
UpdateUI();
if (!running) UpdateCount();
strcpy(g_nid.szTip, g_ic.load() ? "自动连点器 (运行中)" : "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
}
else if ((arg == "--delay" || arg == "-d") && i+1 < tokens.size()) {
int val = atoi(tokens[++i].c_str());
if (val < 0) val = 0;
if (val > 10000) val = 10000;
g_cd = val;
g_em = (val == 0);
char tmp[16]; sprintf(tmp, "%d", val);
SetWindowTextA(g_hDE, tmp);
UpdateDelayDisplay();
if (!g_ic.load()) UpdateCount();
}
else if ((arg == "--button" || arg == "-b") && i+1 < tokens.size()) {
int val = atoi(tokens[++i].c_str());
if (val >= 0 && val <= 2) {
g_cb = val;
SendMessage(g_hBC, CB_SETCURSEL, val, 0);
}
}
else if ((arg == "--sequence" || arg == "-seq") && i+1 < tokens.size()) {
string seq = tokens[++i];
SetWindowTextA(g_hSE, seq.c_str());
ParseCoordinates(seq);
}
else if (arg == "--enable-seq") {
g_seq = true;
SendMessage(g_hSC, BM_SETCHECK, BST_CHECKED, 0);
}
else if (arg == "--disable-seq") {
g_seq = false;
SendMessage(g_hSC, BM_SETCHECK, BST_UNCHECKED, 0);
}
else if ((arg == "--jitter" || arg == "-j") && i+1 < tokens.size()) {
int range = atoi(tokens[++i].c_str());
if (range < 0) range = 0;
if (range > 100) range = 100;
g_jr = range;
g_je = (range > 0);
SendMessage(g_hJC, BM_SETCHECK, g_je.load() ? BST_CHECKED : BST_UNCHECKED, 0);
char tmp[16]; sprintf(tmp, "%d", range);
SetWindowTextA(g_hJE, tmp);
}
else if (arg == "--relative") {
g_rel = true;
SendMessage(g_hRC, BM_SETCHECK, BST_CHECKED, 0);
}
else if (arg == "--absolute") {
g_rel = false;
SendMessage(g_hRC, BM_SETCHECK, BST_UNCHECKED, 0);
}
else if (arg == "--show") {
ShowWindow(g_hM, SW_SHOW);
SetForegroundWindow(g_hM);
}
else if (arg == "--hide") {
ShowWindow(g_hM, SW_HIDE);
}
else if (arg == "--exit" || arg == "-e") {
PostMessage(g_hM, WM_DESTROY, 0, 0);
}
else if (arg == "--help" || arg == "-h") {
PrintCommandHelp();
}
}
}
void DoMultiPointClick(int x, int y, int button, int count, int interval) {
for (int i = 0; i < count; ++i) {
if (g_mpStop.load() || !g_mpAct) break;
SetCursorPos(x, y);
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[1].type = INPUT_MOUSE;
switch (button) {
case 0: input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 1: input[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
case 2: input[0].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; input[1].mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
}
SendInput(2, input, sizeof(INPUT));
if (i < count - 1 && interval > 0) Sleep(interval);
}
}
DWORD WINAPI MultiPointClickThread(LPVOID lp) {
int* p = (int*)lp;
DoMultiPointClick(p[0], p[1], p[2], p[3], p[4]);
delete[] p;
return 0;
}
LRESULT CALLBACK MultiPointMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && g_mpAct && !g_mpStop.load()) {
MSLLHOOKSTRUCT* p = (MSLLHOOKSTRUCT*)lParam;
bool match = false;
if (g_mpBtn == 0 && wParam == WM_LBUTTONDOWN) match = true;
else if (g_mpBtn == 1 && wParam == WM_RBUTTONDOWN) match = true;
else if (g_mpBtn == 2 && wParam == WM_MBUTTONDOWN) match = true;
if (match) {
lock_guard<mutex> lock(g_mpMtx);
if (g_mpBusy) return CallNextHookEx(g_mpHook, nCode, wParam, lParam);
g_mpBusy = true;
int* param = new int[5]{ p->pt.x, p->pt.y, g_mpBtn, g_mpCnt, g_mpInt };
HANDLE h = CreateThread(nullptr, 0, MultiPointClickThread, param, 0, nullptr);
if (h) CloseHandle(h);
Sleep(20);
g_mpBusy = false;
}
}
return CallNextHookEx(g_mpHook, nCode, wParam, lParam);
}
ULONG_PTR g_gdiTok;
LRESULT CALLBACK OverlayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
HINSTANCE hInst = ((LPCREATESTRUCT)lParam)->hInstance;
CreateWindowA("BUTTON", "停止多点", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 10, 80, 30, hwnd, (HMENU)3001, hInst, nullptr);
CreateWindowA("STATIC", "多点工作中...", WS_CHILD | WS_VISIBLE | SS_CENTER,
110, 10, 150, 30, hwnd, nullptr, hInst, nullptr);
SendMessage(GetDlgItem(hwnd, 3001), WM_SETFONT, (WPARAM)g_fN, TRUE);
SendMessage(GetDlgItem(hwnd, 3002), WM_SETFONT, (WPARAM)g_fN, TRUE);
break;
}
case WM_COMMAND:
if (LOWORD(wParam) == 3001) {
g_mpStop.store(true);
g_mpAct = false;
if (g_mpHook) { UnhookWindowsHookEx(g_mpHook); g_mpHook = nullptr; }
if (g_hOv) { DestroyWindow(g_hOv); g_hOv = nullptr; }
ShowWindow(g_hM, SW_SHOW);
if (g_hMacW) ShowWindow(g_hMacW, SW_SHOW);
SetWindowTextA(g_hME, "启用多点");
SetWindowTextA(g_hMS, "已停止");
EnableWindow(g_hMC, TRUE);
EnableWindow(g_hMI, TRUE);
EnableWindow(g_hMB, TRUE);
g_mpStop.store(false);
}
break;
case WM_ERASEBKGND: {
HDC hdc = (HDC)wParam;
RECT rc; GetClientRect(hwnd, &rc);
Graphics graphics(hdc);
GraphicsPath path;
int radius = 15;
path.AddArc(rc.left, rc.top, radius*2, radius*2, 180, 90);
path.AddArc(rc.right - radius*2, rc.top, radius*2, radius*2, 270, 90);
path.AddArc(rc.right - radius*2, rc.bottom - radius*2, radius*2, radius*2, 0, 90);
path.AddArc(rc.left, rc.bottom - radius*2, radius*2, radius*2, 90, 90);
path.CloseFigure();
Color color(180, 40, 40, 40);
SolidBrush brush(color);
graphics.FillPath(&brush, &path);
return 1;
}
case WM_NCHITTEST: {
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
ScreenToClient(hwnd, &pt);
HWND hBtn = GetDlgItem(hwnd, 3001);
if (hBtn) {
RECT rcBtn; GetWindowRect(hBtn, &rcBtn);
MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rcBtn, 2);
if (PtInRect(&rcBtn, pt)) return HTCLIENT;
}
return HTTRANSPARENT;
}
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void ShowOverlayWindow() {
HINSTANCE hInst = GetModuleHandle(nullptr);
static bool gdiplusStarted = false;
if (!gdiplusStarted) {
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&g_gdiTok, &gdiplusStartupInput, NULL);
gdiplusStarted = true;
}
WNDCLASSA wc = {};
wc.lpfnWndProc = OverlayWndProc;
wc.hInstance = hInst;
wc.lpszClassName = "OverlayWindowClass";
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
int screenW = GetSystemMetrics(SM_CXSCREEN);
int x = (screenW - 300) / 2;
int y = 10;
g_hOv = CreateWindowA("OverlayWindowClass", "Overlay",
WS_POPUP | WS_VISIBLE,
x, y, 300, 60,
nullptr, nullptr, hInst, nullptr);
SetLayeredWindowAttributes(g_hOv, RGB(0,0,0), 0, LWA_ALPHA);
SetWindowPos(g_hOv, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(g_hOv, SW_SHOW);
UpdateWindow(g_hOv);
}
LRESULT CALLBACK MacroMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && g_macRec) {
MSLLHOOKSTRUCT* p = (MSLLHOOKSTRUCT*)lParam;
int type = -1;
int data = 0;
if (wParam == WM_LBUTTONDOWN) type = 1;
else if (wParam == WM_LBUTTONUP) type = 2;
else if (wParam == WM_RBUTTONDOWN) type = 3;
else if (wParam == WM_RBUTTONUP) type = 4;
else if (wParam == WM_MBUTTONDOWN) type = 5;
else if (wParam == WM_MBUTTONUP) type = 6;
else if (wParam == WM_MOUSEWHEEL) { type = 9; data = (short)HIWORD(p->mouseData); }
else if (wParam == WM_MOUSEMOVE) {
if (g_macMove) {
static int lmtk = 0;
int now = GetTickCount();
if (now - lmtk < 10) return CallNextHookEx(g_macMH, nCode, wParam, lParam);
lmtk = now;
type = 0;
} else {
return CallNextHookEx(g_macMH, nCode, wParam, lParam);
}
}
if (type != -1) {
lock_guard<mutex> lock(g_macMtx);
g_macEvt.push_back({type, p->pt.x, p->pt.y, data, GetTickCount(), false});
if (g_hMacL) {
char buf[128];
const char* names[] = {"移动","左键按下","左键释放","右键按下","右键释放","中键按下","中键释放","","","滚轮"};
const char* name = (type>=0 && type<=9) ? names[type] : "未知";
sprintf(buf, "%s (%d,%d)", name, p->pt.x, p->pt.y);
SendMessageA(g_hMacL, LB_ADDSTRING, 0, (LPARAM)buf);
SendMessageA(g_hMacL, LB_SETCURSEL, g_macEvt.size()-1, 0);
}
}
}
return CallNextHookEx(g_macMH, nCode, wParam, lParam);
}
LRESULT CALLBACK MacroKeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && g_macRec) {
KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;
int type = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) ? 7 : 8;
lock_guard<mutex> lock(g_macMtx);
g_macEvt.push_back({type, 0, 0, (int)p->vkCode, GetTickCount(), false});
if (g_hMacL) {
char buf[64]; sprintf(buf, "按键 VK=%d", p->vkCode);
SendMessageA(g_hMacL, LB_ADDSTRING, 0, (LPARAM)buf);
SendMessageA(g_hMacL, LB_SETCURSEL, g_macEvt.size()-1, 0);
}
}
return CallNextHookEx(g_macKH, nCode, wParam, lParam);
}
DWORD WINAPI MacroPlayThread(LPVOID) {
int speed = g_macSpeed; if (speed < 1) speed = 1;
bool loop = g_macLoop;
g_macStop = false;
do {
vector<RecordedEvent> eventsCopy;
{
lock_guard<mutex> lock(g_macMtx);
eventsCopy = g_macEvt;
}
for (auto& evt : eventsCopy) {
if (g_macStop.load() || !g_macPlay) break;
int x = evt.x, y = evt.y;
switch (evt.type) {
case 0: SetCursorPos(x, y); break;
case 1: case 2: case 3: case 4: case 5: case 6: {
SetCursorPos(x, y);
INPUT inp = {}; inp.type = INPUT_MOUSE;
switch (evt.type) {
case 1: inp.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; break;
case 2: inp.mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 3: inp.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; break;
case 4: inp.mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
case 5: inp.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; break;
case 6: inp.mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
}
SendInput(1, &inp, sizeof(INPUT));
break;
}
case 7: case 8: {
INPUT inp = {}; inp.type = INPUT_KEYBOARD;
inp.ki.wVk = (WORD)evt.data;
if (evt.type == 8) inp.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &inp, sizeof(INPUT));
break;
}
case 9: {
SetCursorPos(x, y);
INPUT inp = {}; inp.type = INPUT_MOUSE;
inp.mi.dwFlags = MOUSEEVENTF_WHEEL;
inp.mi.mouseData = evt.data;
SendInput(1, &inp, sizeof(INPUT));
break;
}
}
auto start = chrono::steady_clock::now();
while (chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start).count() < speed) {
if (g_macStop.load() || !g_macPlay) break;
Sleep(1);
}
}
} while (loop && g_macPlay && !g_macStop.load());
g_macPlay = false;
if (g_hMacPB) SetWindowTextA(g_hMacPB, "播放");
PostMessageA(g_hMacW, WM_MACRO_UPDATE, 0, 0);
return 0;
}
LRESULT CALLBACK MacroWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
HINSTANCE hInst = ((LPCREATESTRUCT)lParam)->hInstance;
g_hMacL = CreateWindowA("LISTBOX", "",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY,
10, 10, 820, 260, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hMacL, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("BUTTON", "录制", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 280, 80, 30, hwnd, (HMENU)4001, hInst, nullptr);
CreateWindowA("BUTTON", "停止录制", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
100, 280, 80, 30, hwnd, (HMENU)4002, hInst, nullptr);
g_hMacPB = CreateWindowA("BUTTON", "播放", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
190, 280, 80, 30, hwnd, (HMENU)4003, hInst, nullptr);
CreateWindowA("BUTTON", "停止", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
280, 280, 80, 30, hwnd, (HMENU)4004, hInst, nullptr);
HWND hLoop = CreateWindowA("BUTTON", "循环", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
370, 280, 60, 30, hwnd, (HMENU)4005, hInst, nullptr);
CreateWindowA("STATIC", "速度(ms):", WS_CHILD | WS_VISIBLE,
440, 285, 60, 20, hwnd, nullptr, hInst, nullptr);
HWND hSpeed = CreateWindowA("EDIT", "50", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER,
510, 280, 60, 30, hwnd, (HMENU)4006, hInst, nullptr);
SendMessage(hSpeed, WM_SETFONT, (WPARAM)g_fN, TRUE);
HWND hMove = CreateWindowA("BUTTON", "记录鼠标移动", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
590, 280, 130, 30, hwnd, (HMENU)4008, hInst, nullptr);
SendMessage(hMove, WM_SETFONT, (WPARAM)g_fN, TRUE);
SendMessage(hMove, BM_SETCHECK, BST_CHECKED, 0);
g_macMove = true;
CreateWindowA("BUTTON", "清空", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
740, 280, 80, 30, hwnd, (HMENU)4007, hInst, nullptr);
break;
}
case WM_COMMAND: {
int id = LOWORD(wParam);
if (id == 4001) {
if (g_macPlay) { MessageBoxA(hwnd, "请先停止播放", "提示", MB_OK); break; }
{
lock_guard<mutex> lock(g_macMtx);
g_macEvt.clear();
}
if (g_hMacL) SendMessage(g_hMacL, LB_RESETCONTENT, 0, 0);
g_macRec = true;
SetWindowTextA(GetDlgItem(hwnd, 4001), "录制中...");
g_macMH = SetWindowsHookExA(WH_MOUSE_LL, MacroMouseHookProc, GetModuleHandleA(nullptr), 0);
g_macKH = SetWindowsHookExA(WH_KEYBOARD_LL, MacroKeyboardHookProc, GetModuleHandleA(nullptr), 0);
if (!g_macMH || !g_macKH) {
MessageBoxA(hwnd, "钩子安装失败", "错误", MB_OK);
g_macRec = false;
SetWindowTextA(GetDlgItem(hwnd, 4001), "录制");
if (g_macMH) { UnhookWindowsHookEx(g_macMH); g_macMH = nullptr; }
if (g_macKH) { UnhookWindowsHookEx(g_macKH); g_macKH = nullptr; }
}
break;
}
if (id == 4002) {
g_macRec = false;
SetWindowTextA(GetDlgItem(hwnd, 4001), "录制");
if (g_macMH) { UnhookWindowsHookEx(g_macMH); g_macMH = nullptr; }
if (g_macKH) { UnhookWindowsHookEx(g_macKH); g_macKH = nullptr; }
{
lock_guard<mutex> lock(g_macMtx);
MessageBoxA(hwnd, ("录制完成,共 " + to_string(g_macEvt.size()) + " 个事件").c_str(), "提示", MB_OK);
}
break;
}
if (id == 4003) {
{
lock_guard<mutex> lock(g_macMtx);
if (g_macEvt.empty()) { MessageBoxA(hwnd, "宏为空", "提示", MB_OK); break; }
}
if (g_macPlay) break;
g_macStop = false;
g_macPlay = true;
SetWindowTextA(g_hMacPB, "播放中...");
char buf[16]; GetWindowTextA(GetDlgItem(hwnd, 4006), buf, sizeof(buf));
g_macSpeed = atoi(buf); if (g_macSpeed < 1) g_macSpeed = 1;
g_macLoop = (SendMessage(GetDlgItem(hwnd, 4005), BM_GETCHECK, 0, 0) == BST_CHECKED);
g_macThr = CreateThread(nullptr, 0, MacroPlayThread, nullptr, 0, nullptr);
if (!g_macThr) {
g_macPlay = false;
SetWindowTextA(g_hMacPB, "播放");
MessageBoxA(hwnd, "线程创建失败", "错误", MB_OK);
}
break;
}
if (id == 4004) {
g_macStop = true;
g_macPlay = false;
SetWindowTextA(g_hMacPB, "播放");
if (g_macThr) {
WaitForSingleObject(g_macThr, 1000);
CloseHandle(g_macThr);
g_macThr = nullptr;
}
break;
}
if (id == 4007) {
lock_guard<mutex> lock(g_macMtx);
g_macEvt.clear();
if (g_hMacL) SendMessage(g_hMacL, LB_RESETCONTENT, 0, 0);
break;
}
if (id == 4008) {
g_macMove = (SendMessage(GetDlgItem(hwnd, 4008), BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
}
break;
}
case WM_MACRO_UPDATE:
SetWindowTextA(g_hMacPB, "播放");
break;
case WM_CLOSE: {
g_macRec = false;
g_macPlay = false;
g_macStop = true;
if (g_macMH) { UnhookWindowsHookEx(g_macMH); g_macMH = nullptr; }
if (g_macKH) { UnhookWindowsHookEx(g_macKH); g_macKH = nullptr; }
if (g_macThr) { WaitForSingleObject(g_macThr, 500); CloseHandle(g_macThr); g_macThr = nullptr; }
DestroyWindow(hwnd);
break;
}
case WM_DESTROY:
g_hMacW = nullptr;
g_hMacL = nullptr;
g_hMacPB = nullptr;
break;
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void CreateMacroWindow() {
if (g_hMacW) { ShowWindow(g_hMacW, SW_SHOW); SetForegroundWindow(g_hMacW); return; }
HINSTANCE hInst = GetModuleHandle(nullptr);
WNDCLASSA wc = {};
wc.lpfnWndProc = MacroWndProc;
wc.hInstance = hInst;
wc.lpszClassName = "MacroWindowClass";
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClassA(&wc);
g_hMacW = CreateWindowA("MacroWindowClass", "鼠标宏编辑器",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 860, 360,
g_hM, nullptr, hInst, nullptr);
SendMessage(g_hMacW, WM_SETICON, ICON_SMALL, (LPARAM)g_icon);
}
DWORD WINAPI ClickerThread(LPVOID) {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
SetThreadAffinityMask(GetCurrentThread(), 1);
int localDelay = g_cd.load();
int localBtn = g_cb.load();
int localCnt = 0;
while (!g_se) {
if (g_ic.load()) {
if (g_seq.load() && g_cd.load() > 0) {
vector<pair<int,int>> ptsCopy = g_pts;
if (ptsCopy.empty()) {
MessageBoxA(g_hM, "序列点位为空,请先设置点位。", "提示", MB_OK);
g_ic = false;
UpdateUI();
continue;
}
for (auto& pt : ptsCopy) {
if (!g_ic.load()) break;
SimulateClick(pt.first, pt.second, g_cb.load());
g_cc.fetch_add(1);
PostMessageA(g_hM, WM_USER + 1, 0, 0);
int finalDelay = localDelay;
if (g_je.load() && localDelay > 0) {
int range = g_jr.load();
if (range > 0) {
int offset = GetJitterOffset(range);
finalDelay += offset;
if (finalDelay < 1) finalDelay = 1;
}
}
if (finalDelay == 1) usSleep(1000);
else Sleep(finalDelay);
}
continue;
}
if (g_em.load()) {
while (!g_se && g_ic.load() && g_em.load()) {
int btnCur = g_cb.load();
if (btnCur != localBtn) localBtn = btnCur;
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[1].type = INPUT_MOUSE;
switch (localBtn) {
case 0: input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 1: input[0].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; input[1].mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
case 2: input[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
}
SendInput(2, input, sizeof(INPUT));
++localCnt;
}
if (localCnt > 0) { g_cc.fetch_add(localCnt); localCnt = 0; PostMessageA(g_hM, WM_USER + 1, 0, 0); }
localDelay = g_cd.load();
continue;
}
int btnCur = g_cb.load();
if (btnCur != localBtn) localBtn = btnCur;
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[1].type = INPUT_MOUSE;
switch (localBtn) {
case 0: input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 1: input[0].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; input[1].mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
case 2: input[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; input[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
}
SendInput(2, input, sizeof(INPUT));
static int clickCounter = 0;
if (++clickCounter >= 10) { clickCounter = 0; PostMessageA(g_hM, WM_USER + 1, 0, 0); }
int finalDelay = localDelay;
if (g_je.load() && localDelay > 0) {
int range = g_jr.load();
if (range > 0) {
int offset = GetJitterOffset(range);
finalDelay += offset;
if (finalDelay < 1) finalDelay = 1;
}
}
if (finalDelay == 1) usSleep(1000);
else Sleep(finalDelay);
localDelay = g_cd.load();
} else {
if (localCnt > 0) { g_cc.fetch_add(localCnt); localCnt = 0; UpdateCount(); }
Sleep(10);
}
}
return 0;
}
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;
if ((p->vkCode == VK_OEM_PLUS || p->vkCode == VK_ADD) && (GetKeyState(VK_CAPITAL) & 1)) {
if (!g_ic.load()) {
POINT pt; GetCursorPos(&pt);
PostMessageA(g_hM, WM_ADD_COORD, (WPARAM)pt.x, (LPARAM)pt.y);
MessageBeep(MB_OK);
}
}
if (g_rec.load()) {
if (p->vkCode >= VK_F1 && p->vkCode <= VK_F12)
return CallNextHookEx(g_kh, nCode, wParam, lParam);
int type = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) ? 7 : 8;
g_evt.push_back({type, 0, 0, (int)p->vkCode, GetTickCount(), false});
}
if (g_hookHot && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
static bool fd6 = false, fd7 = false, fdE = false;
if (p->vkCode == VK_F6 && !fd6) {
fd6 = true;
bool running = g_ic.load();
g_ic = !running;
UpdateUI();
if (!running) UpdateCount();
strcpy(g_nid.szTip, g_ic.load() ? "自动连点器 (运行中)" : "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
return 1;
} else if (p->vkCode == VK_F6 && wParam == WM_KEYUP) {
fd6 = false;
}
if (p->vkCode == VK_F7 && !fd7) {
fd7 = true;
ToggleRecording();
return 1;
} else if (p->vkCode == VK_F7 && wParam == WM_KEYUP) {
fd7 = false;
}
if (p->vkCode == VK_ESCAPE && !fdE) {
fdE = true;
if (IsWindowVisible(g_hM)) {
DestroyWindow(g_hM);
}
} else if (p->vkCode == VK_ESCAPE && wParam == WM_KEYUP) {
fdE = false;
}
}
}
return CallNextHookEx(g_kh, nCode, wParam, lParam);
}
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && g_rec.load()) {
MSLLHOOKSTRUCT* p = (MSLLHOOKSTRUCT*)lParam;
int currentTime = GetTickCount();
int screenX = p->pt.x, screenY = p->pt.y;
int storeX, storeY;
bool isRel = g_rel;
if (g_rel) {
POINT clientTL = {0,0}; ClientToScreen(g_hM, &clientTL);
storeX = screenX - clientTL.x; storeY = screenY - clientTL.y;
} else { storeX = screenX; storeY = screenY; }
int type = -1, data = 0;
if (wParam == WM_MOUSEMOVE) { type = 0; }
else if (wParam == WM_LBUTTONDOWN) type = 1;
else if (wParam == WM_LBUTTONUP) type = 2;
else if (wParam == WM_RBUTTONDOWN) type = 3;
else if (wParam == WM_RBUTTONUP) type = 4;
else if (wParam == WM_MBUTTONDOWN) type = 5;
else if (wParam == WM_MBUTTONUP) type = 6;
else if (wParam == WM_MOUSEWHEEL) { type = 9; data = (short)HIWORD(p->mouseData); }
else if (wParam == WM_MOUSEHWHEEL) { type = 10; data = (short)HIWORD(p->mouseData); }
if (type != -1) {
if (type == 0 && currentTime - g_lmt < 5) return CallNextHookEx(g_mh, nCode, wParam, lParam);
if (type == 0) g_lmt = currentTime;
g_evt.push_back({type, storeX, storeY, data, currentTime, isRel});
}
}
return CallNextHookEx(g_mh, nCode, wParam, lParam);
}
DWORD WINAPI PlaybackThread(LPVOID) {
if (g_evt.empty()) { MessageBoxA(g_hM, "没有录制数据", "提示", MB_OK); return 0; }
g_ply = true;
for (auto& evt : g_evt) {
if (!g_ply) break;
int sx, sy;
if (evt.isRelative) {
POINT tl = {0,0}; ClientToScreen(g_hM, &tl);
sx = tl.x + evt.x; sy = tl.y + evt.y;
} else { sx = evt.x; sy = evt.y; }
switch (evt.type) {
case 0: SetCursorPos(sx, sy); break;
case 1: case 2: case 3: case 4: case 5: case 6: {
SetCursorPos(sx, sy);
INPUT inp = {}; inp.type = INPUT_MOUSE;
switch (evt.type) {
case 1: inp.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; break;
case 2: inp.mi.dwFlags = MOUSEEVENTF_LEFTUP; break;
case 3: inp.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; break;
case 4: inp.mi.dwFlags = MOUSEEVENTF_RIGHTUP; break;
case 5: inp.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; break;
case 6: inp.mi.dwFlags = MOUSEEVENTF_MIDDLEUP; break;
}
SendInput(1, &inp, sizeof(INPUT));
break;
}
case 7: case 8: {
INPUT inp = {}; inp.type = INPUT_KEYBOARD;
inp.ki.wVk = (WORD)evt.data;
if (evt.type == 8) inp.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &inp, sizeof(INPUT));
break;
}
case 9: case 10: {
SetCursorPos(sx, sy);
INPUT inp = {}; inp.type = INPUT_MOUSE;
inp.mi.dwFlags = (evt.type == 9) ? MOUSEEVENTF_WHEEL : MOUSEEVENTF_HWHEEL;
inp.mi.mouseData = evt.data;
SendInput(1, &inp, sizeof(INPUT));
break;
}
}
Sleep(g_ps.load());
}
g_ply = false;
PostMessageA(g_hM, WM_USER + 3, 0, 0);
return 0;
}
void CreateTrayIcon() {
ZeroMemory(&g_nid, sizeof(NOTIFYICONDATAA));
g_nid.cbSize = sizeof(NOTIFYICONDATAA);
g_nid.hWnd = g_hM;
g_nid.uID = 1;
g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
g_nid.uCallbackMessage = WM_TRAYICON;
g_nid.hIcon = g_icon ? g_icon : LoadIconA(nullptr, IDI_APPLICATION);
strcpy(g_nid.szTip, "自动连点器");
if (!Shell_NotifyIconA(NIM_ADD, &g_nid)) {
Sleep(100);
if (!Shell_NotifyIconA(NIM_ADD, &g_nid)) {
MessageBoxA(g_hM, "托盘图标创建失败!", "提示", MB_OK);
return;
}
}
g_tray = true;
}
void DeleteTrayIcon() { if (g_tray) { Shell_NotifyIconA(NIM_DELETE, &g_nid); g_tray = false; } }
void ShowMainWindow() { ShowWindow(g_hM, SW_SHOW); SetForegroundWindow(g_hM); }
HBRUSH CreateGradientBrush(int width, int height, COLORREF top, COLORREF bottom) {
if (width <= 0 || height <= 0) return nullptr;
HDC hdc = GetDC(nullptr);
HDC mem = CreateCompatibleDC(hdc);
HBITMAP bmp = CreateCompatibleBitmap(hdc, width, height);
HBITMAP old = (HBITMAP)SelectObject(mem, bmp);
for (int y = 0; y < height; ++y) {
double r = (double)y / (height - 1);
BYTE R = (BYTE)(GetRValue(top) + r * (GetRValue(bottom) - GetRValue(top)));
BYTE G = (BYTE)(GetGValue(top) + r * (GetGValue(bottom) - GetGValue(top)));
BYTE B = (BYTE)(GetBValue(top) + r * (GetBValue(bottom) - GetBValue(top)));
HBRUSH line = CreateSolidBrush(RGB(R,G,B));
RECT rc = {0, y, width, y+1};
FillRect(mem, &rc, line);
DeleteObject(line);
}
SelectObject(mem, old);
HBRUSH brush = CreatePatternBrush(bmp);
DeleteObject(bmp);
DeleteDC(mem);
ReleaseDC(nullptr, hdc);
return brush;
}
void StartRecordingActual() {
if (g_ic.load() || g_seq.load()) {
MessageBoxA(g_hM, "请先停止连点或关闭序列。", "提示", MB_OK);
return;
}
g_evt.clear();
g_lmt = 0;
g_rec = true;
SetWindowTextA(g_hRB, "停止录制");
g_mh = SetWindowsHookExA(WH_MOUSE_LL, MouseHookProc, GetModuleHandleA(nullptr), 0);
if (!g_mh) {
MessageBoxA(g_hM, "设置鼠标钩子失败!", "错误", MB_OK);
g_rec = false;
SetWindowTextA(g_hRB, "开始录制");
}
}
void StopRecording() {
g_rec = false;
SetWindowTextA(g_hRB, "开始录制");
if (g_mh) { UnhookWindowsHookEx(g_mh); g_mh = nullptr; }
MessageBoxA(g_hM, ("录制完成,共 " + to_string(g_evt.size()) + " 个事件。").c_str(), "提示", MB_OK);
}
void ToggleRecording() {
if (g_rec.load()) { StopRecording(); return; }
if (g_recPend) {
KillTimer(g_hM, g_cdTimer);
g_recPend = false;
SetWindowTextA(g_hRB, "开始录制");
return;
}
if (g_ic.load() || g_seq.load()) {
MessageBoxA(g_hM, "请先停止连点或关闭序列。", "提示", MB_OK);
return;
}
g_recPend = true;
g_curCd = g_cdSec;
char buf[32]; sprintf(buf, "倒计时 %ds", g_curCd);
SetWindowTextA(g_hRB, buf);
SetTimer(g_hM, g_cdTimer, 1000, nullptr);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
HINSTANCE hInst = ((LPCREATESTRUCT)lParam)->hInstance;
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_STANDARD_CLASSES;
InitCommonControlsEx(&icex);
int baseX = 20, baseY = 20;
int colW = 340, gap = 25, statusW = colW*3 + gap*2;
int rowH = 34;
g_fS = CreateFont(44, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, "Segoe UI");
g_fN = CreateFont(22, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, "Segoe UI");
g_bW = CreateSolidBrush(RGB(255,255,255));
g_hS = CreateWindowA("STATIC", "● 已停止", WS_CHILD|WS_VISIBLE|SS_CENTER,
baseX, baseY, statusW, 56, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hS, WM_SETFONT, (WPARAM)g_fS, TRUE);
baseY += 68;
int x1 = baseX, x2 = baseX + colW + gap, x3 = baseX + (colW+gap)*2;
int y = baseY;
CreateWindowA("BUTTON", "基础设置", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x1, y, colW, 190, hwnd, nullptr, hInst, nullptr);
CreateWindowA("STATIC", "间隔(ms):", WS_CHILD|WS_VISIBLE, x1+15, y+32, 85, rowH, hwnd, nullptr, hInst, nullptr);
g_hDE = CreateWindowA("EDIT", "100", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
x1+105, y+28, 95, 34, hwnd, (HMENU)1001, hInst, nullptr);
SendMessage(g_hDE, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hDL = CreateWindowA("STATIC", "当前:100ms", WS_CHILD|WS_VISIBLE|SS_LEFT,
x1+210, y+32, 120, rowH, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hDL, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "按键:", WS_CHILD|WS_VISIBLE, x1+15, y+80, 65, rowH, hwnd, nullptr, hInst, nullptr);
g_hBC = CreateWindowA("COMBOBOX", "", WS_CHILD|WS_VISIBLE|WS_BORDER|CBS_DROPDOWNLIST|CBS_HASSTRINGS,
x1+85, y+76, 120, 120, hwnd, (HMENU)1002, hInst, nullptr);
SendMessage(g_hBC, WM_SETFONT, (WPARAM)g_fN, TRUE);
SendMessageA(g_hBC, CB_ADDSTRING, 0, (LPARAM)"左键");
SendMessageA(g_hBC, CB_ADDSTRING, 0, (LPARAM)"中键");
SendMessageA(g_hBC, CB_ADDSTRING, 0, (LPARAM)"右键");
SendMessage(g_hBC, CB_SETCURSEL, 0, 0);
g_hJC = CreateWindowA("BUTTON", "抖动", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
x1+15, y+124, 85, 30, hwnd, (HMENU)1003, hInst, nullptr);
SendMessage(g_hJC, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "范围:", WS_CHILD|WS_VISIBLE, x1+115, y+124, 55, 30, hwnd, nullptr, hInst, nullptr);
g_hJE = CreateWindowA("EDIT", "10", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
x1+170, y+120, 75, 34, hwnd, (HMENU)1004, hInst, nullptr);
SendMessage(g_hJE, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hRC = CreateWindowA("BUTTON", "相对窗口", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
x1+15, y+160, 130, 28, hwnd, (HMENU)1010, hInst, nullptr);
SendMessage(g_hRC, WM_SETFONT, (WPARAM)g_fN, TRUE);
SendMessage(g_hRC, BM_SETCHECK, BST_CHECKED, 0);
g_rel = true;
CreateWindowA("BUTTON", "序列点击", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x2, y, colW, 190, hwnd, nullptr, hInst, nullptr);
g_hSC = CreateWindowA("BUTTON", "启用序列", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
x2+15, y+32, 130, 30, hwnd, (HMENU)1005, hInst, nullptr);
SendMessage(g_hSC, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "坐标 (x,y;x,y):", WS_CHILD|WS_VISIBLE, x2+15, y+74, 190, 30, hwnd, nullptr, hInst, nullptr);
g_hSE = CreateWindowA("EDIT", "100,200;300,400", WS_CHILD|WS_VISIBLE|WS_BORDER,
x2+15, y+104, colW-30, 44, hwnd, (HMENU)1006, hInst, nullptr);
SendMessage(g_hSE, WM_SETFONT, (WPARAM)g_fN, TRUE);
char initBuf[512]; GetWindowTextA(g_hSE, initBuf, sizeof(initBuf));
ParseCoordinates(initBuf);
CreateWindowA("BUTTON", "录制回放", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x3, y, colW, 190, hwnd, nullptr, hInst, nullptr);
g_hRB = CreateWindowA("BUTTON", "开始录制", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
x3+15, y+32, 140, 42, hwnd, (HMENU)1007, hInst, nullptr);
SendMessage(g_hRB, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hPB = CreateWindowA("BUTTON", "回放", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
x3+170, y+32, 140, 42, hwnd, (HMENU)1008, hInst, nullptr);
SendMessage(g_hPB, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "速度(ms):", WS_CHILD|WS_VISIBLE, x3+15, y+92, 85, 30, hwnd, nullptr, hInst, nullptr);
g_hSS = CreateWindowA("EDIT", "50", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
x3+105, y+88, 95, 34, hwnd, (HMENU)1009, hInst, nullptr);
SendMessage(g_hSS, WM_SETFONT, (WPARAM)g_fN, TRUE);
y += 210;
int row2H = 160;
CreateWindowA("BUTTON", "鼠标多点", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x1, y, colW, row2H, hwnd, nullptr, hInst, nullptr);
CreateWindowA("STATIC", "次数:", WS_CHILD|WS_VISIBLE, x1+15, y+32, 55, 30, hwnd, nullptr, hInst, nullptr);
g_hMC = CreateWindowA("EDIT", "5", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
x1+75, y+28, 75, 34, hwnd, (HMENU)2001, hInst, nullptr);
SendMessage(g_hMC, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "间隔(ms):", WS_CHILD|WS_VISIBLE, x1+165, y+32, 80, 30, hwnd, nullptr, hInst, nullptr);
g_hMI = CreateWindowA("EDIT", "100", WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
x1+250, y+28, 75, 34, hwnd, (HMENU)2002, hInst, nullptr);
SendMessage(g_hMI, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "按键:", WS_CHILD|WS_VISIBLE, x1+15, y+78, 55, 30, hwnd, nullptr, hInst, nullptr);
g_hMB = CreateWindowA("COMBOBOX", "", WS_CHILD|WS_VISIBLE|WS_BORDER|CBS_DROPDOWNLIST|CBS_HASSTRINGS,
x1+75, y+74, 110, 120, hwnd, (HMENU)2003, hInst, nullptr);
SendMessage(g_hMB, WM_SETFONT, (WPARAM)g_fN, TRUE);
SendMessageA(g_hMB, CB_ADDSTRING, 0, (LPARAM)"左键");
SendMessageA(g_hMB, CB_ADDSTRING, 0, (LPARAM)"右键");
SendMessageA(g_hMB, CB_ADDSTRING, 0, (LPARAM)"中键");
SendMessage(g_hMB, CB_SETCURSEL, 0, 0);
g_hME = CreateWindowA("BUTTON", "启用多点", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
x1+200, y+72, 125, 42, hwnd, (HMENU)2004, hInst, nullptr);
SendMessage(g_hME, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hMS = CreateWindowA("STATIC", "未启动", WS_CHILD|WS_VISIBLE|SS_CENTER,
x1+15, y+126, colW-30, 26, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hMS, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("BUTTON", "鼠标宏", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x2, y, colW, row2H, hwnd, nullptr, hInst, nullptr);
g_hMac = CreateWindowA("BUTTON", "打开宏编辑器", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
x2+65, y+50, 200, 50, hwnd, (HMENU)2005, hInst, nullptr);
SendMessage(g_hMac, WM_SETFONT, (WPARAM)g_fN, TRUE);
CreateWindowA("STATIC", "录制鼠标/键盘动作,支持循环播放", WS_CHILD|WS_VISIBLE|SS_CENTER,
x2+15, y+118, colW-30, 28, hwnd, nullptr, hInst, nullptr);
CreateWindowA("BUTTON", "统计", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
x3, y, colW, row2H, hwnd, nullptr, hInst, nullptr);
g_hC = CreateWindowA("STATIC", "已点击: 0 次", WS_CHILD|WS_VISIBLE,
x3+25, y+40, 200, 34, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hC, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hP = CreateWindowA("STATIC", "CPS: 0.0", WS_CHILD|WS_VISIBLE|SS_LEFT,
x3+25, y+90, 200, 34, hwnd, nullptr, hInst, nullptr);
SendMessage(g_hP, WM_SETFONT, (WPARAM)g_fN, TRUE);
y += row2H + 20;
CreateWindowA("BUTTON", "控制", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,
baseX, y, statusW, 96, hwnd, nullptr, hInst, nullptr);
g_hSB = CreateWindowA("BUTTON", "开始 (F6)", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
baseX+25, y+22, 170, 52, hwnd, (HMENU)1, hInst, nullptr);
SendMessage(g_hSB, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hTB = CreateWindowA("BUTTON", "停止 (F6)", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
baseX+210, y+22, 170, 52, hwnd, (HMENU)2, hInst, nullptr);
SendMessage(g_hTB, WM_SETFONT, (WPARAM)g_fN, TRUE);
g_hEB = CreateWindowA("BUTTON", "退出", WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
baseX+395, y+22, 170, 52, hwnd, (HMENU)3, hInst, nullptr);
SendMessage(g_hEB, WM_SETFONT, (WPARAM)g_fN, TRUE);
y += 110;
CreateWindowA("STATIC", "F6 切换 | F7 录制 | ESC 退出 | CapsLock + [+] 记录坐标",
WS_CHILD|WS_VISIBLE|SS_CENTER, baseX, y, statusW, 32, hwnd, nullptr, hInst, nullptr);
bool f6ok = RegisterHotKey(hwnd, HOTKEY_F6, 0, VK_F6);
bool escok = RegisterHotKey(hwnd, HOTKEY_ESC, 0, VK_ESCAPE);
bool f7ok = RegisterHotKey(hwnd, HOTKEY_F7, 0, VK_F7);
if (!f6ok || !f7ok || !escok) {
g_hookHot = true;
char msg[256];
sprintf(msg, "部分热键注册失败(F6:%s, F7:%s, ESC:%s),\n程序已自动启用键盘钩子作为替代方案。\nF6/F7 将独占(其他程序无法收到),ESC 不拦截。",
f6ok ? "成功" : "失败",
f7ok ? "成功" : "失败",
escok ? "成功" : "失败");
MessageBoxA(hwnd, msg, "热键冲突提示", MB_OK | MB_ICONWARNING);
} else {
g_hookHot = false;
}
g_kh = SetWindowsHookExA(WH_KEYBOARD_LL, KeyboardHookProc, GetModuleHandleA(nullptr), 0);
if (!g_kh) MessageBoxA(hwnd, "键盘钩子安装失败", "警告", MB_OK);
HRGN hRgn = CreateRoundRectRgn(0, 0, 1100, 820, 30, 30);
SetWindowRgn(hwnd, hRgn, TRUE);
g_init = true;
g_cd = 100; g_cb = 0; g_em = false;
UpdateUI(); UpdateCount(); UpdateCPSDisplay();
break;
}
case WM_EXECUTE_CMD: {
char* cmd = (char*)wParam;
if (cmd) {
ExecuteCommand(string(cmd));
delete[] cmd;
}
break;
}
case WM_COPYDATA: {
COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
if (pcds->dwData == 1) {
string cmd = (char*)pcds->lpData;
char* cmdCopy = new char[cmd.size() + 1];
strcpy(cmdCopy, cmd.c_str());
PostMessage(hwnd, WM_EXECUTE_CMD, (WPARAM)cmdCopy, 0);
ShowMainWindow();
return TRUE;
}
break;
}
case WM_ADD_COORD: {
if (g_ic.load()) break;
int x = (int)wParam, y = (int)lParam;
char buf[512] = {0}; GetWindowTextA(g_hSE, buf, sizeof(buf)-1);
string str = buf;
if (!str.empty()) str += ";";
char coord[32]; sprintf(coord, "%d,%d", x, y);
str += coord;
SetWindowTextA(g_hSE, str.c_str());
g_pts.push_back({x,y});
break;
}
case WM_USER + 3: SetWindowTextA(g_hPB, "回放"); break;
case WM_SHOWWINDOW:
if (wParam == TRUE) { if (!g_tray) CreateTrayIcon(); SetTimer(hwnd, TIMER_CPS, 500, nullptr); }
break;
case WM_TRAYICON: {
if (lParam == WM_LBUTTONDBLCLK) {
ShowMainWindow();
} else if (lParam == WM_RBUTTONUP) {
POINT pt; GetCursorPos(&pt);
HMENU hMenu = CreatePopupMenu();
AppendMenuA(hMenu, MF_STRING, TRAY_SHOW, "显示窗口");
AppendMenuA(hMenu, MF_STRING, TRAY_HIDE, "隐藏窗口");
AppendMenuA(hMenu, MF_SEPARATOR, 0, NULL);
const char* toggleText = g_ic.load() ? "暂停连点" : "继续连点";
AppendMenuA(hMenu, MF_STRING, TRAY_TOGGLE, toggleText);
AppendMenuA(hMenu, MF_SEPARATOR, 0, NULL);
AppendMenuA(hMenu, MF_STRING, TRAY_EXIT, "退出程序");
SetForegroundWindow(hwnd);
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
PostMessageA(hwnd, WM_NULL, 0, 0);
DestroyMenu(hMenu);
}
break;
}
case WM_TIMER: {
if (wParam == TIMER_CPS) {
if (!g_em.load()) {
int total = g_cc.load();
static int last = 0;
static double lastTime = 0.0;
LARGE_INTEGER freq, now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&now);
double t = (double)now.QuadPart / freq.QuadPart;
if (lastTime != 0.0 && (t - lastTime) > 0.01) {
double cps = (total - last) / (t - lastTime);
g_cps.store(cps);
UpdateCPSDisplay();
}
last = total; lastTime = t;
}
UpdateCount();
} else if (wParam == g_cdTimer) {
if (g_recPend) {
g_curCd--;
if (g_curCd <= 0) {
KillTimer(hwnd, g_cdTimer);
g_recPend = false;
SetWindowTextA(g_hRB, "开始录制");
StartRecordingActual();
} else {
char buf[32]; sprintf(buf, "倒计时 %ds", g_curCd);
SetWindowTextA(g_hRB, buf);
}
} else KillTimer(hwnd, g_cdTimer);
}
break;
}
case WM_COMMAND: {
int id = LOWORD(wParam);
int code = HIWORD(wParam);
if (id == TRAY_SHOW) { ShowMainWindow(); break; }
if (id == TRAY_HIDE) { ShowWindow(g_hM, SW_HIDE); break; }
if (id == TRAY_TOGGLE) {
bool running = g_ic.load();
g_ic = !running;
UpdateUI();
if (!running) UpdateCount();
strcpy(g_nid.szTip, g_ic.load() ? "自动连点器 (运行中)" : "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
break;
}
if (id == TRAY_EXIT) { DestroyWindow(g_hM); break; }
if (id == 2004) {
if (!g_mpAct) {
char buf[16];
GetWindowTextA(g_hMC, buf, sizeof(buf));
int cnt = max(1, atoi(buf));
GetWindowTextA(g_hMI, buf, sizeof(buf));
int inter = max(0, atoi(buf));
int sel = (int)SendMessage(g_hMB, CB_GETCURSEL, 0, 0);
if (sel < 0) sel = 0;
g_mpCnt = cnt;
g_mpInt = inter;
g_mpBtn = sel;
g_mpStop.store(false);
g_mpBusy = false;
ShowWindow(g_hM, SW_HIDE);
if (g_hMacW) ShowWindow(g_hMacW, SW_HIDE);
ShowOverlayWindow();
g_mpHook = SetWindowsHookExA(WH_MOUSE_LL, MultiPointMouseHookProc, GetModuleHandleA(nullptr), 0);
if (!g_mpHook) {
MessageBoxA(hwnd, "钩子安装失败", "错误", MB_OK);
ShowWindow(g_hM, SW_SHOW);
if (g_hMacW) ShowWindow(g_hMacW, SW_SHOW);
if (g_hOv) { DestroyWindow(g_hOv); g_hOv = nullptr; }
break;
}
g_mpAct = true;
SetWindowTextA(g_hME, "停止多点");
SetWindowTextA(g_hMS, "工作中...");
EnableWindow(g_hMC, FALSE);
EnableWindow(g_hMI, FALSE);
EnableWindow(g_hMB, FALSE);
} else {
g_mpStop.store(true);
g_mpAct = false;
if (g_mpHook) { UnhookWindowsHookEx(g_mpHook); g_mpHook = nullptr; }
if (g_hOv) { DestroyWindow(g_hOv); g_hOv = nullptr; }
ShowWindow(g_hM, SW_SHOW);
if (g_hMacW) ShowWindow(g_hMacW, SW_SHOW);
SetWindowTextA(g_hME, "启用多点");
SetWindowTextA(g_hMS, "已停止");
EnableWindow(g_hMC, TRUE);
EnableWindow(g_hMI, TRUE);
EnableWindow(g_hMB, TRUE);
g_mpStop.store(false);
}
break;
}
if (id == 2005) { CreateMacroWindow(); break; }
if (id == 1010 && code == BN_CLICKED) {
g_rel = (SendMessage(g_hRC, BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
}
if (id == 1003 && code == BN_CLICKED) {
g_je.store(SendMessage(g_hJC, BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
}
if (id == 1004 && code == EN_CHANGE && g_init) {
char buf[16] = {0}; GetWindowTextA(g_hJE, buf, sizeof(buf));
if (buf[0]) { int val = atoi(buf); if (val < 0) val = 0; if (val > 100) val = 100; g_jr.store(val); }
break;
}
if (id == 1005 && code == BN_CLICKED) {
if (g_ic.load()) {
SendMessage(g_hSC, BM_SETCHECK, g_seq.load() ? BST_CHECKED : BST_UNCHECKED, 0);
break;
}
g_seq.store(SendMessage(g_hSC, BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
}
if (id == 1006 && code == EN_KILLFOCUS) {
if (g_ic.load()) break;
char buf[512] = {0}; GetWindowTextA(g_hSE, buf, sizeof(buf)-1);
ParseCoordinates(buf);
break;
}
if (id == 1007) { ToggleRecording(); break; }
if (id == 1008) {
if (g_ply.load()) { g_ply = false; SetWindowTextA(g_hPB, "回放"); break; }
if (g_ic.load() || g_seq.load()) {
MessageBoxA(hwnd, "请先停止连点或关闭序列。", "提示", MB_OK);
break;
}
if (g_evt.empty()) { MessageBoxA(hwnd, "没有录制数据。", "提示", MB_OK); break; }
SetWindowTextA(g_hPB, "停止");
g_hPlay = CreateThread(nullptr, 0, PlaybackThread, nullptr, 0, nullptr);
if (!g_hPlay) { MessageBoxA(hwnd, "创建回放线程失败。", "错误", MB_OK); SetWindowTextA(g_hPB, "回放"); }
break;
}
if (id == 1009 && code == EN_CHANGE) {
char buf[16] = {0}; GetWindowTextA(g_hSS, buf, sizeof(buf));
if (buf[0]) { int val = max(1, min(1000, atoi(buf))); g_ps.store(val); }
break;
}
if (id == 1002) {
if (code == CBN_SELCHANGE) {
int sel = (int)SendMessage(g_hBC, CB_GETCURSEL, 0, 0);
if (sel >= 0 && sel <= 2) g_cb.store(sel);
} else if (code == 0) { DestroyWindow(g_hM); }
break;
}
if (id == 1001 && code == EN_CHANGE && g_init) {
char buf[16] = {0}; GetWindowTextA(g_hDE, buf, sizeof(buf));
if (buf[0] == '\0') break;
int val = atoi(buf);
if (val < 0) val = 0; if (val > 10000) val = 10000;
if (val == 0) {
int ret = MessageBoxA(hwnd, "进入极速模式(延迟0),CPU占用略高。\n确定继续?", "提示", MB_OKCANCEL);
if (ret == IDCANCEL) { val = 100; char tmp[16]; sprintf(tmp, "%d", val); SetWindowTextA(g_hDE, tmp); }
}
g_cd.store(val);
g_em.store(val == 0);
UpdateDelayDisplay();
if (!g_ic.load()) UpdateCount();
break;
}
switch (id) {
case 1: {
char buf[512] = {0}; GetWindowTextA(g_hSE, buf, sizeof(buf)-1);
ParseCoordinates(buf);
if (g_seq.load() && g_pts.empty()) {
MessageBoxA(hwnd, "序列已启用但坐标为空,已自动禁用。", "提示", MB_OK);
g_seq = false;
SendMessage(g_hSC, BM_SETCHECK, BST_UNCHECKED, 0);
}
g_ic = true;
UpdateUI();
strcpy(g_nid.szTip, "自动连点器 (运行中)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
break;
}
case 2: {
g_ic = false;
UpdateUI();
UpdateCount();
strcpy(g_nid.szTip, "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
break;
}
case 3: DestroyWindow(g_hM); break;
case 1001: ShowMainWindow(); break;
}
break;
}
case WM_HOTKEY: {
if (wParam == HOTKEY_F6) {
bool running = g_ic.load();
g_ic = !running;
UpdateUI();
if (!running) UpdateCount();
strcpy(g_nid.szTip, g_ic.load() ? "自动连点器 (运行中)" : "自动连点器 (已停止)");
Shell_NotifyIconA(NIM_MODIFY, &g_nid);
} else if (wParam == HOTKEY_ESC) {
DestroyWindow(g_hM);
} else if (wParam == HOTKEY_F7) {
ToggleRecording();
}
break;
}
case WM_USER + 1: UpdateCount(); break;
case WM_CTLCOLORSTATIC: {
HDC hdc = (HDC)wParam;
HWND hCtrl = (HWND)lParam;
if (hCtrl == g_hS) {
if (!g_bS) {
RECT rc; GetClientRect(g_hS, &rc);
g_bS = CreateGradientBrush(rc.right-rc.left, rc.bottom-rc.top, RGB(30,60,120), RGB(60,90,138));
}
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255,255,255));
return (LRESULT)g_bS;
}
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
SetTextColor(hdc, RGB(0,0,0));
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
}
case WM_CTLCOLOREDIT: {
HDC hdc = (HDC)wParam;
SetTextColor(hdc, RGB(0,0,0));
SetBkColor(hdc, RGB(255,255,255));
return (LRESULT)g_bW;
}
case WM_CLOSE:
ShowWindow(hwnd, SW_HIDE);
return 0;
case WM_DESTROY: {
KillTimer(hwnd, TIMER_CPS);
if (g_recPend) { KillTimer(hwnd, g_cdTimer); g_recPend = false; }
if (g_mpAct) {
g_mpStop.store(true);
if (g_mpHook) UnhookWindowsHookEx(g_mpHook);
if (g_hOv) DestroyWindow(g_hOv);
g_mpAct = false;
}
if (g_hMacW) DestroyWindow(g_hMacW);
UnregisterHotKey(hwnd, HOTKEY_F6);
UnregisterHotKey(hwnd, HOTKEY_ESC);
UnregisterHotKey(hwnd, HOTKEY_F7);
DeleteTrayIcon();
if (g_mh) UnhookWindowsHookEx(g_mh);
if (g_kh) UnhookWindowsHookEx(g_kh);
if (g_hPlay) { WaitForSingleObject(g_hPlay, 500); CloseHandle(g_hPlay); g_hPlay = nullptr; }
if (g_icon) DestroyIcon(g_icon);
if (g_fS) DeleteObject(g_fS);
if (g_fN) DeleteObject(g_fN);
if (g_bW) DeleteObject(g_bW);
if (g_bS) DeleteObject(g_bS);
g_init = false;
g_se = true;
PostQuitMessage(0);
break;
}
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
(void)hPrev; (void)nShow;
SetDPIAwareness();
bool onlyHelp = false;
if (lpCmd && strlen(lpCmd) > 0) {
string cmd = lpCmd;
size_t start = cmd.find_first_not_of(" \t");
if (start != string::npos) {
cmd = cmd.substr(start);
size_t end = cmd.find_last_not_of(" \t");
if (end != string::npos) cmd = cmd.substr(0, end+1);
if (cmd == "--help" || cmd == "-h") onlyHelp = true;
}
}
if (onlyHelp) {
PrintCommandHelp();
return 0;
}
HANDLE hMutex = CreateMutexA(NULL, FALSE, "AutoClickerMutex");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
HWND hExisting = FindWindowA("AutoClickerClass", NULL);
if (hExisting) {
if (lpCmd && strlen(lpCmd) > 0) {
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = strlen(lpCmd) + 1;
cds.lpData = lpCmd;
SendMessage(hExisting, WM_COPYDATA, 0, (LPARAM)&cds);
ShowWindow(hExisting, SW_SHOW);
SetForegroundWindow(hExisting);
} else {
ShowWindow(hExisting, SW_SHOW);
SetForegroundWindow(hExisting);
}
}
CloseHandle(hMutex);
return 0;
}
timeBeginPeriod(1);
g_icon = LoadCustomIcon();
WNDCLASSA wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.lpszClassName = "AutoClickerClass";
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hIcon = g_icon;
if (!RegisterClassA(&wc)) {
MessageBoxA(nullptr, "注册窗口类失败", "错误", MB_OK);
timeEndPeriod(1); CloseHandle(hMutex); return 1;
}
g_hM = CreateWindowA("AutoClickerClass", "自动连点器",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 1100, 820,
nullptr, nullptr, hInst, nullptr);
if (!g_hM) {
MessageBoxA(nullptr, "创建窗口失败", "错误", MB_OK);
timeEndPeriod(1); CloseHandle(hMutex); return 1;
}
SendMessageA(g_hM, WM_SETICON, ICON_SMALL, (LPARAM)g_icon);
SendMessageA(g_hM, WM_SETICON, ICON_BIG, (LPARAM)g_icon);
if (lpCmd && strlen(lpCmd) > 0) {
char* cmdCopy = new char[strlen(lpCmd) + 1];
strcpy(cmdCopy, lpCmd);
PostMessage(g_hM, WM_EXECUTE_CMD, (WPARAM)cmdCopy, 0);
}
ShowWindow(g_hM, SW_SHOW);
UpdateWindow(g_hM);
HANDLE hClicker = CreateThread(nullptr, 0, ClickerThread, nullptr, 0, nullptr);
if (!hClicker) {
MessageBoxA(nullptr, "创建线程失败", "错误", MB_OK);
timeEndPeriod(1); CloseHandle(hMutex); return 1;
}
MSG msg;
while (GetMessageA(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
g_se = true;
WaitForSingleObject(hClicker, INFINITE);
CloseHandle(hClicker);
timeEndPeriod(1);
CloseHandle(hMutex);
return 0;
}

粤公网安备44010602015266号
共 2 条回复
当我使用macOS时 : 31 | using namespace Gdiplus; | ^ temp2.cpp:33:5: error: unknown type name 'HMODULE' 33 | HMODULE hUser32 = GetModuleHandleW(L"user32.dll"); | ^ temp2.cpp:33:23: error: use of undeclared identifier 'GetModuleHandleW' 33 | HMODULE hUser32 = GetModuleHandleW(L"user32.dll"); | ^ temp2.cpp:35:23: error: unknown type name 'WINAPI' 35 | typedef BOOL (WINAPI *SetDPIAwarenessContextFunc)(PVOID); | ^ temp2.cpp:35:59: error: unknown type name 'PVOID' 35 | typedef BOOL (WINAPI *SetDPIAwarenessContextFunc)(PVOID); | ^ temp2.cpp:35:17: error: a type specifier is required for all declarations 35 | typedef BOOL (WINAPI *SetDPIAwarenessContextFunc)(PVOID); | ~~~~~~~ ^ temp2.cpp:36:9: error: unknown type name 'SetDPIAwarenessContextFunc' 36 | SetDPIAwarenessContextFunc pV2 = (SetDPIAwarenessContextFunc)GetProcAddress(hUser32, "SetProcessDPIAwarenessContext"); | ^ temp2.cpp:36:43: error: use of undeclared identifier 'SetDPIAwarenessContextFunc' 36 | SetDPIAwarenessContextFunc pV2 = (SetDPIAwarenessContextFunc)GetProcAddress(hUser32, "SetProcessDPIAwarenessContext"); | ^ temp2.cpp:37:29: error: use of undeclared identifier 'PVOID' 37 | if (pV2) { if (pV2((PVOID)-4)) return; } | ^ temp2.cpp:39:5: error: unknown type name 'HMODULE' 39 | HMODULE hShcore = LoadLibraryW(L"Shcore.dll"); | ^ temp2.cpp:39:23: error: use of undeclared identifier 'LoadLibraryW' 39 | HMODULE hShcore = LoadLibraryW(L"Shcore.dll"); | ^ temp2.cpp:41:26: error: unknown type name 'WINAPI' 41 | typedef HRESULT (WINAPI *SetDPIAwarenessFunc)(int); | ^ temp2.cpp:41:17: error: a type specifier is required for all declarations 41 | typedef HRESULT (WINAPI *SetDPIAwarenessFunc)(int); | ~~~~~~~ ^ temp2.cpp:42:9: error: use of undeclared identifier 'SetDPIAwarenessFunc'; did you mean 'SetDPIAwareness'? 42 | SetDPIAwarenessFunc pPerMonitor = (SetDPIAwarenessFunc)GetProcAddress(hShcore, "SetProcessDpiAwareness"); | ^~~~~~~~~~~~~~~~~~~ | SetDPIAwareness temp2.cpp:32:6: note: 'SetDPIAwareness' declared here 32 | void SetDPIAwareness() { | ^ temp2.cpp:42:28: error: expected ';' after expression 42 | SetDPIAwarenessFunc pPerMonitor = (SetDPIAwarenessFunc)GetProcAddress(hShcore, "SetProcessDpiAwareness"); | ^ | ; temp2.cpp:42:29: error: use of undeclared identifier 'pPerMonitor' 42 | SetDPIAwarenessFunc pPerMonitor = (SetDPIAwarenessFunc)GetProcAddress(hShcore, "SetProcessDpiAwareness"); | ^ temp2.cpp:42:44: error: use of undeclared identifier 'SetDPIAwarenessFunc'; did you mean 'SetDPIAwareness'? 42 | SetDPIAwarenessFunc pPerMonitor = (SetDPIAwarenessFunc)GetProcAddress(hShcore, "SetProcessDpiAwareness"); | ^~~~~~~~~~~~~~~~~~~ | SetDPIAwareness temp2.cpp:32:6: note: 'SetDPIAwareness' declared here 32 | void SetDPIAwareness() { | ^ temp2.cpp:43:13: error: use of undeclared identifier 'pPerMonitor' 43 | if (pPerMonitor) { if (SUCCEEDED(pPerMonitor(2))) { FreeLibrary(hShcore); return; } } | ^ temp2.cpp:43:42: error: use of undeclared identifier 'pPerMonitor' 43 | if (pPerMonitor) { if (SUCCEEDED(pPerMonitor(2))) { FreeLibrary(hShcore); return; } } | ^ fatal error: too many errors emitted, stopping now [-ferror-limit=] 20 errors generated.