0%

windows编程(2)- 消息与循环

文章首发于:My Blog 欢迎大佬们前来逛逛

win32打开控制台的方法

首先加入输入输出头文件

  1. AllocConsole:为控制台分配空间

  2. GetStdHandle:创建一个标准输入输出设备,指定其为STD_OUTPUT_HANDLE则就是一个标准输出控制台。

  3. 创建一个HANDLE变量console接收GetStdHandle创建的控制台。

往控制台中写数据:

使用WriteConsole写数据,首先要格式化字符串,后面两个直接给NULL即可。

1
2
3
char buff[256
sprintf(buff, "xxxxxx", ...);
WriteConsole(console, buff, strlen(buff), NULL, NULL);

窗口创建消息

WM_CREATE消息,在创建窗口时(此时窗口未显示时)触发

在窗口创建时创建一个控制台:

1
2
3
4
5
6
7
8
9
10
11
12
13
HANDLE console;
//窗口创建消息
void onCreate() {
//打开控制台
AllocConsole();
//设置标准输出设备
console = GetStdHandle(STD_OUTPUT_HANDLE);
}

...
case WM_CREATE:
onCreate();
break;

窗口销毁消息

WM_DESTROY消息,在销毁窗口的时候触发,但是此时窗口并没有关闭,我们需要手动触发关闭窗口的消息。

此函数:向系统指示线程已发出终止 (退出) 的请求。 它通常用于响应 WM_DESTROY 消息。

此时我们可以发送一个关闭窗口的消息:

PostQuitMessage函数,传递任意一个参数,返回值为一个WM_QUIT消息,然后此消息被GetMessage所捕获,此时GetMessage返回0,则关闭消息循环,结束窗口。

1
2
3
4
5
6
7
8
9
10
11
12
//窗口销毁消息
void onDestroy() {
PostQuitMessage(666);//发送WM_QUIT消息,导致GetMessage函数返回0
}

...

case WM_DESTROY:
{
onDestroy();
break;
}

定时器消息

WM_TIMER接受定时器发出的消息,我们可以自己创建定时器:

wParam:定时器的标识符;lParam:定时器中断函数指针。

使用SetTimer创建定时器,接受四个参数

  • hWnd:与计时器相关联的窗口句柄
  • nIDEvent:创建一个编号为此的计时器,用于标识创建的计时器,即标识符
  • uElapse:计时器的时间间隔。
  • lpTimerFunc:指定计时器工作是轮询还是中断(这个很重要)

首先我们创建一个简单的计时器:

1
2
3
4
5
6
7
8
case WM_CREATE:
//在创建窗口之前显示此消息
MessageBox(hwnd, "我创建了", "ylh的Box\n", NULL);
//中断消息
SetTimer(hwnd, 11111, 222, NULL); //第四个参数是NULL,则发送消息
SetTimer(hwnd, 22222, 333, NULL);
break;
}

两个计时器的标记分别为 11111 和 22222,他们分别每隔 222ms 和333ms执行一次操作。

如果第四个参数是NULL,则表示轮询,即发送消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SYSTEMTIME systime{}; //存储事件的结构体
...
case WM_TIMER: {
GetLocalTime(&systime); //获取当前的日期时间
//!!!!!!!!
int wmID = LOWORD(wParam); //获取定时器的消息
char timebuff[256]{};
if (wmID == 11111) {
sprintf(timebuff, "1111消息: 定时器消息: %d-%d-%d\n", systime.wYear, systime.wMonth, systime.wDay);
}
else {
sprintf(timebuff, "other消息: 定时器消息: %d-%d-%d\n", systime.wYear, systime.wMonth, systime.wDay);
}
WriteConsole(console, timebuff, strlen(timebuff), NULL, NULL);
break;
}

定时器的中断机制

SetTiemr第四个参数传递一个函数指针,则表示定时器每隔多长时间进行一次中断机制。

