WTL CIdleHandler的正确用法是什么?

时间:2017-09-19 22:49:17

标签: c++ windows winapi atl wtl

我正在尝试学习WTL / Win32编程,而且我不太了解CIdleHandler mixin类的设计。

对于WTL 9.1,CMessageLoop代码如下(来自atlapp.h):

for(;;)
    {
        while(bDoIdle && !::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if(!OnIdle(nIdleCount++))
                bDoIdle = FALSE;
        }

        bRet = ::GetMessage(&m_msg, NULL, 0, 0);

        if(bRet == -1)
        {
            ATLTRACE2(atlTraceUI, 0, _T("::GetMessage returned -1 (error)\n"));
            continue;   // error, don't process
        }
        else if(!bRet)
        {
            ATLTRACE2(atlTraceUI, 0, _T("CMessageLoop::Run - exiting\n"));
            break;   // WM_QUIT, exit message loop
        }

        if(!PreTranslateMessage(&m_msg))
        {
            ::TranslateMessage(&m_msg);
            ::DispatchMessage(&m_msg);
        }

        if(IsIdleMessage(&m_msg))
        {
            bDoIdle = TRUE;
            nIdleCount = 0;
        }
    }

对空闲处理程序的实际调用非常简单。

// override to change idle processing
virtual BOOL OnIdle(int /*nIdleCount*/)
{
    for(int i = 0; i < m_aIdleHandler.GetSize(); i++)
    {
        CIdleHandler* pIdleHandler = m_aIdleHandler[i];
        if(pIdleHandler != NULL)
            pIdleHandler->OnIdle();
    }
    return FALSE;   // don't continue
}

调用IsIdleMessage

static BOOL IsIdleMessage(MSG* pMsg)
{
    // These messages should NOT cause idle processing
    switch(pMsg->message)
    {
    case WM_MOUSEMOVE:
#ifndef _WIN32_WCE
    case WM_NCMOUSEMOVE:
#endif // !_WIN32_WCE
    case WM_PAINT:
    case 0x0118:    // WM_SYSTIMER (caret blink)
        return FALSE;
    }

    return TRUE;
}

我的分析如下:似乎曾经每次&#34; PeekMessage Drought&#34; (没有消息发送到Win32应用程序的时间段),将调用OnIdle处理程序。

但为什么只有一次呢?在PeekMessage的情况下,您是不是希望不断地一次又一次地调用后台空闲任务?此外,对我来说,WM_LBUTTONDOWN(用户在窗口上左键单击某些内容)激活空闲处理(bDoIdle = True)似乎很奇怪,但是WM_MOUSEMOVE被明确调出以防止重新激活空闲处理。

任何人都可以给我&#34;正确的&#34;使用WTL空闲循环的场景(或更具体地说:CIdleHandler)?我想我的期望是空闲处理功能是小型的增量任务,只需要100毫秒即可完成。然后他们会在后台反复调用。

但似乎在WTL中并非如此。或者我可能没有完全理解空闲循环?因为如果我有一个增量后台任务注册为CIdleHandler ...那么如果用户离开窗口,任务将只运行一次!如果没有任何消息进入系统(例如WM_LBUTTONDOWN),bDoIdle变量将一直保持为假!

有没有人对这一切有一个很好的解释?

1 个答案:

答案 0 :(得分:2)

正如评论中所说,OnIdle处理程序应该在特定活动之后开始空闲时调用,尤其是为了更新UI。这解释了“一次”调用处理程序:发生了一些事情,然后您有机会更新UI元素。如果您需要持续进行后台处理,则应该使用计时器或工作线程。

WTL样本建议使用空闲处理程序,例如在\Samples\Alpha\mainfrm.h

Window类获取线程的消息循环并请求空闲更新:

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    // ...

    // register object for message filtering and idle updates
    CMessageLoop* pLoop = _Module.GetMessageLoop();
    ATLASSERT(pLoop != NULL);
    pLoop->AddMessageFilter(this);
    pLoop->AddIdleHandler(this);

稍后在消息处理和用户交互之后,空闲处理程序更新工具栏以反映可能的状态更改:

virtual BOOL OnIdle()
{
    UIUpdateToolBar();
    return FALSE;
}