消息分为 队列消息(进入线程的消息队列) 和 非队列消息(不进入线程的消息队列)。
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE、WM_CHAR、WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统负责把消息加入到相应线程的消息队列中,于是就有了消息循环(从消息队列中读取并派送消息)。
还有一种是非队列消息,他绕过系统消息队列,直接将消息发送到窗口过程。例如,当用户激活一个窗口系统发送WM_ACTIVATE、WM_SETFOCUS、WM_SETCURSOR,创建窗口时发送WM_CREATE消息。
//MSG 结构体
typedef struct tagMSG {
HWND hwnd;
UINT message; //消息ID
WPARAM wParam; //标识按下或释放的键码
LPARAM lParam; //含有对了解按键非常有用的其他信息
DWORD time; //时间(消息投递到消息队列的时间)
POINT pt; //光标在屏幕上的当前位置
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
事件 ——>封装消息(MSG结构体)——>传递消息到队列 ——>消息响应(消息处理函数)
( 消息队列 ——>GetMessage() ——>DispatchMessage() ——>winProc )
例:GetMessage(&msg, NULL, 0, 0)
原型:
BOOL GetMessage(
LPMSG lpMsg, //MSG结构体的消息指针
HWND hWnd, //要检索消息的窗口的句柄,窗口必须属于调用线程
UINT wMsgFilterMin, //指定要检索的最小消息值的整数值
UINT wMsgFilterMax //指定要检索的最大消息值的整数值
);
其中如果第二个参数取值NULL:
代表GetMessage检索属于调用线程的任何窗口的消息和使用PostThreadMessage函数发布到调用线程的线程消息。
第三个参数:使用WM_KEYFIRST指定第一个键盘消息或WM_MOUSEFIRST指定第一个鼠标消息。
第四个参数:使用WM_KEYLAST指定最后的键盘消息或WM_MOUSEFIRST指定最后的鼠标消息。
过程:
当前消息队列 ——>查看窗口是否重绘(WM_PAINT)
——>查看定时器(WM_TIMER)
——>优化内存,优化资源
——>等待
返回值:
如果函数检索到的消息不是WM_QUIT消息,则返回非0值。
如果函数检索到了WM_QUIT消息,则返回0值。
如果发生错误,返回值为-1。例如,参数hWnd是一个无效的窗口句柄或lpMsg是一个无效的指针。要获得更多的错误信息可以调用GetLastError函数。
警告:
因为函数的返回值可以为非0,0,-1,所以应该避免如下代码:
while(GetMessage( lpMsg, hWnd, 0, 0) ......)
当返回值为-1时,可能导致程序致命的错误。
另外,如果这样写的话:
while(GetMessage( lpMsg, hWnd, 0, 0) != 0 && GetMessage( lpMsg, hWnd, 0, 0) != -1)
相当于调用了两次GetMessage()函数,获取了两次msg消息,却只在while的循环里只处理了一次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);
}
}
TIPS:
程序通常使用返回值来确定是否应该结束主消息循环并退出程序。
GetMessage函数检索由hWnd参数标识的关联窗体,或者由IsChild函数指定的它们子窗体的消息,并且消息范围在wMsgFilterMin和wMsgFilterMax参数指定值之间的消息。
注意,不管在wMsgFilterMin和wMsgFilterMax指定的范围如何,GetMassage函数总是检索会WM_QUIT消息。
在函数调用期间,系统用SendMessage、SendMessageCallback,、SendMessageTimeout或者SendNotifyMessage函数发送待处理的、非队列的消息给属于当前线程的窗口。系统也可能处理内部事件。消息的处理顺序如下:
Sentmessages(发送消息)
Postedmessages(投递消息)
输入(硬件)消息或者系统内部事件
Sentmessages (again)(发送消息)
WM_PAINT消息
WM_TIMER消息
在投递消息之前检索输入消息,可以利用wMsgFilterMin和wMsgFilterMax参数做检索设置。
GetMessage不会从消息队列中移除WM_PAINT消息。该消息将一直存在于消息队列中直到被处理。
例:PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
原型:
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg //指定消息的处理方式
);
前四项参数都与GetMessage()一样,最后一项参数有以下几种取值:
PM_NOREMOVE:消息经PeekMessage处理后并不从消息队列中移除。
PM_REMOVE:消息经PeekMessage处理后从消息队列中移除。
(PS:可以有选择性的组合PM_NOYIELD 和 PM_NOREMOVE | | PM_NOYIELD 和 PM_REMOVE。PM_NOYIELD标志将阻止系统释放等待调用程序空闲的线程。)
PM_QS_INPUT:Windows 98/Me,Windows 2000/XP: 处理鼠标和键盘消息。
PM_QS_PAINT:Windows 98/Me,Windows 2000/XP:处理绘图消息。
PM_QS_POSTMESSAGE:Windows 98/Me,Windows 2000/XP:处理所有投递消息,包括定时器和热键消息。
PM_QS_SENDMESSAGE:Windows 98/Me,Windows 2000/XP: 处理所有发送消息。
过程:同GetMessage()
返回值:
如果有可用的消息,则返回值为非0;
如果没有可用的消息,则返回值为0;
TIPS:
同GetMessage(),另:但是,如果WM_PAINT消息的更新区域为空,则PeekMessage会将其从消息队列中移除。
例:TranslateMessage(&msg)
原型:
BOOL TranslateMessage(
CONST MSG *lpMsg
);
返回值:将键盘消息转换为字符消息,如果字符消息被发送到线程的消息队列,则返回值为非0。
如果消息是WMYKEYDOWN、WMYKEYUP、WMYSysKEYDOWN或WMY SysKyUp,则返回值为非零,而不考虑转换。
如果消息未被转换(即,字符消息未被发送到线程的消息队列),则返回值为0。
例:DispatchMessage(&msg)
原型:
LRESULT DispatchMessage(
CONST MSG *lpMsg
);
功能:分发一个消息给回调函数。
返回值:
返回值指定窗口过程返回的值。虽然它的含义取决于正在发送的消息,但返回值通常被忽略。
( LRESULT:指的是从窗口程序或者回调函数返回的32位值,LRESULT = LONG + RESULT也就是长整型。)
例:SendMessage(hWnd, WM_CLOSE, 0, 0)
原型:
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
功能:发送一个指定的消息到一个窗口。
返回值:
返回值指定窗口过程返回的值,它的含义取决于正在发送的消息。
例:PostMessage(hWnd, WM_CLOSE, 0, 0)
原型:
BOOL PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。如果为NULL,此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样
功能:该函数将一个消息放入(寄送)到与指定窗口创建的线程相联系消息队列里,不等待线程处理消息就返回,是异步消息模式。
返回值:如果调用成功,则返回非0值,否则返回0。