zoukankan      html  css  js  c++  java
  • duilib库分析: 消息流程分析

     

     

    看下CWindowWnd类与CPaintManagerUI类是咋进行消息分发的吧.

    1. 先看下CPaintManagerUI类的MessageLoop函数:

    1. void CPaintManagerUI::MessageLoop()  
    2. {  
    3.     MSG msg = { 0 };  
    4.     while( ::GetMessage(&msg, NULL, 0, 0) ) {    // 获取消息  
    5.         if( !CPaintManagerUI::TranslateMessage(&msg) ) { // 消息过滤  
    6.             ::TranslateMessage(&msg);  
    7.             ::DispatchMessage(&msg); // 分发到窗口的消息处理窗口中. 也就是调用CWindowWnd类的__WndProc函数或是__ControlProc函数.  
    8.         }  
    9.     }  
    10. }  

    消息第一次会由CPaintManagerUI类的TranslateMessage消息接收到.

    2. 调用CWindowWnd::Create创建窗口.  完成以下操作:

    1) 如果要子类下Window的控件(就是系统的控件, 而不是duilib的模拟控件), 就设置__ControlProc函数为消息回调函数.

    2)不子类化, 就注册窗口类. 此时设置__WndProc为窗口消息处理回调函数.  

    3)用CreateWindowEx API函数创建窗口.

    这里先不看子类化相关的, 我要先看明白标准的窗口创建过程.  这也操作后消息就会分发到__WndProc了, 

    3. 看下__WndProc函数的定义: 

    1. LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     CWindowWnd* pThis = NULL;  
    4.     if( uMsg == WM_NCCREATE ) { // 要在此消息中把类于窗口进行绑定  
    5.         LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);   // 来自于CreateWindowEx函数的最后一个参数( 也就是CWindowWnd对象指针了 )  
    6.         pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);  
    7.         pThis->m_hWnd = hWnd;  
    8.         ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis)); // 设置到窗口的用户数据中  
    9.     }   
    10.     else {  
    11.         pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));  
    12.         if( uMsg == WM_NCDESTROY && pThis != NULL ) {  
    13.             LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);    // 收到窗口能处理到的最后一个消息了, 要进行收尾工作了.  
    14.             ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);    // 取消类对象与窗口的绑定关系  
    15.             if( pThis->m_bSubclassed ) pThis->Unsubclass();  
    16.             pThis->m_hWnd = NULL;  
    17.             pThis->OnFinalMessage(hWnd);  
    18.             return lRes;  
    19.         }  
    20.     }  
    21.     if( pThis != NULL ) {  
    22.         return pThis->HandleMessage(uMsg, wParam, lParam);  // 在此调用继承类的消息处理函数  
    23.     }   
    24.     else {  
    25.         return ::DefWindowProc(hWnd, uMsg, wParam, lParam); // 未绑定类对象, 就调用默认的窗口消息处理函数  
    26.     }  
    27. }  



    消息第二次就由__WndProc接收到, 然后再传到CWindowWnd类的HandlerMessage函数中.

    3. 看看CWindowWnd类的继承类对于HandlerMessage虚函数的实现.

    1. LRESULT CMainWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )  
    2. {  
    3.     LRESULT lRes        = 0;    // 消息处理返回值.  
    4.     BOOL    bHandled    = TRUE; // 消息是否要继续往下传.  
    5.     switch ( uMsg )  
    6.     {  
    7.     case WM_CREATE: lRes    = OnInitResource( bHandled ); break;  // 进行初始化工作. 比如最重要的XML加载解析工作.  
    8.     default:  
    9.         bHandled    = FALSE;  
    10.     }  
    11.   
    12.     if ( bHandled )  
    13.     {  
    14.         return lRes;  
    15.     }  
    16.   
    17.     if ( m_pm.MessageHandler( uMsg, wParam, lParam, lRes ) )    // 传给CPaintManagerUI::MessageHandler函数进行具体的控件处理工作  
    18.     {  
    19.         return lRes;  
    20.     }  
    21.   
    22.     return CWindowWnd::HandleMessage( uMsg, wParam, lParam );  // 没处理过的就调用CWindowWnd类的默认消息处理函数吧.  
    23. }  



    在这里就是用户要按消息进行具体的处理了. 之后要传到CPaintManagerUI类对象的MessageHandler函数. 未处理的消息就要返回给CWindowWnd类的默认消息处理函数来处理了. 

    4.  CPaintManagerUI类的TranslateMessage, MessageHandler函数的内容.

    1. BOOL CPaintManagerUI::TranslateMessage(const LPMSG pMsg)  
    2. {  
    3.     HWND hwndParent = ::GetParent(pMsg->hwnd); // 获取消息接收窗口的父窗口  
    4.     UINT uStyle = GetWindowStyle(pMsg->hwnd); // 获取窗口的样式  
    5.     LRESULT lRes = 0;  
    6.     for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { // 这个m_aPreMessage保存着CPaintManagerUI类对象.   
    7.         CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);  
    8.         if( pMsg->hwnd == pT->GetPaintWindow() // 消息是否属于当前CPaintManagerUI绑定的窗口  
    9.          || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) ) // 消息是否为当前窗口中窗口的消息, (如ActiveX控件 )  
    10.         {  
    11.             if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return TRUE; // 此时就调用PreMessageHandler过滤函数.  
    12.         }  
    13.     }  
    14.     return FALSE;  
    15. }  

    m_aPreMessage为静态成员变量, 在CPaintManagerUI::Init进行窗口与此类绑定时添加到此变量中. 

    5. CPaintManagerUI::PreMessageHandler消息过滤函数.

    1. BOOL CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)  
    2. {  
    3.     // 遍历当前的消息过滤列表. m_aPreMessageFilter的元素为IMessageFilterUI接口.只一个虚函数MessageHandler.   
    4.     // 用户可以添加此接口的继承类变量到m_aPreMessageFilters列表中. ( 调用AddMessageFilter函数实现 )  
    5.     for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ )   
    6.     {  
    7.         BOOL bHandled = FALSE;  
    8.         LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);  
    9.         if( bHandled ) {  
    10.             return TRUE;  
    11.         }  
    12.     }  
    13.     // 以下是对几个按键消息的过滤.  
    14.     // WM_KEYDOWN     检查是否为VK_TAB键, 要进行控件焦点的移动.  
    15.     // WM_SYSCHAR     获取与wParam中的字符加速键匹配的控件, 并激活它.  
    16.     // WM_SYSKEYDOWN  生成控件事件( 用TEventUI来模拟 )  
    17. }  



    5. CPaintManagerUI::MessageHandler函数.

    1) 遍历m_aMessageFilters列表中的IMessageFilterUI接口, 并调用MessageHandler函数, 再次进行相关的消息过滤功能.(与上面的m_aPreMessageFilters类似)

    2) 在此会处理窗口的WM_PAINT消息. 显示所有控件的外观与状态. 

    3) 处理鼠标事件, 实现控件激活和相关事件.

    4) 处理WM_TIMER消息, 所有控件要用CPaintManagerUI的SetTimer, KillTimer等函数实现计时器功能. 

    5) 处理CPaintManagerUI类的自定消息, WM_APP + 1与 +2,

    WM_APP + 1是用于控件延迟销毁控件对象

    WM_APP + 2销毁异步消息的处理. 

    ( 异步控件消息用CPaintManagerUI::SendNotify函数, 把消息对象添加到m_aAsyncNotify列表中, 再PostMessage函数WM_APP + 2 )

    5) 其它基本的窗口相关消息的处理.

    CPaintManagerUI把DUILIB内部的事件都是用TEventUI结构的形式调用CControlUI类的Event函数来投递的.

  • 相关阅读:
    Quartz.net 定式调度任务
    Quartz.net 定时调度CronTrigger时间配置格式说明
    json 数据 添加 删除 排序
    Myeclipse 安装离线adt的方法 ()
    关于C# DataTable 的一些操作
    Android 设置代理(验证用户名和密码)
    js ShowDialogModal 关闭子页面并刷新父页面,保留查询条件
    sql语句中----删除表数据drop、truncate和delete的用法
    SQL数据库增删改查基本语句
    asp.net(C#)利用QRCode生成二维码---.NET菜鸟的成长之路
  • 原文地址:https://www.cnblogs.com/liangxiaofeng/p/4880919.html
Copyright © 2011-2022 走看看