首页 > 代码库 > PeekMessage的一点事

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(), 100, 100, 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指定的过滤消息段是100-100,而CM_SEND2定义的不是100,但是也会处理send消息。

PeekMessage的返回值与获取的MSG取决于再点完按钮之后,用户的操作,和send消息队列无关。此处返回的msgid是15表示WM_PAINT消息。

 

参考资料:

1、 Jeffrey Richter的《Programming Applications for Microsoft Windows》第四版第26章

2、MSDN#PeekMessage