1
2
3
4
5
6
7
8
9
void CALLBACK TimeProc(HWND hwnd, UINT uint , UINT_PTR uPtr, DWORD dword) {
//MessageBox(hwnd, "警告!", "Warning", NULL);
//中断消息
}

...

//中断消息
SetTimer(hwnd, 33333, 444, (TIMERPROC)TimeProc); //每隔444ms单独进入中断函数执行操作

鼠标移动消息

WM_MOUSEMOVE 指定了鼠标移动的触发消息。

wParam:指定了鼠标移动时某些键是否被按下;lParam:鼠标的位置。

如果得到鼠标移动时的位置?lParam参数

  • LOWORD:低16位存储了x坐标
  • HIWORD:高16位存储了y坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//鼠标移动消息
void onMouseMove(WPARAM wParam, LPARAM lParam) {
if (wParam == MK_CONTROL) {
char buff[256]{};
sprintf(buff, "按下了Control键。\n");
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
sprintf(buff, "(x,y) = (%d,%d)\n", x, y);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}

...

case WM_MOUSEMOVE:
onMouseMove(wParam,lParam);
break;
}

案例:鼠标移动时切换样式

在窗口左边时为样式1,在窗口右边为样式2。

需要的一些操作:

  1. 添加资源文件(在此不多赘述)
  2. 加载鼠标资源:使用LoadCursor函数,使用MAKEINTRESOURCE将整数值转换为与资源管理功能兼容的资源类型。 此宏用于代替包含资源名称的字符串。
  3. 获取窗口的宽度:GetWindowRect函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//案例:鼠标样式切换
void MouseChange(HWND hwnd,WPARAM wParam,LPARAM lParam) {
HINSTANCE hInstance = GetModuleHandle(NULL);
HCURSOR cursor1 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
HCURSOR cursor2 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR2));
int x = LOWORD(lParam), y = HIWORD(lParam);
RECT rect;
GetWindowRect(hwnd, &rect);
if (x < rect.right - rect.left >> 1) {
SetCursor(cursor1);
}
else {
SetCursor(cursor2);
}
}

...
case WM_MOUSEMOVE:
onMouseMove(wParam,lParam);
//鼠标移动时样式切换
MouseChange(hwnd, wParam, lParam);
break;

鼠标点击消息

WM_LBUTTONDOWN消息处理鼠标左键点击,右键点击和中键等类似

wParam:指定了鼠标移动时某些键是否被按下;lParam:鼠标的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//鼠标左键按下消息
void OnMouseClick(WPARAM wParam,LPARAM lParam) {
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
sprintf(buff, "(x,y) = (%d,%d)\n", x, y);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}

...

case WM_MBUTTONDOWN://鼠标中键按下
case WM_RBUTTONDOWN://鼠标右键按下
case WM_LBUTTONDOWN://鼠标左键按下
OnMouseClick(wParam,lParam);
break;

鼠标滑轮消息

WM_MOUSEHWEEL触发滑轮消息。

wParam:为正说明是正滑,为负则是反滑(记得转换为int类型);lParam:指定滑轮所在的xy坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//鼠标滑轮消息
void onMouseWheel(WPARAM wParam, LPARAM lParam) {
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
//正表示往前;负值表示往后
sprintf(buff, "wParam: %d\n", (int)wParam / WHEEL_DELTA);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}

...

case WM_MOUSEWHEEL:
onMouseWheel(wParam, lParam);
break;

键盘消息

WM_KEYUP键盘弹起,WM_KEYDOWN键盘按下

