1、开场白
来了,来了,番外篇来了。。。。。。。。。
1.5、回忆杀
有些时候吧,真的想给自己一个大逼斗。
上上篇(文章链接)最后说要明年才能写完,但其实当时基本上已经做完了,但是就在我测试的时候发现了一个逆天Bug,点击按键切换后无法生效,这就意味着核心功能直接丧失了,我调试了好就,微软官方的资料翻了又翻,就是找不到问题在哪儿,然后我就被这个Bug卡了一个多月。。。。。。直到昨天晚上,我在看代码时突然就找到问题了。。。。。。
在回调函数WindowProc中,有一个整形变量(局部)ClickKey,用于记录当前点击按键,它的初始值是0,表示鼠标左键。我添加了一个组合框控件,其中有三个选项,默认选中第一项,也就是鼠标左键。我对CBN_SELCHANGE(组合框当前选中项发生改变)消息进行操作,每收到一次就获取当前选中项的索引(恰好对应0,1,2)并赋值给ClickKey,最后在计时器消息中对每一个值做出不同的操作。可以说明的是,上述操作中没有任何逻辑、顺序错误,错就错在ClickKey是一个局部变量。。。。
1.9、坑
在回调函数中总有如下代码结构:
switch(uMsg)
{
case WM_CREATE:
break;
case WM_PAINT:
break;
case WM_COMMAND:
break;
case WM_HOTKEY:
break;
case WM_TIMER:
break;
...
}
在这看似简单的代码中有一个惊天大坑:假设在函数内部、switch外部定义一个局部变量并赋值,然后在WM_COMMAND消息中对这个值做出改变,最后在WM_TIMER中使用该变量,那么这个变量的值将不会发生任何改变,依旧是他的初始值。为什么?因为当WM_COMMAND分支结束后会立刻销毁变量的值,也就意味着它会被初始化。。。。。。要解决它有两种方法:一是设为全局变量,二是设为静态变量,这两种变量的生命周期都要比局部变量长。
好好好,说到底,我调试来调试去,最后一切的根源就是少写了六个英文字母。。。。。。
2、正片
制作第一步--创建窗口
直接给出熟悉的代码
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//创建窗口类
WNDCLASSEX wndclass = { 0 };
HWND hwnd;
MSG msg;
TCHAR lpszClassName[] = { TEXT("MouseClickerWin32") };
TCHAR lpszAppName[] = { TEXT("鼠标连点器") };
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wndclass.hIconSm = NULL;
wndclass.hCursor = LoadCursor(hInstance, IDC_ARROW);
wndclass.hbrBackground = CreateSolidBrush(0xFFFFFF);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = lpszClassName;
//注册窗口类
RegisterClassEx(&wndclass);
//创建窗口
hwnd = CreateWindowEx(WS_EX_TOPMOST, lpszClassName, lpszAppName,
WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HIGHT, NULL, NULL, hInstance, NULL);
//显示窗口
ShowWindow(hwnd, SW_SHOW);
//更新窗口
UpdateWindow(hwnd);
//消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//WinMain函数返回值
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY: //窗口销毁消息处理
PostQuitMessage(0); //销毁窗口
break;
}
}
制作第二步--基本准备
首先为了控制代码量,这里要批量创建控件,因此需要定义一个有关的结构体,并创建结构数组以保存每个控件的信息
这里创建一个头文件来定义结构体以及存放控件ID
//fResource.h头文件
//
#pragma once
#ifndef FRESOURCE_H_
#define FRESOURCE_H_
#define WIDTH 255 //窗口宽度
#define HIGHT 150 //窗口高度
#define IDC_BTN_MODIFY 1001 //修改按钮
#define IDC_BTN_OK 1002 //确定按钮
#define IDC_BTN_CANCLE 1003 //取消按钮
#define IDC_CBX_CLICKKEY 1004 //点击按键组合框
#define IDC_EDIT_INR 1005 //点击间隔编辑框
#define IDT_TIMER 1006 //计时器(控制点击)
#define IDT_TIMER_EDITSCAN 1007 //计时器(检测编辑控件中是否有内容)
#define HKEY_ALT 0x1011 //热键
typedef struct ControllersInfo
{
int x;
int y;
int nWidth;
int nHight;
LPCTSTR lpszClaaName;
LPCTSTR lpszWindowName;
LONG wndStyle;
int nID;
}CONTROLLERSINFOSTRUCT;
#endif // !FRESOURCE_H_
接着在MouseClicker.cpp中声明结构体变量并初始化
//MouseClicker.cpp
///
CONTROLLERSINFOSTRUCT ChildWnd[]
{
{100, 10, 82, 20, TEXT("Edit"), NULL, ES_NUMBER | WS_BORDER | WS_DISABLED, IDC_EDIT_INR},
{85, 35, 70, 25, TEXT("Button"), TEXT("确定"), BS_PUSHBUTTON | WS_DISABLED, IDC_BTN_OK},
{10, 35, 70, 25, TEXT("Button"), TEXT("修改频率"), BS_PUSHBUTTON, IDC_BTN_MODIFY},
{160, 35, 70, 25, TEXT("Button"), TEXT("取消"), BS_PUSHBUTTON | WS_DISABLED, IDC_BTN_CANCLE},
{80, 65, 85, 200, TEXT("ComboBox"), NULL, CBS_DROPDOWNLIST, IDC_CBX_CLICKKEY},
};
#define CONTROLLERS (sizeof(ChildWnd) / sizeof(ChildWnd[0]))//计算结构体数组中的元素个数
还有其他的常规准备
//MouseClicker.cpp
///
//储存点击按键信息
DWORD KeyDownValue[3] = { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
DWORD KeyUpValue[3] = { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
//缓冲区
TCHAR szBuf[20] = { 0 };
int clickCTR = 1;//储存点击频率
BOOL isTimerCreated = FALSE, isUserInput = FALSE;
//isTimerCreated表是计时器是否创建,用于控制连点器的启动及停止
//isUserInput表示是否为用户输入模式,1、用于控制是否能够启动连点器,
//及如果用户正在编辑点击频率,则不允许启动连点器。
//2、用于控制是否创建用于编辑控件检测的计时器
//以下为回调函数WindowProc中的变量
HWND ChildWindow[CONTROLLERS];//控件句柄
HDC hdc; //设备DC
PAINTSTRUCT ps; //绘图结构体
HGDIOBJ hFontOld; //用于设置字体
static HFONT hFont; //用于创建字体,static一定要加
static int ClickKey = 0; //存储点击按键,static一定要加
int nLen = 0; //用于储存编辑控件内文本长度
制作第三步--界面实现
首先是WM_CREATE消息中批量创建控件
//MouseClicker.cpp
///
case WM_CREATE:
//创建一个好看的字体
hFont = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
//填充编辑控件中的文本
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);//将格式化的字符串写入缓冲区
ChildWnd[0].lpszWindowName = szBuf;//设置编辑控件中的文本为缓冲区中的字符串
//批量创建控件
for (int i = 0; i < CONTROLLERS; i++)
{
ChildWindow[i] = CreateWindow(ChildWnd[i].lpszClaaName, ChildWnd[i].lpszWindowName, WS_CHILD | WS_VISIBLE | ChildWnd[i].wndStyle,
ChildWnd[i].x, ChildWnd[i].y, ChildWnd[i].nWidth, ChildWnd[i].nHight, hwnd, (HMENU)ChildWnd[i].nID, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
//使用创建的字体
SendMessage(ChildWindow[i], WM_SETFONT, (WPARAM)hFont, 0);
}
//向组合框的下拉列表中添加列表项
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标左键"));
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标中键"));
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标右键"));
//设置组合框的默认选项
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_SETCURSEL, 0, 0);
//设置编辑控件的最大可填写字数
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), EM_SETLIMITTEXT, (WPARAM)2, 0);
//
RegisterHotKey(hwnd, HKEY_ALT, MOD_ALT, NULL);//注册连点器的启动快捷键
break;
接着是WM_PAINT消息处理
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetBkMode(hdc, TRANSPARENT);
hFontOld = SelectObject(hdc, hFont);
TextOut(hdc, 10, 90, TEXT("按下[Alt]键开始/结束点击"), _tcslen(TEXT("按下[Alt]键开始/结束点击")));
TextOut(hdc, 10, 13, TEXT("当前点击频率:"), _tcslen(TEXT("当前点击频率:")));
TextOut(hdc, 10, 69, TEXT("点击按键:"), _tcslen(TEXT("点击按键:")));
SelectObject(hdc, hFontOld);
EndPaint(hwnd, &ps);
break;
接着是WM_CTLCOLORSTATIC消息
case WM_CTLCOLORSTATIC:
return (LRESULT)CreateSolidBrush(0xFFFFFF);//返回一个白色画刷,程序会用它来绘制禁用或只读状态编辑控件的背景色,当然也包括静态控件。
制作第四步--功能实现
1、WM_COMMAND消息
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BTN_MODIFY: //按下修改频率按钮
isUserInput = TRUE; //设置用户编辑模式为真
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), TRUE); //启用编辑控件
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), TRUE);//启用取消按钮
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), FALSE);//禁用修改频率按钮
//将编辑控件内的文本内容设置为空
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)TEXT(""));
break;
case IDC_BTN_OK: //按下确定按钮
isUserInput = FALSE; //设置用户编辑模式为假
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE); //禁用确定按钮
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), FALSE);//禁用编辑控件
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), FALSE);//禁用取消按钮
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), TRUE);//启用修改频率按钮
//获取编辑控件内的内容并赋值给clickCTR
clickCTR = GetDlgItemInt(hwnd, IDC_EDIT_INR, NULL, TRUE);
//判断clickCTR是否超出极限值(据我测试,最大点击频率为15)
if (clickCTR <= 0)
clickCTR = 1;
if (clickCTR > 15)
clickCTR = 15;
//你也可以这么写[doge]
/*(clickCTR <= 0) ? clickCTR = 1 : ((clickCTR > 15) ? clickCTR = 15 : clickCTR = clickCTR);*/
//将格式化字符串写入缓冲区
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);
//设置编辑控件中的文本为缓冲区中的字符串
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)szBuf);
break;
case IDC_BTN_CANCLE: //按下取消按钮
isUserInput = FALSE; //设置用户编辑模式为假
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE); //禁用确定按钮
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), FALSE); //禁用编辑控件
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), FALSE); //禁用取消按钮
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), TRUE); //启用修改频率按钮
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)szBuf);
break;
}
switch (HIWORD(wParam))
{
case EN_CHANGE: //编辑控件内容改变消息
if(isUserInput) //如果处于用户编辑模式
//设置计时器,以实时检测编辑控件内是否为空
SetTimer(hwnd, IDT_TIMER_EDITSCAN, 10, NULL);
break;
case CBN_SELCHANGE: //组合框选中项发生改变消息
//获取当前选中项的索引
ClickKey = SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_GETCURSEL, 0, 0);
break;
}
break;
2、WM_HOTKEY消息
case WM_HOTKEY:
switch (wParam)
{
case HKEY_ALT:
if (!isUserInput) //如果不处于用户输入模式
{
if (isTimerCreated)//如果计时器已经创建(点击开始)
{
ShowWindow(hwnd, SW_SHOWDEFAULT); //回复窗口正常显示
KillTimer(hwnd, IDT_TIMER); //销毁控制点击的计时器
isTimerCreated = FALSE; //将计时器创建状态设为假(注意它只负责这个计时器)
}
else//如果计时器没有创建(点击开始过并且已经结束)
{
ShowWindow(hwnd, SW_MINIMIZE); //最小化窗口
SetTimer(hwnd, IDT_TIMER, 1, NULL);//设置控制点击的计时器
isTimerCreated = TRUE; //将计时器创建状态设为真(注意它只负责这个计时器)
}
}
break;
}
break;
3、WM_TIMER消息
case WM_TIMER:
switch (wParam)
{
case IDT_TIMER: //如果是控制点击的计时器
for (int i = 1; i <= clickCTR; i++) //进行点击
mouse_event(KeyDownValue[ClickKey] | KeyUpValue[ClickKey], 0, 0, 0, 0);
break;
case IDT_TIMER_EDITSCAN: //如果是负责编辑控件检测的计时器
//获取编辑控件内文本的长度
nLen = SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_GETTEXTLENGTH, 0, 0);
if(nLen > 0)//如果有内容
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), TRUE);//启用确定按钮
else//编辑控件内无内容
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE);//禁用确定按钮
KillTimer(hwnd, IDT_TIMER_EDITSCAN);//销毁这个计时器,等待下次编辑控件内内容改变并且要处于用户编辑模式。节约资源
break;
}
break;
3、完整代码
//MouseClicker.cpp
///
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "fResource.h"
CONTROLLERSINFOSTRUCT ChildWnd[]
{
{100, 10, 82, 20, TEXT("Edit"), NULL, ES_NUMBER | WS_BORDER | WS_DISABLED, IDC_EDIT_INR},
{85, 35, 70, 25, TEXT("Button"), TEXT("确定"), BS_PUSHBUTTON | WS_DISABLED, IDC_BTN_OK},
{10, 35, 70, 25, TEXT("Button"), TEXT("修改频率"), BS_PUSHBUTTON, IDC_BTN_MODIFY},
{160, 35, 70, 25, TEXT("Button"), TEXT("取消"), BS_PUSHBUTTON | WS_DISABLED, IDC_BTN_CANCLE},
{80, 65, 85, 200, TEXT("ComboBox"), NULL, CBS_DROPDOWNLIST, IDC_CBX_CLICKKEY},
};
#define CONTROLLERS (sizeof(ChildWnd) / sizeof(ChildWnd[0]))
DWORD KeyDownValue[3] = { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
DWORD KeyUpValue[3] = { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
TCHAR szBuf[20] = { 0 };
int clickCTR = 1;
BOOL isTimerCreated = FALSE, isUserInput = FALSE;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndclass = { 0 };
HWND hwnd;
MSG msg;
TCHAR lpszClassName[] = { TEXT("MouseClickerWin32") };
TCHAR lpszAppName[] = { TEXT("鼠标连点器") };
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wndclass.hIconSm = NULL;
wndclass.hCursor = LoadCursor(hInstance, IDC_ARROW);
wndclass.hbrBackground = CreateSolidBrush(0xFFFFFF);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = lpszClassName;
RegisterClassEx(&wndclass);
hwnd = CreateWindowEx(WS_EX_TOPMOST, lpszClassName, lpszAppName, WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HIGHT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND ChildWindow[CONTROLLERS];
HDC hdc;
PAINTSTRUCT ps;
HGDIOBJ hFontOld;
static HFONT hFont;
static int ClickKey = 0;
int nLen = 0;
switch (uMsg)
{
case WM_CREATE:
hFont = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);
ChildWnd[0].lpszWindowName = szBuf;
for (int i = 0; i < CONTROLLERS; i++)
{
ChildWindow[i] = CreateWindow(ChildWnd[i].lpszClaaName, ChildWnd[i].lpszWindowName, WS_CHILD | WS_VISIBLE | ChildWnd[i].wndStyle,
ChildWnd[i].x, ChildWnd[i].y, ChildWnd[i].nWidth, ChildWnd[i].nHight, hwnd, (HMENU)ChildWnd[i].nID, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
SendMessage(ChildWindow[i], WM_SETFONT, (WPARAM)hFont, 0);
}
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标左键"));
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标中键"));
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_ADDSTRING, 0, (LPARAM)TEXT("鼠标右键"));
SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_SETCURSEL, 0, 0);
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), EM_SETLIMITTEXT, (WPARAM)2, 0);
//
RegisterHotKey(hwnd, HKEY_ALT, MOD_ALT, NULL);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetBkMode(hdc, TRANSPARENT);
hFontOld = SelectObject(hdc, hFont);
TextOut(hdc, 10, 90, TEXT("按下[Alt]键开始/结束点击"), _tcslen(TEXT("按下[Alt]键开始/结束点击")));
TextOut(hdc, 10, 13, TEXT("当前点击频率:"), _tcslen(TEXT("当前点击频率:")));
TextOut(hdc, 10, 69, TEXT("点击按键:"), _tcslen(TEXT("点击按键:")));
SelectObject(hdc, hFontOld);
EndPaint(hwnd, &ps);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BTN_MODIFY:
isUserInput = TRUE;
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), TRUE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), TRUE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), FALSE);
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)TEXT(""));
break;
case IDC_BTN_OK:
isUserInput = FALSE;
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), TRUE);
clickCTR = GetDlgItemInt(hwnd, IDC_EDIT_INR, NULL, TRUE);
(clickCTR <= 0) ? clickCTR = 1 : ((clickCTR > 15) ? clickCTR = 15 : clickCTR = clickCTR);
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)szBuf);
break;
case IDC_BTN_CANCLE:
isUserInput = FALSE;
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_EDIT_INR), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_CANCLE), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_MODIFY), TRUE);
StringCchPrintf(szBuf, _countof(szBuf), TEXT("%d次/每毫秒"), clickCTR);
SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_SETTEXT, 0, (LPARAM)szBuf);
break;
}
switch (HIWORD(wParam))
{
case EN_CHANGE:
if(isUserInput)
SetTimer(hwnd, IDT_TIMER_EDITSCAN, 10, NULL);
break;
case CBN_SELCHANGE:
ClickKey = SendMessage(GetDlgItem(hwnd, IDC_CBX_CLICKKEY), CB_GETCURSEL, 0, 0);
break;
}
break;
case WM_HOTKEY:
switch (wParam)
{
case HKEY_ALT:
if (!isUserInput)
{
if (isTimerCreated)
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
KillTimer(hwnd, IDT_TIMER);
isTimerCreated = FALSE;
}
else
{
ShowWindow(hwnd, SW_MINIMIZE);
SetTimer(hwnd, IDT_TIMER, 1, NULL);
isTimerCreated = TRUE;
}
}
break;
}
break;
case WM_TIMER:
switch (wParam)
{
case IDT_TIMER:
for (int i = 1; i <= clickCTR; i++)
mouse_event(KeyDownValue[ClickKey] | KeyUpValue[ClickKey], 0, 0, 0, 0);
break;
case IDT_TIMER_EDITSCAN:
nLen = SendMessage(GetDlgItem(hwnd, IDC_EDIT_INR), WM_GETTEXTLENGTH, 0, 0);
if(nLen > 0)
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), TRUE);
else
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OK), FALSE);
KillTimer(hwnd, IDT_TIMER_EDITSCAN);
break;
}
break;
case WM_CTLCOLORSTATIC:
return (LRESULT)CreateSolidBrush(0xFFFFFF);
case WM_DESTROY:
KillTimer(hwnd, IDT_TIMER);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//fResource.h
//
#pragma once
#ifndef FRESOURCE_H_
#define FRESOURCE_H_
#define WIDTH 255 //窗口宽度
#define HIGHT 150 //窗口高度
#define IDC_BTN_MODIFY 1001 //修改按钮
#define IDC_BTN_OK 1002 //确定按钮
#define IDC_BTN_CANCLE 1003 //取消按钮
#define IDC_CBX_CLICKKEY 1004 //点击按键组合框
#define IDC_EDIT_INR 1005 //点击间隔编辑框
#define IDT_TIMER 1006 //计时器(控制点击)
#define IDT_TIMER_EDITSCAN 1007 //计时器(检测编辑控件中是否有内容)
#define HKEY_ALT 0x1011 //热键
typedef struct ControllersInfo
{
int x;
int y;
int nWidth;
int nHight;
LPCTSTR lpszClaaName;
LPCTSTR lpszWindowName;
LONG wndStyle;
int nID;
}CONTROLLERSINFOSTRUCT;
#endif // !FRESOURCE_H_
4、动态启用/禁用确定按钮内部逻辑详解
5、再见
再见。