消息机制
过程驱动:程序是按照我们预先定义好的顺序执行,每执行一步,下一步都已经按照预定的顺序继续执行,直到程序结束。
DOS程序是过程驱动程序,程序按照实现编写好的指令一步一步顺序执行
事件驱动:程序的执行顺序是无序的。某个时间点所执行的代码,是由外界通知。由于我们无法决定用户执行顺序,所以代码的执行也是无序。如点击一个按钮,会发送一个按钮点击时间的消息,程序就会对此消息进行处理
Win32的消息机制:事件驱动。
消息类型
MSG结构体
windows程序中,消息由MSG结构体表示。定义如下
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
成员
hwnd
类型:HWND
其窗口过程接收消息的窗口的句柄。 当消息是线程消息时,此成员为 NULL 。
message
类型: UINT
消息的标识符。 应用程序只能使用低字;高字由系统保留。
wParam
类型: WPARAM
关于消息的附加信息。 确切含义取决于 消息 成员的值。
lParam
类型: LPARAM
关于消息的附加信息。 确切含义取决于 消息 成员的值。
time
类型:DWORD
消息的发布时间。
pt
类型: POINT
发布消息时的光标位置(以屏幕坐标表示)。
lPrivate
常见的消息类型
WM_TIMER
在计时器过期时发布到安装线程的消息队列。
#define WM_TIMER 0x0113
参数
wParam [in]
计时器标识符。
lParam [in]
指向安装计时器时传递给 SetTimer 函数的应用程序定义的回调函数的指针。
返回值
类型: LRESULT
如果应用程序处理此消息,则应用程序应返回零。
WM_CREATE
当应用程序请求通过调用 CreateWindowEx 或 CreateWindow 函数创建窗口时发送。 (函数返回之前发送消息。) 新窗口的窗口过程在创建窗口后接收此消息,但在窗口变为可见之前。
#define WM_CREATE 0x0001
参数
wParam
未使用此参数。
lParam
指向 CREATESTRUCT 结构的指针,其中包含有关正在创建的窗口的信息。
返回值
类型: LRESULT
如果应用程序处理此消息,它应返回零以继续创建窗口。 如果应用程序返回 –1,则窗口将被销毁, CreateWindowEx 或 CreateWindow 函数返回 NULL 句柄。
WM_SIZE
更改窗口大小后发送到窗口。窗口通过其 WindowProc 函数接收此消息。
#define WM_SIZE 0x0005
参数
wParam
请求的大小调整的类型。 此参数的取值可为下列值之一:
值 含义
SIZE_MAXHIDE
4
当其他一些窗口最大化时,消息将发送到所有弹出窗口。
SIZE_MAXIMIZED
2
窗口已最大化。
SIZE_MAXSHOW
3
当其他一些窗口还原到其以前的大小时,消息将发送到所有弹出窗口。
SIZE_MINIMIZED
1
窗口已最小化。
SIZE_RESTORED
0
窗口已调整大小,但 SIZE_MINIMIZED 和 SIZE_MAXIMIZED 值均未应用。
lParam
lParam 的低序字指定工作区的新宽度。
lParam 的高序字指定工作区的新高度。
返回值
类型: LRESULT
如果应用程序处理此消息,它应返回零。
WM_CLOSE
发送为窗口或应用程序应终止的信号。窗口通过其 WindowProc 函数接收此消息。
WM_SYSCOMMAND
当用户从 “窗口 ”菜单中选择命令时,窗口会收到此消息, (以前称为系统或控件菜单) ,或者当用户选择最大化按钮、最小化按钮、还原按钮或关闭按钮时。
#define WM_SYSCOMMAND 0x0112
参数
wParam
请求的系统命令的类型。 此参数的取值可为下列值之一:
值 含义
SC_CLOSE
0xF060
关闭窗口。
SC_CONTEXTHELP
0xF180
使用指针将光标更改为问号。 如果用户随后单击对话框中的控件,该控件将收到 WM_HELP 消息。
SC_DEFAULT
0xF160
选择默认项;用户双击窗口菜单。
SC_HOTKEY
0xF150
激活与应用程序指定的热键关联的窗口。 lParam 参数标识要激活的窗口。
SC_HSCROLL
0xF080
水平滚动。
SCF_ISSECURE
0x00000001
指示屏幕保存程序是否安全。
SC_KEYMENU
0xF100
检索窗口菜单作为击键的结果。 有关详细信息,请参见“备注”部分。
SC_MAXIMIZE
0xF030
最大化窗口。
SC_MINIMIZE
0xF020
最小化窗口。
SC_MONITORPOWER
0xF170
设置显示的状态。 此命令支持具有节能功能的设备,例如电池供电的个人电脑。
lParam 参数可以具有以下值:
-1 (显示器开机)
1 (显示器将低功率)
2 (显示器正在关闭)
SC_MOUSEMENU
0xF090
在单击鼠标后检索窗口菜单。
SC_MOVE
0xF010
移动窗口。
SC_NEXTWINDOW
0xF040
移动到下一个窗口。
SC_PREVWINDOW
0xF050
移动到上一个窗口。
SC_RESTORE
0xF120
将窗口还原到其正常位置和大小。
SC_SCREENSAVE
0xF140
执行在System.ini文件的 [boot] 节中指定的屏幕保存程序应用程序。
SC_SIZE
0xF000
调整窗口的大小。
SC_TASKLIST
0xF130
激活"开始"菜单菜单。
SC_VSCROLL
0xF070
垂直滚动。
lParam
低序单词指定光标的水平位置(以屏幕坐标为单位)(如果使用鼠标选择窗口菜单命令)。 否则,不使用此参数。
高序单词指定光标的垂直位置(以屏幕坐标为单位)(如果使用鼠标选择窗口菜单命令)。 如果使用系统加速器选择命令,则此参数为 1;如果使用助记键,则为 1。
返回值
如果应用程序处理此消息,则应用程序应返回零。
发送消息
SendMessage函数调用指定窗口的窗口过程,在窗口过程处理消息之前不会返回
LRESULT SendMessageA(
[in] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
参数
[in] hWnd
类型:HWND
窗口过程将接收消息的窗口句柄。 如果此参数 HWND_BROADCAST ( (HWND) 0xffff) ,则会将消息发送到系统中的所有顶级窗口,包括已禁用或不可见的未所有者窗口、重叠窗口和弹出窗口;但消息不会发送到子窗口。
消息发送受 UIPI 的约束。 进程的线程只能将消息发送到进程较小或等于完整性级别的线程的消息队列。
[in] Msg
类型: UINT
要发送的消息。
有关系统提供的消息的列表,请参阅 系统定义的消息。
[in] wParam
类型: WPARAM
其他的消息特定信息。
[in] lParam
类型: LPARAM
其他的消息特定信息。
返回值
类型: LRESULT
返回值指定消息处理的结果;这取决于发送的消息。
PostMessage将信息放入消息队列中,不等待处理消息的情况下立即返回
BOOL PostMessageA(
[in, optional] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
参数
[in, optional] hWnd
类型:HWND
窗口过程接收消息的窗口的句柄。 以下值具有特殊含义。
Value 含义
HWND_BROADCAST
( (HWND) 0xffff)
消息将发布到系统中的所有顶级窗口,包括已禁用或不可见的未拥有窗口、重叠的窗口和弹出窗口。 消息不会发布到子窗口。
Null
该函数的行为类似于对 PostThreadMessage 的调用, 其中 dwThreadId 参数设置为当前线程的标识符。
从 Windows Vista 开始,消息发布受 UIPI 的约束。 进程的线程只能将消息发布到小于或等于完整性级别的进程中线程的消息队列。
[in] Msg
类型: UINT
要发布的消息。
有关系统提供的消息的列表,请参阅 系统定义的消息。
[in] wParam
类型: WPARAM
其他的消息特定信息。
[in] lParam
类型: LPARAM
其他的消息特定信息。
返回值
类型: BOOL
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
接收消息
GetMessage 获取消息,阻塞函数
BOOL GetMessage(
[out] LPMSG lpMsg,
[in, optional] HWND hWnd,
[in] UINT wMsgFilterMin,
[in] UINT wMsgFilterMax
);
PeekMessage 获取消息,非阻塞函数,如果获取不到消息就直接向下执行,直到结束
BOOL PeekMessageA(
[out] LPMSG lpMsg,
[in, optional] HWND hWnd,
[in] UINT wMsgFilterMin,
[in] UINT wMsgFilterMax,
[in] UINT wRemoveMsg
);
消息循环
Windows程序开始执行后,Windows为该程序创建一个“消息队列”。这个消息队列用来存放该程序可能创建的各种不同窗口的消息。程序中有一段代码,叫做“消息循环”, 它用来从队列中取出消息,并且将它们发送给相应的窗口过程。在没有消息发生的时候,你的程序实际上就在消息循环中转圈子。
涉及的函数:
TranslateMessage
将virtual-key消息转换为字符消息。 字符消息将发布到调用线程的消息队列,下次线程调用 GetMessage 或 PeekMessage 函数时要读取。
DispatchMessage
将消息调度到窗口过程。 它通常用于调度 GetMessage 函数检索的消息。
消息循环实现
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
在接收到WM_QUIT消息之前GetMessage返回非0值,在主函数结尾处添加消息循环,程序就会一直循环处理窗体消息
消息处理
对于程序创建的窗口,编写窗体过程函数来处理消息
窗体过程函数形式,使用switch判断接收的消息类型,并且执行对应的处理
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:窗体创建消息
printf("aa\n");//打印字符"aa"
return 0;
case WM_DESTROY://窗体销毁消息
PostQuitMessage(0);
return 0;
case WM_PAINT://窗体重绘消息
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
对于系统预定义窗体类,如进度条控件,有自己的消息处理函数。无需编写窗体过程函数。如向进度条控件发送消息PBM_SETRANGE,设置进度条范围
进度条g_hProgress1会处理该消息,并且设置成进度范围1-100
用户自定义消息
用户可自定义消息。消息符号本质上是宏定义的整数类型
其中0到WM_USER-1是系统保留消息,为了避免与系统消息冲突,应用程序可以定义 (WM_USER到 0x7FFF) 的消息编号
定义消息类型
或者利用枚举类型,成员递增1的特性,定义消息
发送自定义消息、参数,获取返回值
printf("send custom message!\n",i);
int i = SendMessage(hwnd, MyMsg0,1,2);
printf("return value:%d\n",i);
自定义消息处理,接收消息参数、返回数值
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case MyMsg0://自定义消息类型
printf("message parameter:%ld %ld\n",wParam,lParam);//打印消息参数
return 11;//返回值 11
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
运行结果,在控制台出现字符
send custom message!
message received!message parameter:1 2
return value:11