wParam:表示按键的虚拟键盘的代码。lParam:表示一些标记。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//按键消息
void onKeyDown(WPARAM wParam, LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
int f = LOWORD(lParam);
sprintf(buff, "键盘按下: %d\n", (int)wParam);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//按键弹起
void onKeyUp(WPARAM wParam, LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
sprintf(buff, "wParam键盘弹起: %d\n", (int)wParam);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}

...

case WM_KEYUP:
//onKeyUp(wParam, lParam);
break;
case WM_KEYDOWN:
//onKeyDown(wParam,lParam);
break;

外部设备消息

WM_DEVICECHANGE消息处理外部设备的消息。

*Param:发生的事件,包括已向系统添加或删除设备。或者已插入设备或介质等提示功能。lParam指向包含特定于事件的数据的结构的指针。 其格式取决于 wParam* 参数的值

什么是外部设备? 外部io设备等等。。。

我们可以获取他们的信息。

首先引入头文件: #include <Dbt.h>

DBT_DEVICEARRIVAL表示外部设备进入。

即当触发消息WM_DEVICECHANGE时, wParam == DBT_DEVICEARRIVAL 时,会触发。

如果我们想拷贝别人U盘上的内容:则我们可以这样操作

  1. 将lParam转换为PDEV_BROADCAST_VOLUME类型。这个结构体指针中有一个参数dbcv_unitmask表示的是外部设备所处的盘符,因此我们便可以获取U盘的盘符,然后执行 copy操作。
  2. dbcv_unitmask是由右往左表示的,即最右边到左边表示‘A’ B C D ….,如果是0,则表示是A盘,因此我们便可以获取盘符
  3. xcopy的拷贝操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//外部设备消息(引入: #include <Dbt.h>)
void onDevice(WPARAM wParam,LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
//插入设备: 0x8000
if (wParam == DBT_DEVICEARRIVAL) {
auto pDev = (PDEV_BROADCAST_HDR)lParam;
auto pVol = (PDEV_BROADCAST_VOLUME)pDev;

DWORD dw = pVol->dbcv_unitmask; //存储了盘符的32位数据
auto f = [&]() {
int i = 0;
for (; i < 26; i++) {
if (dw & 1) {
break;
}
dw >>= 1; //右移一位
}
return i + 'A';//获取盘符
};
char panfu = f();
sprintf(buff, "U盘的盘符是: %c\n", panfu);
memset(buff, 0, sizeof(buff));
sprintf(buff, "xcopy %c:\\test E:\\dst /E", panfu);
system(buff);
}
}

...

case WM_DEVICECHANGE:
onDevice(wParam, lParam);
break;

本节源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#include <Windows.h>
#include <cstdio>
#include <Dbt.h>
#include "resource.h"

HANDLE console; //控制台

//定时器中断函数
void CALLBACK TimeProc(HWND hwnd, UINT uint , UINT_PTR uPtr, DWORD dword) {
//中断消息
}
//窗口创建消息
void onCreate() {
//打开控制台
AllocConsole();
//设置标准输出设备
console = GetStdHandle(STD_OUTPUT_HANDLE);
}
//定时器消息
void onTimer() {
}
//窗口销毁消息
void onDestroy() {
PostQuitMessage(666);//发送WM_QUIT消息,导致GetMessage函数返回0
}
//鼠标移动消息
void onMouseMove(WPARAM wParam, LPARAM lParam) {
if (wParam == MK_CONTROL) {
char buff[256]{};
sprintf(buff, "按下了Control键。\n");
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
sprintf(buff, "(x,y) = (%d,%d)\n", x, y);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//鼠标左键按下消息
void OnMouseClick(WPARAM wParam,LPARAM lParam) {
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
sprintf(buff, "(x,y) = (%d,%d)\n", x, y);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//鼠标滑轮消息
void onMouseWheel(WPARAM wParam, LPARAM lParam) {
int x = LOWORD(lParam), y = HIWORD(lParam);
char buff[256]{};
//正表示往前;负值表示往后
sprintf(buff, "wParam: %d\n", (int)wParam / WHEEL_DELTA);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//案例:鼠标样式切换
void MouseChange(HWND hwnd,WPARAM wParam,LPARAM lParam) {
HINSTANCE hInstance = GetModuleHandle(NULL);
HCURSOR cursor1 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
HCURSOR cursor2 = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR2));
int x = LOWORD(lParam), y = HIWORD(lParam);
RECT rect;
GetWindowRect(hwnd, &rect);
if (x < rect.right - rect.left >> 1) {
SetCursor(cursor1);
}
else {
SetCursor(cursor2);
}
}
//按键消息
void onKeyDown(WPARAM wParam, LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
int f = LOWORD(lParam);
sprintf(buff, "键盘按下: %d\n", (int)wParam);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//按键弹起
void onKeyUp(WPARAM wParam, LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
sprintf(buff, "wParam键盘弹起: %d\n", (int)wParam);
WriteConsole(console, buff, strlen(buff), NULL, NULL);
}
//外部设备消息(引入: #include <Dbt.h>)
void onDevice(WPARAM wParam,LPARAM lParam) {
char buff[256]{};
//wParam表示虚拟按键代码
//插入设备: 0x8000
if (wParam == DBT_DEVICEARRIVAL) {
auto pDev = (PDEV_BROADCAST_HDR)lParam;
auto pVol = (PDEV_BROADCAST_VOLUME)pDev;

DWORD dw = pVol->dbcv_unitmask; //存储了盘符的32位数据
auto f = [&]() {
int i = 0;
for (; i < 26; i++) {
if (dw & 1) {
break;
}
dw >>= 1; //右移一位
}
return i + 'A';//获取盘符
};
char panfu = f();
sprintf(buff, "U盘的盘符是: %c\n", panfu);
memset(buff, 0, sizeof(buff));
sprintf(buff, "xcopy %c:\\test E:\\dst /E", panfu);
system(buff);
}
}
//消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

switch (message)
{
case WM_LBUTTONDBLCLK: {
char buff[256]{};
sprintf(buff, "鼠标双击了");
WriteConsole(console, buff, strlen(buff), NULL, NULL);
break;
}
case WM_DEVICECHANGE:
onDevice(wParam, lParam);
break;
case WM_KEYUP:
//onKeyUp(wParam, lParam);
break;
case WM_KEYDOWN:
//onKeyDown(wParam,lParam);
break;
case WM_MOUSEWHEEL:
onMouseWheel(wParam, lParam);
break;
case WM_MBUTTONDOWN://鼠标中键按下
case WM_RBUTTONDOWN://鼠标右键按下
case WM_LBUTTONDOWN://鼠标左键按下
OnMouseClick(wParam,lParam);
break;
case WM_MOUSEMOVE:
onMouseMove(wParam,lParam);
//鼠标移动时样式切换
MouseChange(hwnd, wParam, lParam);
break;
case WM_TIMER: {
onTimer();
break;
}
case WM_CREATE:
onCreate();
break;
case WM_DESTROY:
{
onDestroy();
break;
}
default:
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR cmd,
int flag
) {
//调用控制台
AllocConsole();
console = GetStdHandle(STD_OUTPUT_HANDLE);

//注册窗口类
WNDCLASSEXW wcex{NULL};

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcex.lpfnWndProc = WndProc; //消息处理函数
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL; //图标
wcex.hCursor = NULL; //光标
wcex.hbrBackground = NULL;//画刷
wcex.lpszMenuName = NULL;//菜单名字
wcex.lpszClassName = L"ylh的窗口类";
wcex.hIconSm = NULL;

RegisterClassExW(&wcex);
//创建窗口
HWND hwnd = CreateWindowExW(
wcex.style,
wcex.lpszClassName,
L"ylh牛逼",
WS_OVERLAPPEDWINDOW, //窗口风格
100, 100, 500, 500,
NULL, NULL, wcex.hInstance, NULL
);
//显示窗口
ShowWindow(hwnd, SW_SHOW);
//刷新窗口
UpdateWindow(hwnd);
//消息循环
MSG msg{};
while (GetMessage(&msg, NULL, NULL, NULL)) { //GetMessage的返回值控制退出
//翻译消息
TranslateMessage(&msg);
//发送消息
DispatchMessage(&msg);
}
system("pause");
return 0;
}
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道