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
////////////