让我们与星光探索者一起,探索Windows吧!

下发假设您自己的窗口类的窗口过程函数均为WndProc,且您正确的按流程写了自定义的窗口程序

下方均是提供的一种处理方式的代码,并不是必须要这么做。

处理WM_CLOSE消息

WM_CLOSE消息在窗口即将终止时产生。

此消息附带的两个信息

  • wParam 未使用

  • lParam 未使用

处理窗口过程函数一个例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
// 应调用此函数,这样会发送WM_QUIT消息
// 使GetMessage返回0,这样进程才会正确退出
PostQuitMessage(0);
// 不要让默认处理函数处理,否则可能会出错
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

此外,WM_DESTROY和WM_QUIT也有退出的意思,这三个消息有什么区别呢?

WM_DESTROY在窗口销毁时产生,附带的两个信息和WM_CLOSE相同。但在WM_CLOSE可以选择决定是否销毁窗口。WM_QUIT消息在调用PostQuitMessage产生,使得GetMessage返回0。

处理WM_CREATE消息

WM_CREATE消息在窗口被创建时产生,在CreateWindow函数返回之前且窗口未显示之前产生。如果这个消息返回0,窗口将会被创建,返回-1将销毁创建的窗口。通常处理此消息时,我们初始化一些数据。

此事件附带的参数

  • wParam 未使用

  • lParam 指向 CREATESTRUCT结构的指针,可以获得CreateWindow的全部参数

