freeloop:Win32编程点滴:消息循环(Message loop)

  第个版本

  首先让我们来写个最容易让人想到消息循环形式:

MSG msg
while( GetMessage(&msg,NULL,0,0) )
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}


  GetMessage个参数是用来获取MSG结构指针第 2个参数是个窗口句柄(HWND)用来获取指定窗口消息填NULL表示获取当前线程所有窗口消息或者线程消息(Thread message)最后两个参数是wMsgFilterMin和wMsgFilterMax用来获取指定消息当都填0则表示获取所有消息

  TranslateMessage根据WM_KEYUP,WM_KEYDOWN的类时间生成相应WM_CHAR的类消息

  DispatchMessage将窗口消息交给相应窗口过程(WindowProc)来处理

  以上这个消息循环在大部分情况下都能工作得很好尤其是般写点小写成上面形式完全没有问题不过偶尔可能会出些问题所以请继续往下看还有哪些改进余地

  第 2个版本

  如果我们仔细下MSDN上有关GetMessage介绍说明那么就可以看到MSDN指出了 while( GetMessage(...) ) 方式是并给出了如下形式:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
     (bRet  -1)
    {
        // handle the error and possibly exit
    }
    
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}


  原来GetMessage除了在收到WM_QUIT消息时候返回0的外在发生时候返回是-1在大部分情况下即使发生了msg也保存了上消息个消息处理了两次我想大部分人都不会察觉到吧不过如果我们想自己写个类似于MFC的类框架或者严于律己人来说这点健壮性还是不容忽略

  在继续下个版本改进以前现在模仿MFC或WTL个PreTranslateMessage:

BOOL PreTranslateMessage(LPMSG pMsg)
{
     FALSE;
}

BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
     (bRet  -1)
    {
         // handle the error and possibly exit
    }
      (!PreTranslaeMessage(&msg))
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}


  PreTranslateMessage作用是非常重要由于DispatchMessage是将消息分发给各个窗口过程(WindowProc)处理我觉得有3种情况要放在PreTranslateMessage里处理:

  全局性东西不适宜或不方便放在窗口过程中处理东西

  要处理某些第 3方或通用Control控件消息

  线程消息

  下面要讲东西都将放在PreTranslateMessage

  第 3个版本

  首先要放在PreTraslateMessage属于上面3中情况全局性东西快捷键

HACCEL hAcc = LoadAccelerator(hInst,MAKEINTRESOURCE(IDA_XXX));

BOOL PreTranslateMessage(LPMSG pMsg)
{
     (TranslateAccelerator(hWnd,hAcc,pMsg)) 
         TRUE;
     FALSE;
}


  这里要注意除了自己定义快捷键的外很多ActiveXControl控件都暴露出了有TranslateAccelerator接口如果没有在PreTranslateMessage中定会缺乏某些使人不方便行为比如tab键导航尤其是嵌入了webbrowserControl控件,如果想让用户舒适得使用这个内嵌浏览器定要下面类似代码:

IWebBrowser2 * m_pBrowser;

BOOL PreTranslateMessage(LPMSG pMsg)
{
    IOleInPlaceActiveObject * pObj;
     ( SUCCESSED(m_pBrowser->QueryInterface(IID_IOleInPlaceActiveObject,&pObj)) && 
    S_OK  pObj->TranslateAccelerator(pMsg))
    {
         TRUE;
    }
     FALSE;
}


  第 4个版本

  如果不查MSDN是否能立刻说出IsDialogMessage作用呢?IsDialogMessage并不仅仅是个IsXXX作用是:判断个消息是否为个对话框消息如果是就处理它所以代码应该如下:

BOOL PreTranslateMessage(LPMSG pMsg)
{
     (IsDialogMessage(hDlg,pMsg))
         TRUE;
     FALSE;
}


  IsDialogMessage是用来处理对话框上面Control控件键盘导航例如:当焦点在个按钮上面时候按下tab键这时应该将焦点设到下个Control控件上面而由于焦点在这个按钮上面所以只有这个按钮才收得到这个tab键键盘消息因此我们需要在消息循环中也就是PreTranslateMessage中IsDialogMessage来处理这样消息

  般而言上面hDlg参数个当前存在非模态窗口当然如MSDN所说如果个普通窗口上面Control控件需要使用键盘导航也可以IsDialogMessage来处理那么为什么上面指定是非模态窗口模态窗口不需要了吗?是模态窗口自带消息循环用不着我们自己消息循环

  第 5个版本?

  我想我暂时是想不出第 5个版本了即便是个简简单单消息循环也还有很多深层次东西可以挖掘我们平时用惯了MFC/ATL/WTL的类框架它们已经将消息循环封装很好了很多东西都已经自动处理了我想虽然有些东西我们不需要亲自处理但是还是需要对此有定了解

  最后就已WTL消息循环源代码结束这篇文章吧:

///////////////////////////////////////////////////////////////////////////////
// CMessageLoop - message loop implementation

 CMessageLoop
{
public:
    ATL::CSimpleArray<CMessageFilter*> m_aMsgFilter;
    ATL::CSimpleArray<CIdleHandler*> m_aIdleHandler;
    MSG m_msg;

// Message filter operations
    BOOL AddMessageFilter(CMessageFilter* pMessageFilter)
    {
         m_aMsgFilter.Add(pMessageFilter);
    }

    BOOL RemoveMessageFilter(CMessageFilter* pMessageFilter)
    {
         m_aMsgFilter.Remove(pMessageFilter);
    }

// Idle handler operations
    BOOL AddIdleHandler(CIdleHandler* pIdleHandler)
    {
         m_aIdleHandler.Add(pIdleHandler);
    }

    BOOL RemoveIdleHandler(CIdleHandler* pIdleHandler)
    {
         m_aIdleHandler.Remove(pIdleHandler);
    }

#ndef _ATL_NO_OLD_NAMES
    // for compatilibility with old names only
    BOOL AddUpdateUI(CIdleHandler* pIdleHandler)
    {
        ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIObject and AddUpdateUI are deprecated. Please change your code to use CIdleHandler and OnIdle\n"));
         AddIdleHandler(pIdleHandler);
    }

    BOOL RemoveUpdateUI(CIdleHandler* pIdleHandler)
    {
        ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIObject and RemoveUpdateUI are deprecated. Please change your code to use CIdleHandler and OnIdle\n"));
         RemoveIdleHandler(pIdleHandler);
    }
#end // !_ATL_NO_OLD_NAMES

// message loop
     Run
    {
        BOOL bDoIdle = TRUE;
         nIdleCount = 0;
        BOOL bRet;

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

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

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

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

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

         ()m_msg.wParam;
    }

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

         TRUE;
    }

// Overrideables
    // Override to change message filtering
    virtual BOOL PreTranslateMessage(MSG* pMsg)
    {
        // loop backwards
        for( i = m_aMsgFilter.GetSize - 1; i >= 0; i--)
        {
            CMessageFilter* pMessageFilter = m_aMsgFilter[i];
            (pMessageFilter != NULL && pMessageFilter->PreTranslateMessage(pMsg))
                 TRUE;
        }
         FALSE;   // not translated
    }

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


Tags:  feelloop freeloop什么意思 magneloop freeloop

延伸阅读

最新评论

发表评论