PeekMessage的一点事
PeekMessage函数:
先看看MSDN怎么说:
PeekMessage function
Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
BOOL WINAPI PeekMessage(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax,
_In_ UINT wRemoveMsg
);
更多信息参见MSDN。
PeekMessage的Dispatches incoming sent messages行为:
MSDN说的很清楚了,PeekMessage会首先处理send消息队列,然后检查post消息队列的消息,最后如果存在消息的时候,获取消息到lpMsg。
从MSDN中得到的信息结合实验得出的小结论是:
1、 PeekMessage会首先处理send消息队列。
a) 消息队列分send消息队列和post消息队列,不管设定的wMsgFilterMin、wMsgFilterMax和wRemoveMsg,send消息都会得到处理。
b) wMsgFilterMin、wMsgFilterMax和wRemoveMsg对post消息队列有效。
c) PeekMessage内部会循环处理send消息队列直至该队列为空。
2、PeekMessage处理完send消息队列后,接着处理post消息队列。
send消息处理的实验设计及结果:
实验设计:
创建一个MFC对话框程序,带一个按钮。
1、 在按钮点击消息响应函数OnBnClickedBtnPeekmsg()中创建3个辅助线程,每个线程给窗口send一个自定义消息。由于主线程正在处理按钮点击消息,所以send消息会被放到send消息队列,正常情况下是等待OnBnClickedBtnPeekmsg()完成后得到处理。
2、 OnBnClickedBtnPeekmsg()中Sleep 2秒,已等待辅助线程完全创建成功。
3、 OnBnClickedBtnPeekmsg()中调用PeekMessage。
4、 自定义消息的处理函数OnMySendMessage2里面Sleep个10秒。
5、 在各个处理部分加上日志。
实验代码:
DWORD WINAPI ThreadProc2(LPVOID lpParam) { HWND hWnd = (HWND)(lpParam); ASSERT(hWnd); ::SendMessage(hWnd, CM_SEND2, 0, 0); return 0; } LRESULT CTestSendMessageDlg::OnMySendMessage2(WPARAM wParam, LPARAM lParam) { CString strLog; strLog.Format(_T("###OnMySendMessage2: begin.\n")); Log(strLog); ::Sleep(10 * 1000); strLog.Format(_T("###OnMySendMessage2: end.\n")); Log(strLog); return 0L; } void CTestSendMessageDlg::OnBnClickedBtnPeekmsg() { /* 验证目标: 1、PeekMessage会先处理SendMessage的消息。 不管设定的wMsgFilterMin、wMsgFilterMax和wRemoveMsg,Send消息都会得到处理 验证步骤: 1、主线程创建个辅助线程,辅助线程Send一个消息,在Send消息内等待s 2、主线程等待s后,调用一次PeekMessage,输出日志 */ CString strLog; DWORD dwThreadID = 0; HANDLE hThread = NULL; for (int i = 0; i < 3; i++) { hThread = ::CreateThread(NULL, 0, ThreadProc2, GetSafeHwnd(), 0, &dwThreadID); ::CloseHandle(hThread); strLog.Format(_T("###PeekMessage: thread 0x%x is created.\n"), dwThreadID); Log(strLog); } ::Sleep(2000); strLog.Format(_T("###PeekMessage: begin.\n")); Log(strLog); MSG msg; // 不管设定的wMsgFilterMin、wMsgFilterMax和wRemoveMsg,Send消息都会得到处理 BOOL bRet = PeekMessage(&msg, GetSafeHwnd(), 0, WM_USER, PM_NOREMOVE); if (bRet) { strLog.Format(_T("###PeekMessage: msg[id=%d, w=%d, l=%d].\n"), msg.message, msg.wParam, msg.lParam); Log(strLog); } else { strLog.Format(_T("###PeekMessage: return false.\n")); Log(strLog); } strLog.Format(_T("###PeekMessage: end.\n")); Log(strLog); }
结果:
09:12:20.843 [t:2864h] ###PeekMessage: thread 0x2c00 is created.
09:12:20.843 [t:2864h] ###PeekMessage: thread 0x2aa8 is created.
09:12:20.843 [t:2864h] ###PeekMessage: thread 0x2b8c is created.
09:12:22.844 [t:2864h] ###PeekMessage: begin.
09:12:22.844 [t:2864h] ###OnMySendMessage2: begin.
09:12:32.845 [t:2864h] ###OnMySendMessage2: end.
09:12:32.845 [t:2864h] ###OnMySendMessage2: begin.
线程 'Win32 线程' (0x2c00) 已退出,返回值为 0 (0x0)。
09:12:42.847 [t:2864h] ###OnMySendMessage2: end.
09:12:42.847 [t:2864h] ###OnMySendMessage2: begin.
线程 'Win32 线程' (0x2b8c) 已退出,返回值为 0 (0x0)。
09:12:52.848 [t:2864h] ###OnMySendMessage2: end.
线程 'Win32 线程' (0x2aa8) 已退出,返回值为 0 (0x0)。
09:12:52.855 [t:2864h] ###PeekMessage: msg[id=15, w=0, l=0].
09:12:52.856 [t:2864h] ###PeekMessage: end.
从结果可以看出,在PeekMessage内部会处理完线程发送的3个send消息然后返回。虽然PeekMessage指定的过滤消息段是0-WM_USER,而CM_SEND2定义是WM_USER+9529,但是也会处理send消息。
PeekMessage的返回值与获取的MSG取决于再点完按钮之后,用户的操作,和send消息队列无关。此处返回的msgid是15表示WM_PAINT消息。
参考资料:
1、 Jeffrey Richter的《Programming Applications for Microsoft Windows》第四版第26章
这是最好的时代,这是最坏的时代;这是智慧的年代,这是愚蠢的年代;这是信仰的时期,这是怀疑的时期;我们的前途拥有一切,我们的前途一无所有。