处理窗口过程函数一个示例如下(运行本示例,您应该能看到先弹出一个消息框,再显示窗口):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_CREATE:
{
// 大部分小伙伴刚掌握时还不太熟练Windows基本数据类型
// LPCTSTR = const TCHAR*
// CREATESTRUE结构可以获得CreateWindow传的所有参数
// lpszClassName是其中之一
// reinterpret_cast是C++的类型转换使用的,
LPCTSTR lpClassName = reinterpret_cast<CREATESTRUCT*>(lParam)->lpszClass;
MessageBox(hwnd, TEXT("窗口创建了!"), lpClassName, MB_OK);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

处理WM_SIZE消息

WM_SIZE消息将在窗口被调整大小结束后产生。窗口在第一次创建的时候理所当然会调整大小,所以至少会触发一次

此消息附带的两个信息

  • wParam 调整大小的方式

  • lParam 调整后的新范围。低字节为调整后的新宽度,高字节为调整后的新高度

处理此事件的一个示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// #include <iostream> // 使用std::cout包含了这个头文件

LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
// 销毁分配的控制台
FreeConsole();
return 0;
case WM_CREATE:
// 给此窗口分配控制台,控制台会由系统处理
AllocConsole();
// 重定向标准输出流,使得printf可以在控制台输出
freopen("CONOUT$","w+t", stdout);
case WM_SIZE:
// 可用 LOWORD 获取低字节内容 HIWORD 获取高字节内容
std::cout << "宽度" << LOWORD(lParam) << "高度" << HIWORD(lParam) << std::endl;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

运行此程序,可能的结果如下:

处理常见鼠标消息

鼠标移动,按下,松开,会产生鼠标消息。一般情况下,只有在你自己的窗口产生的鼠标消息,才会接收到,要想鼠标在哪个窗口都能处理到鼠标消息,请使用SetCapture 函数

鼠标在窗口移动时,产生WM_MOUSEMOVE消息消息,根据移动的快慢,产生WM_MOUSEMOVE的消息频率是不同的

鼠标左键按下时,产生WM_LBUTTONDOWN消息 ,松开时产生WM_LBUTTONUP消息。鼠标右键按下时,产生WM_RBUTTONDOWN消息,松开时产生WM_RBUTTONUP消息

消息产生均附带的两个信息

  • wParam 产生消息时,其他按键的情况(例如Ctrl,Shift键有没有按下)

  • lParam 产生消息时,低字节为鼠标的x坐标,高字节为鼠标的y坐标,是相对窗口而言的

处理消息的一种示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
FreeConsole();
return 0;
case WM_CREATE:
AllocConsole();
freopen("CONOUT$","w+t", stdout);
case WM_LBUTTONDOWN:
printf("鼠标左键按下 x:%d y: %d\n", LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_LBUTTONUP:
printf("鼠标左键松开 x:%d y: %d\n", LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_MOUSEMOVE:
printf("鼠标移动消息 x:%d y: %d\n", LOWORD(lParam), HIWORD(lParam));
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

鼠标双击消息

当鼠标左键或右键或中键双击时,并且创建窗口时使用CS_DBCLICK风格,才会产生产生鼠标双击消息。很多小伙伴可能疑惑鼠标中键在哪。实际上鼠标的那个滚轮是可以按下去的,那个就是鼠标中键了。并不是鼠标双击消息产生时,就不会产生鼠标单击消息。

以鼠标左键双击消息(WM_LBUTTONDBLCLK),产生消息的顺序是:

WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP

消息产生均附带的两个信息

  • wParam 产生消息时,其他按键的情况(例如Ctrl,Shift键有没有按下)

  • lParam 产生消息时,低字节为鼠标的x坐标,高字节为鼠标的y坐标,是相对窗口而言的

键盘消息

当键盘的案件被按下或松开时,产生键盘事件。如WM_KEYDOWN 消息WM_KEYUP 消息等。

消息产生均附带的两个信息

  • wParam 按下按键的VirtualKey(虚拟键代码)

  • lParam 按键的参数(如点击了几次)

按下按键只会表明按下的是哪个按键,例如在输入大写字母A和小写字母a时,都是按下了a键,产生WM_KEYDOWN或WM_KEYUP等时没法区分。如果要得到按下按键时的ASCII值,使用WM_CHAR 消息 更加方便。

自定义消息

有的时候,我们想有自定义消息,那么我们可以使用WM_USER宏。自定义消息的格式为WM_USER + x。

以下是消息编号的范围。

范围 含义
0 到 WM_USER –1 保留供系统使用的消息。
通过 0x7FFF 进行WM_USER 用于专用窗口类的整数消息。
通过0xBFFF WM_APP (0x8000) 可供应用程序使用的消息。
通过 0xFFFF 进行0xC000 应用程序使用的字符串消息。
大于 0xFFFF 系统保留。

示例代码:

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
#include <windows.h>
#include <iostream>

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
// 定义自己的消息
#define MY_MESSAGE WM_USER + 2

LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);


INT APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
static const TCHAR TITLE[] = TEXT("window");
static const TCHAR CLASSNAME[] = TEXT("MyStruct");

WNDCLASS wc{0};
wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH); // background color
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // window's icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // window's cursor
wc.hInstance = hInstance; // window's instance
wc.lpfnWndProc = WinProc; // window process callback
wc.lpszClassName = CLASSNAME; // name of the window class
wc.lpszMenuName = NULL; // name of the menu
wc.style = CS_HREDRAW | CS_VREDRAW; // window style

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, // optional window style
CLASSNAME, // window class
TITLE, // window title
WS_OVERLAPPEDWINDOW, //window style

// width and height
CW_USEDEFAULT, CW_USEDEFAULT,
// x y
CW_USEDEFAULT, CW_USEDEFAULT,

NULL, // parent window
NULL, // menu
hInstance, // instance handle
NULL // additional application data
);
if (hwnd == NULL)
{
return 0;
}

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

MSG msg{0};
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
{
// 在鼠标左键时发送消息处理,并获得处理结果
LRESULT nRet = SendMessage(hwnd, MY_MESSAGE, 0, 100);
if (nRet == 114514)
{
MessageBox(hwnd, TEXT("好臭的处理方式!"), TEXT("差评"), MB_ICONWARNING);
}
return 0;
}
// 处理自定义消息
case MY_MESSAGE:
MessageBox(hwnd, TEXT("处理我自己的消息!"), TEXT("MY_MESSAGE"), MB_OK);
return 114514; // 返回处理结果
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

运行此代码,应该在鼠标左键时弹出一个消息框显示”处理我自己的消息!”,然后显示”好臭的处理方式!”。

本期到此结束,下期看星光探索者探索使用计时器和菜单,图标,光标等资源文件