3消息循环中的消息处理

来源:互联网 发布:网络维护实训周报 编辑:程序博客网 时间:2024/05/21 22:25

1.简介

duilib中消息的流程十分复杂,窗口想处理消息,可以通过重载以下几个函数来实现:

- virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled);- virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);- virtual void Notify(TNotifyUI& msg)

2.消息类型

(参考:http://www.cppblog.com/suiaiguo/archive/2009/07/18/90412.html)

从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护。队列消息送到系统消息队列,然后到线程消息队列,一般来讲,系统总是将消息Post在消息队列的末尾(有时为了效率,也会有些小聪明:如同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息)。非队列消息直接送给目的窗口过程。

对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE, WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由 Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,然后把取出的消息送往创建窗口的线程的相应队列。线程看到自己的消息队列中有消息,就从队列中取出来,发送到合适的窗口过程去处理。

非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,如SendMessage()。

3.消息循环

注意:CPaintManagerUI::MessageLoop() 函数为静态成员函数

void CPaintManagerUI::MessageLoop(){    MSG msg = { 0 };    while( ::GetMessage(&msg, NULL, 0, 0) )     {        if( !CPaintManagerUI::TranslateMessage(&msg) )         {            ::TranslateMessage(&msg); //将按键转化为ASCII字符            ::DispatchMessage(&msg); //将消息分发到特定窗口的窗口过程中,消息处理完成才会返回        }    }}

3.1 分发到窗口过程之前

消息到来时,首先由 CPaintManagerUI::TranslateMessage(&msg) 函数进行过滤,只有通过过滤后的消息,才能分发到各个窗口。 TranslateMessage() 函数同样为静态成员函数,其实现如下(简化):

bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg){    //1.当窗口有父窗口时,循环搜索当前的hwnd所以对应的窗口或其长辈窗口(父窗口、祖父窗口);    //2.当窗口无父窗口时,循环搜索当前的hwnd所以对应的窗口;    //如果找到,则处理;否则直接返回false    CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);            if (pT->TranslateAccelerator(pMsg))        return true;    if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) )         return true;    return false;}

其中 m_aPreMessages 为CPaintManagerUI数组,每个元素对应于每个窗口。下面将分别介绍TranslateAccelerator() 函数和 PreMessageHandler() 函数。

3.1.1 函数 TranslateAccelerator()

win32提供的同名函数 TranslateAccelerator() 有两个功能:进行消息翻译,并将消息投递到消息队列中,故如果 TranslateAccelerator(msg) 返回为true时,将不能再进行 ::TranslateMessage() 和 ::DispatchMessage(&msg)。 CPaintManagerUI的TranslateAccelerator直接对消息进行了处理,与win32提供的同名函数效果基本相同。

bool CPaintManagerUI::TranslateAccelerator(LPMSG pMsg){    for (int i = 0; i < m_aTranslateAccelerator.GetSize(); i++)    {        LRESULT lResult = static_cast<ITranslateAccelerator *>(m_aTranslateAccelerator[i])->TranslateAccelerator(pMsg);        if( lResult == S_OK ) return true;    }    return false;}

3.1.2 函数 PreMessageHandler()

PreMessageHandler()函数用于消息过滤,过滤器实现 IMessageFilterUI 接口中的MessageHandler()接口函数;通过 CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter) 函数实现过滤器的添加。

bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/){    //使用过滤器对消息过滤    for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ )     {        bool bHandled = false;        IMessageFilterUI filter = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i]);        LRESULT lResult = filter->MessageHandler(uMsg, wParam, lParam, bHandled);        if( bHandled )             return true;    }    //常规过滤一些消息    switch( uMsg )     {    case WM_KEYDOWN:    case WM_SYSCHAR:    case WM_SYSKEYDOWN:    }    return false;}

将 IMessageFilterUI 列于此处:

class IMessageFilterUI{public:    virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0;};

将 AddMessageFilter() 列于此处:

bool CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter){    ASSERT(m_aMessageFilters.Find(pFilter)<0);    return m_aMessageFilters.Add(pFilter);}

3.2 分发到窗口过程之后

分发到窗口后,将由窗口的窗口过程回调函数进行处理。在窗口创建之前,即WM_NCCREATE消息时,将CWindowWnd对象指针保存在GWLP_USERDATA中,在处理其他消息中,利用此分别调用不同窗口的处理函数:pThis->HandleMessage(uMsg, wParam, lParam)。

继承CWindowWnd的窗口可以通过重载HandleMessage()函数从而个性化处理各种消息。

LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){    CWindowWnd* pThis = NULL;    if( uMsg == WM_NCCREATE )     {        //将CWindowWnd指针保存到hwnd所指向的内核对象中(即窗口)    }     else     {        pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));        if( uMsg == WM_NCDESTROY && pThis != NULL )         {            //重置hwnd所指向的内核对象(即窗口)        }    }    if( pThis != NULL )     {        return pThis->HandleMessage(uMsg, wParam, lParam);    }     else     {        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);    }}

从上可以看出,每个窗口对象的 HandleMessage() 函数基本上可理解为该窗口的窗口过程函数;其中 HandleMessage() 为CWindowWnd中定义的虚函数。

下面将 WindowImplBase 重载的 HandleMessage() 列出:

LRESULT WindowImplBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){    LRESULT lRes = 0;    BOOL bHandled = TRUE;    switch (uMsg)    {        case WM_CREATE:         lRes = OnCreate(uMsg, wParam, lParam, bHandled); break;        case WM_CLOSE:          lRes = OnClose(uMsg, wParam, lParam, bHandled); break;        case WM_DESTROY:        lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break;        #if defined(WIN32) && !defined(UNDER_CE)        case WM_NCACTIVATE:     lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break;        case WM_NCCALCSIZE:     lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break;        case WM_NCPAINT:        lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break;        case WM_NCHITTEST:      lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break;        case WM_GETMINMAXINFO:  lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break;        case WM_MOUSEWHEEL:     lRes = OnMouseWheel(uMsg, wParam, lParam, bHandled); break;        #endif        case WM_SIZE:           lRes = OnSize(uMsg, wParam, lParam, bHandled); break;        case WM_CHAR:           lRes = OnChar(uMsg, wParam, lParam, bHandled); break;        case WM_SYSCOMMAND:     lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break;        case WM_KEYDOWN:        lRes = OnKeyDown(uMsg, wParam, lParam, bHandled); break;        case WM_KILLFOCUS:      lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break;        case WM_SETFOCUS:       lRes = OnSetFocus(uMsg, wParam, lParam, bHandled); break;        case WM_LBUTTONUP:      lRes = OnLButtonUp(uMsg, wParam, lParam, bHandled); break;        case WM_LBUTTONDOWN:    lRes = OnLButtonDown(uMsg, wParam, lParam, bHandled); break;        case WM_MOUSEMOVE:      lRes = OnMouseMove(uMsg, wParam, lParam, bHandled); break;        case WM_MOUSEHOVER:     lRes = OnMouseHover(uMsg, wParam, lParam, bHandled); break;        default:                bHandled = FALSE; break;    }    if (bHandled) return lRes;    lRes = HandleCustomMessage(uMsg, wParam, lParam, bHandled);    if (bHandled) return lRes;    if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))        return lRes;    return CWindowWnd::HandleMessage(uMsg, wParam, lParam);}

HandleCustomMessage()函数为 WindowImplBase 定义的虚函数,继承WindowImplBase的窗口可以重写该函数。

注意最后的 m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes),CPaintManagerUI中的 MessageHandler() 函数已经帮我们实现了大量的功能。 所有的notify都是在此处理的。 后面将对notify的流程进行分析。现将 m_PaintManager.MessageHandler() 简单粘贴如下:

bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes){    //消息的notify    TNotifyUI* pMsg = NULL;    while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) )     {        m_aAsyncNotify.Remove(0);        if( pMsg->pSender != NULL )         {            if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);        }        for( int j = 0; j < m_aNotifiers.GetSize(); j++ )         {            static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);        }        delete pMsg;    }    // Cycle through listeners    for( int i = 0; i < m_aMessageFilters.GetSize(); i++ )     {        bool bHandled = false;        LRESULT lResult = static_cast<IMessageFilterUI*>(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);        if( bHandled )         {            lRes = lResult;            return true;        }    }    // Custom handling of events    switch( uMsg )     {        case WM_APP + 1:        case WM_CLOSE:        case WM_ERASEBKGND:        case WM_PAINT: //重要,负责绘制窗口中的各个控件        ....    }    //消息的notify    pMsg = NULL;    while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) )     {        m_aAsyncNotify.Remove(0);        if( pMsg->pSender != NULL )         {            if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);        }        for( int j = 0; j < m_aNotifiers.GetSize(); j++ )         {            static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);        }        delete pMsg;    }    return false;}

4.总结

通过上面的分析,目前适合窗口重载的消息处理函数有以下几个:

//窗口过程之前virtual LRESULT IMessageFilterUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0;//进入窗口过程中之后virtual LRESULT CWindowWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);virtual LRESULT WindowImplBase::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

当然可能还会有一些其他函数,只是上面的这几个比较重要。记住!!!

0 0
原创粉丝点击