一 窗口和消息
1 WPARAM和LPARAM的意义
在Windows是一种16位系统时,WndProc的第三个参数被定义为WORD,是一个16位的无符号整数,而第四个参数被定义为一个LONG,是一个32位有符号整数,所以导致对单词PARAM(参数)加前缀W和L。
但在32位Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG,因此窗口过程的这两个参数都是32位的值。
2 前缀
前缀 |
全称 |
释义 |
CS |
class style |
类风格选项 |
CW |
create windows |
创建窗口选项 |
DT |
draw text |
绘制文本选项 |
IDI |
Icon ID |
图标ID号 |
IDC |
Cursor ID |
光标ID号 |
MB |
Message Box |
消息框选项 |
SND |
Sound |
声音选项 |
WM |
Windows Message |
窗口消息 |
WS |
Windows Style |
窗口风格 |
3 新的函数类型
WndProc函数返回一个类型为LRESULT的值,该值是一个LONG型,32位有符号。
WndProc函数被指定为CALLBACK类型,WinMain函数被指定为WINAPI类型。这些类型指在Windows本身和用户的应用程序之间发生的函数调用的特殊调用序列。
4 窗口类结构WNDCLASS
有10个域。分别是:
① style:类风格,用于在什么时候发出窗口变化消息
② cbClsExtra:在类结构保存的窗口结构中预留一些额外空间
③ cbWndExtra:在Windows内部保存的窗口结构中预留一些额外空间
④ hbrBackground:指定基于这个类创建的窗口背景颜色
⑤ hCursor:读取光标
⑥ hIcon:读取图标
⑦ hInstance:程序的实例句柄
⑧ lpfnWndProc:指定处理基于这个窗口类创建的所有窗口的窗口过程
⑨ lpszClassName:指定类名
⑩ lpszMenuName:指定窗口类菜单
5 注册窗口类RegisterClass
一般在Windows XP及以后都可以很顺利的注册成功。
所以可以只写RegisterClass(&wndclass);
6 创建窗口CreateWindow
窗口类定义了窗口的一般特征,调用CreateWindow可以指定有关窗口的更详细的信息。
hwnd = CreateWindow (szAppName, // 指定一个窗口类,基于该窗口类创建窗口
TEXT ("Hello Win"), // 这个字符串会出现在标题栏中
WS_OVERLAPPEDWINDOW, // 本窗口风格
CW_USEDEFAULT, //窗口左上角的X坐标
CW_USEDEFAULT, //窗口左上角的Y坐标
CW_USEDEFAULT, //窗口的宽度
CW_USEDEFAULT, //窗口的高度
NULL, //窗口对象的父窗口句柄
NULL, //窗口对象的菜单句柄或者子窗口编号
hInstance, //当前进程的实例句柄
NULL) ; //窗口对象的参数指针句柄
创建窗口返回的是窗口句柄。
7 显示窗口
窗口创建成功后,系统将在内存中为其分配一块内存,但是此时窗口并未显示在显示器上,所以需要使用两个调用。
① ShowWindow(窗口句柄,iCmdShow);
其中的第二个参数用于确定如何儿子屏幕上显示窗口,是最小化还是常规还是最大化。
② UpdateWindow(窗口句柄)
调用上句将导致客户区被绘制。它通过给窗口过程发送一个WM_PAINT消息来做到这一点。
8 消息循环
调用UpdateWindow之后,窗口就出现在显示器上。
Windows为当前运行的每个Windows程序维护一个“消息队列”。在发生事件的时候,Windows将事件转换为一个“消息”,并将消息放入程序的消息队列中。
程序通过执行一个叫做“消息循环”的代码从消息队列中取出消息。
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
其中msg是一个类型为MSG的结构,MSG结构在WINUSER.H中定义如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG
其中,hwnd 是消息发向的窗口句柄。
message 是消息标识符,以WM_开头。
wParam 一个32位的消息参数。
lParam 一个32位的消息参数。
time 消息放入消息队列的时间。
pt 消息放入消息队列时鼠标的坐标。
消息循环以GetMessage调用开始,它从消息队列中取出一个消息,GetMessage(&msg, NULL, 0, 0)这一调用传给Windows一个指向名为msg的MSG结构的指针。其余三个参数设置为NULL或0,表示程序接收自己创建的所有窗口的所有消息。
只要从消息队列中取出的消息的message域不为WM_QUIT,GetMessage就返回一个非0值,while循环就可以继续。
TranslateMessage(&msg);将msg结构传给Windows,进行一些键盘转换。
DispatchMessage(&msg);将msg结构传给Windows,然后Windows将里面的消息发给相应的窗口过程进行处理。处理后,WndProc返回到Windows,Windows返回到程序,程序继续下一个while循环。
9 窗口过程WndProc
实际的动作发生在窗口过程中。窗口过程确定了在窗口的客户区显示什么,以及怎么处理用户输入。
窗口过程是命名为WndProc的函数。(也可以其他不冲突的名字)
一个Windows程序可以包含多个窗口过程。
一个窗口过程总是与调用RegisterClass注册的特定窗口类相关联。
CreateWindow函数根据特定的窗口类创建一个窗口,返回该窗口的句柄。
但是基于一个窗口类可以创建多个窗口。
1程序 包括 n个窗口过程
1窗口过程 关联 1窗口类
1窗口类 创建 n个窗口
CreateWindows根据窗口类创建一个窗口。
窗口过程总是声明成如下形式:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
这4个参数跟MSG结构的前4个域是相同的。
第一个参数:接收消息的窗口句柄。
第二个参数:消息类型,标识消息的数字
最后两个参数:32位的消息参数。
程序通常不直接调用窗口过程,而是由Windows调用窗口过程。
10 窗口过程处理消息
窗口过程接收的每个消息均是用一个数值来标识的,也就是传给窗口过程的message参数。
窗口过程处理消息时,必须返回0.
窗口过程不予处理的所有消息应该被传给名为DefWindowsProc的Windows函数。
用switch语句来处理不同消息,消息以WM_开头。
11 WM_CREATE消息
当Windows在处理CreateWindow函数时,窗口过程就会接收到WM_CREATE消息。
通常,窗口过程在WM_CREATE处理期间进行一次窗口初始化。
12 WM_PAINT消息
当窗口客户区域的一部分或全部变成“无效”时,必须进行刷新,WM_PAINT将通知程序。
在最初创建窗口的时候,整个客户区都是无效的,因为程序还没有在窗口上画任何东西。在调用UpdateWindow时,通常会触发第一个WM_PAINT消息,指示窗口过程在客户区域上画一些东西。
在改变程序窗口大小后,客户区也会变得无效,至于怎么变得无效由CS_引导的类风格选项确定。
对WM_PAINT的处理几乎总是从一个BeginPaint调用开始,以一个EndPaint结束。
hdc = BeginPaint(hwnd, &pt);
do something;(如GetClientRect(hwnd, &rect);)
EndPaint(hwnd, &pt);
hwnd是要刷新的窗口的窗口句柄。
pt是指向类型为PAINTSTRUCT的结构指针。
在BeginPaint调用中,如果客户区域的背景还未被擦除,就由Windows来擦除。然后使用注册窗口类的WNDCLASS结构中的hbrBackground域中第一的刷子来删除背景。
BeginPaint调用使整个客户区有效,并返回一个“设备描述表句柄”。设备描述表是指物理输出设备及其驱动程序。可以利用该“设备描述表句柄”在客户区域显示文本和图形。
EndPaint调用释放设备描述表句柄。
GetClientRect(hwnd, &rect);
第一个参数:程序的窗口句柄;
第二个参数:指向RECT类型的rectangle结构。该结构有4个LONG域,标识客户区域的尺寸。
当改变窗口大小时,WndProc通过调用GetClientRect来获取变化后的窗口大小,重新绘制客户区。
13 WM_DESTROY消息
当用户点击关闭按钮时发生。
程序可以通过调用PostQuitMessage以标准方式响应WM_DESTROY消息;
PostQuitMessage(0);
该函数在程序的消息队列插入一个WM_QUIT消息。
GetMessage对于除了WM_QUIT消息之外的从消息队列中取出的所有消息都返回非0值。而当GetMessage取到一个WM_QUIT消息时,返回0.
14 关闭程序时的消息传递
① 用户点击关闭按钮
② 产生WM_SYSCOMMAND消息;
③ 产生WM_CLOSE消息响应WM_SYSCOMMAND;
④ 产生WM_DESTROY消息响应WM_CLOSE;
⑤ 产生WM_QUIT消息响应WM_DESTROY。
15 进队消息和不进队消息
消息能够分为:进队消息和不进队消息。
进队消息是由Windows放入程序消息队列中的。在程序的消息循环中,重新返回并分配给窗口过程。
不进队消息在Windows调用窗口时直接发送给窗口过程。
也就是说,进队消息发送给消息队列,不进队消息发送给窗口过程。
在任何情况下,窗口过程都将获得窗口所有的消息。窗口过程是窗口的消息中心。
进队消息基本上是用户输入的结果,还包括时钟消息、刷新消息、退出消息。
不进队消息基本上是来自调用特定的Windows函数。