现在的位置: 首页 > 综合 > 正文

duilib 消息流程

2018年02月24日 ⁄ 综合 ⁄ 共 3981字 ⁄ 字号 评论关闭
消息循环小探:
    在程序中如果想要处理某消息,可以在GetMessage中截获后处理,当然也可以在窗口过程中直接处理。这里要注意的是,有的事件根本没有进入消息循环,而是直接发送到了窗口过程,比如点击窗口关闭时,在GetMessage里就截获不到消息,在窗口过程里才能收到。所以这就是为什么一个窗口类继承于CWindowWnd必须实现HandleMessage函数并且处理相关消息并PostQuitMessage,否则GetMessage不会收到WM_QUIT消息,从而造成程序没有正常退出。

程序消息流程
    我们都知道消息一般要发送到窗口的消息队列中(也有直接分派到窗口过程中去的),利用GetMessageMap从中获取消息,然后再分派到窗口过程中去。duilib为我们在消息分派到具体的窗口过程函数之前,也可以进行相应的处理。一般用来先处理键盘按下等消息。在函数CPaintManagerUI::TranslateMessage(&msg)中 处理。其思想如下:
CPaintManagerUI有一个m_aPreMessageFilters容器,只要窗口类继承于IMessageFilterUI,m_aPreMessageFilters就可以添加监听该类,一般在WM_CREATE消息时操作:m_PaintManager.AddPreMessageFilter(this)。该类实现了MessageHandler接口就可以预处理消息,而不经过窗口过程。其余的消息则发往他们的窗口过程。
    在程序窗口创建时,注册了窗口的函数过程。该窗口过程如下:
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CWindowWnd* pThis = NULL;
    if( uMsg == WM_NCCREATE ) {
        LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
        pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
        pThis->m_hWnd = hWnd;
        ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
    }
    else {
        pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
        if( uMsg == WM_NCDESTROY && pThis != NULL ) {
            LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
            ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
            if( pThis->m_bSubclassed ) pThis->Unsubclass();
            pThis->m_hWnd = NULL;
            pThis->OnFinalMessage(hWnd);
            return lRes;
        }
    }
    if( pThis != NULL ) {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    else {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}
所以任何消息的处理函数在该类的HandleMessage中。而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);
}
所以可以在WindowImplBase的子类的HandleMessage中先截获相关消息。那程序中的控件又是怎么对相关消息产生响应的?或者说点击一个button应该如何去响应的地方实现?这一切都是CPaintManagerUI的功劳。看上面代码:
if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
  return lRes;
在这里,程序作出了反应,进而推动整个程序。

m_PaintManager.MessageHandler是整个框架的关键所在,而且细节也是相当的复杂。下面从两个重要的方面讨论。

①界面的显示

    duilib的核心就是美化界面。显示绚丽界面的代码也在该函数中实现。和window SDK程序一样也要通过该消息来显示 WM_PAINT。由于细节比较复杂,咱只讲大体思路。整个程序界面和控件都是一个容器,容器又负责里面的子控件绘制,从而得到整个漂亮的界面。

②控件消息的响应

    其实,duilib没有什么真正的控件,每个控件都是‘画’出来的,不像SDK和MFC里的控件都对应一个窗口类,每个类又有其窗口函数,duilib只有主界面一个HWND,所有的诸如点击按钮等消息都是他在接收处理。而控件的消息无外乎鼠标点击,鼠标Hover等等消息。鼠标点击或Hover时通过坐标找到相应的控件,然后模拟控件对事件的响应,这就是duilib处理控件的方法。由于其中涉及到一些控件具体的实现,而我小菜还没得及仔细分析,下次有空再表。

抱歉!评论已关闭.