C++自制简易连点器番外篇附加一个坑

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、再见 

再见。

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
制作一个简易系统需要考虑很多因素,包括系统的目标、功能和设计等,因此在这里我只能给出一个简单的示例,帮助你了解如何使用C++来开发一个系统。 假设我们要实现一个简单的学生成绩管理系统,实现以下功能: - 添加学生信息 - 删除学生信息 - 修改学生信息 - 查询学生信息 - 显示所有学生信息 首先,我们需要定义一个学生类,用于存储学生的姓名、学号和成绩等信息。代码如下: ```c++ class Student { public: string name; // 学生姓名 int id; // 学生学号 int score; // 学生成绩 Student(string name, int id, int score) { this->name = name; this->id = id; this->score = score; } }; ``` 接下来,我们需要实现一个学生信息管理类,用于实现上述功能。代码如下: ```c++ class StudentManager { private: vector<Student> students; // 学生信息列表 public: // 添加学生信息 void addStudent(Student student) { students.push_back(student); } // 删除学生信息 void removeStudent(int id) { for (auto it = students.begin(); it != students.end(); it++) { if (it->id == id) { students.erase(it); break; } } } // 修改学生信息 void modifyStudent(int id, Student student) { for (auto& s : students) { if (s.id == id) { s = student; break; } } } // 查询学生信息 Student* queryStudent(int id) { for (auto& s : students) { if (s.id == id) { return &s; } } return nullptr; } // 显示所有学生信息 void displayAllStudents() { for (auto& s : students) { cout << "name: " << s.name << ", id: " << s.id << ", score: " << s.score << endl; } } }; ``` 最后,我们可以在主函数中使用这个学生信息管理类来实现学生成绩管理系统。代码如下: ```c++ int main() { StudentManager manager; // 添加学生信息 manager.addStudent(Student("张三", 1001, 90)); manager.addStudent(Student("李四", 1002, 85)); manager.addStudent(Student("王五", 1003, 95)); // 修改学生信息 manager.modifyStudent(1002, Student("赵六", 1004, 88)); // 删除学生信息 manager.removeStudent(1003); // 查询学生信息 Student* student = manager.queryStudent(1001); if (student != nullptr) { cout << "name: " << student->name << ", id: " << student->id << ", score: " << student->score << endl; } // 显示所有学生信息 manager.displayAllStudents(); return 0; } ``` 这个简单的示例演示了如何使用C++来实现一个简单的系统。但是,实际上,真正的系统开发需要考虑更多的因素,如数据存储、用户界面设计、安全性等,需要使用更加完整和复杂的框架和工具来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值