什么是消息队列
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。
目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列。
系统V消息队列是随内核持续的,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构(struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中找到访问入口。 消息队列就是一个消息的链表。每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该消息队列的大量信息,包括消息队列键值、用户ID、组ID、消息队列中消息数目等等,甚至记录了最近对消息队列读写进程的ID。读者可以访问这些信息,也可以设置其中的某些信息。
添加到消息队列
PostMessage()
PostMessage是Windows API中的一个常用函数,用于将一条消息放入到消息队列中。消息队列里的消息通过调用GetMessage和PeekMessage取得。
头文件:winuser.h;
输入库:user32.lib;
返回值:如果函数调用成功,返回非零,否则函数调用返回值为零
函数原型:
WINUSERAPI
BOOL
WINAPI
PostMessageA(
_In_opt_ HWND hWnd, //消息要发送到的窗口句柄。可取有特定含义的两个值:
//HWND_BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、 被覆盖的窗口和弹出式窗口。消息不被寄送到子窗口
//NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符函数一样
_In_ UINT Msg, //消息标识符
_In_ WPARAM wParam, //附加消息
_In_ LPARAM lParam);
WINUSERAPI
BOOL
WINAPI
PostMessageW(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
#ifdef UNICODE
#define PostMessage PostMessageW
#else
#define PostMessage PostMessageA
#endif // !UNICODE
SendMessage()
该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。
返回值:返回值指定消息处理的结果,依赖于所发送的消息。
函数原型:
WINUSERAPI
LRESULT
WINAPI
SendMessageA(
_In_ HWND hWnd, //其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口
_In_ UINT Msg, //指定被发送的消息
_Pre_maybenull_ _Post_valid_ WPARAM wParam,
_Pre_maybenull_ _Post_valid_ LPARAM lParam);
WINUSERAPI
LRESULT
WINAPI
SendMessageW(
_In_ HWND hWnd,
_In_ UINT Msg,
_Pre_maybenull_ _Post_valid_ WPARAM wParam,
_Pre_maybenull_ _Post_valid_ LPARAM lParam);
#ifdef UNICODE
#define SendMessage SendMessageW
#else
#define SendMessage SendMessageA
#endif // !UNICODE
两者的对比
函数SendMessage将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回(它不等消息处理完)。
从消息队列中获取消息
GetMesage()
GetMessage是从调用线程的消息队列里取得一个消息并将其放于指定的结构。此函数可取得与指定窗口联系的消息和由PostThreadMessage寄送的线程消息。此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。获取消息成功后,线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值。
返回值:如果函数取得WM_QUIT之外的其他消息,返回非零值。如果函数取得WM_QUIT消息,返回值是零。如果出现了错误,返回值是-1。例如,当hWnd是无效的窗口句柄或lpMsg是无效的指针时。若想获得更多的错误信息,请调用GetLastError函数。
函数原型:
WINUSERAPI
BOOL
WINAPI
GetMessageA(
_Out_ LPMSG lpMsg, //指向MSG结构的指针,该结构从线程的消息队列里接收消息信息
_In_opt_ HWND hWnd, //取得其消息的窗口的句柄。当其值取NULL时,GetMessage为任何属于调用线程的窗口检索消息,线程消息通过PostThreadMessage寄送给调用线程
_In_ UINT wMsgFilterMin, //指定被检索的最小消息值的整数
_In_ UINT wMsgFilterMax); //指定被检索的最大消息值的整数
WINUSERAPI
BOOL
WINAPI
GetMessageW(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax);
#ifdef UNICODE
#define GetMessage GetMessageW
#else
#define GetMessage GetMessageA
#endif // !UNICODE
PeekMessage()
PeekMessage是一个Windows API函数。该函数为一个消息检查线程消息队列,并将该消息(如果存在)放于指定的结构。
返回值:如果消息可得到,返回非零值;如果没有消息可得到,返回值是零
函数原型:
WINUSERAPI
BOOL
WINAPI
PeekMessageA(
_Out_ LPMSG lpMsg, //接收消息信息的MSG结构指针
_In_opt_ HWND hWnd, //其消息被检查的窗口句柄
_In_ UINT wMsgFilterMin, //指定被检查的消息范围里的第一个消息
_In_ UINT wMsgFilterMax, //指定被检查的消息范围里的最后一个消息
_In_ UINT wRemoveMsg); //确定消息如何被处理,取值可以见下图
WINUSERAPI
BOOL
WINAPI
PeekMessageW(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax,
_In_ UINT wRemoveMsg);
#ifdef UNICODE
#define PeekMessage PeekMessageW
#else
#define PeekMessage PeekMessageA
#endif // !UNICODE
![](https://img.haomeiwen.com/i9766278/96bf0664271f33b8.png)
两者的对比
不同点:GetMessage函数从系统获取消息,将消息从系统中移除,属于阻塞函数。当系统无消息时,GetMessage会等待下一条消息。而函数PeekMesssge是以查看的方式从系统中获取消息,可以不将消息从系统中移除,是非阻塞函数;当系统无消息时,返回FALSE,继续执行后续代码。
相同点:1、两个函数都只得到那些与参数hWnd标识的窗口相联系的消息或被lsChild确定为其子窗口相联系的消息,并且该消息要在由参数wMsgFiterMin和wMsgFiherMax确定的范围内。如果hWnd为NULL,则PeekMessage/GetMessage不接收属于其他线程的窗口的消息。如果hWnd为-1,PeekMessage/GetMessage只返回hWnd值为NULL的消息,该消息由函数PostThreadMessage寄送。如果wMsgFilterMin和wMsgFilterMax都为零,PeekMessage/GetMessage返回所有可得的消息(即,无范围过滤)。
2、两个函数通常不从队列里清除WM_PAINT消息。该消息将保留在队列里直到处理完毕。但如果WM_PAINT消息不指向无效区,PeekMessage/GetMessage将能够从队列里清除WM_PAINT消息。常数WM_KEYFIRST和WM_KEYLAST可作为过滤值取得所有键盘消息;常数WM_MOUSEFIRST和WM_MOUSELAST可用来接收所有的鼠标消息。
网友评论