zoukankan      html  css  js  c++  java
  • Duilib 窗口创建流程分析

    1. 一般来说会自定义一个窗口类继承UIBase.h中定义的CWindowWnd类。

    1 class CMainFrameUI:
    2     public CBasicWnd,public INotifyUI,public IDialogBuilderCallback
    3 {
    4     ...
    5 };

    CWindowWnd类定义了一些接口,调用Windows对话框相关的API来创建显示窗口。
    如:

    1   CMainFrameUI * pMainframeUI = new CMainFrameUI();
    2   if( pMainframeUI == NULL ) return 0;
    3   pMainframeUI->Create(NULL, _T("Title"), /*WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_VISIBLE */UI_WNDSTYLE_FRAME , 0, 0, 0, 0);
    4   ::ShowWindow(*pMainframeUI, SW_SHOW);
    5   CPaintManagerUI::MessageLoop(); 

    2. 这里的pMainFrameUI->Create会调用Windows的API ::RegisterClass 注册窗口 然后 ::CreateWindowEx 来创建一个窗口。

    1 HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
    2 {
    3     if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
    4     if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
    5     m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
    6     ASSERT(m_hWnd!=NULL);
    7     return m_hWnd;
    8 }

    注册的时候会定义一个回调 CWindowWnd::__WndProc;

     1 bool CWindowWnd::RegisterWindowClass()
     2 {
     3     WNDCLASS wc = { 0 };
     4     wc.style = GetClassStyle();
     5     wc.cbClsExtra = 0;
     6     wc.cbWndExtra = 0;
     7     wc.hIcon = NULL;
     8     wc.lpfnWndProc = CWindowWnd::__WndProc;
     9     wc.hInstance = CPaintManagerUI::GetInstance();
    10     wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    11     wc.hbrBackground = NULL;
    12     wc.lpszMenuName  = NULL;
    13     wc.lpszClassName = GetWindowClassName();
    14     ATOM ret = ::RegisterClass(&wc);
    15     ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
    16     return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
    17 }

    回调函数:

     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);
     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 }

    主要会调用:pThis->HandleMessage(uMsg, wParam, lParam);

     1 LRESULT CBasicWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
     2 {
     3     LRESULT lRes = 0;
     4     BOOL bHandled = TRUE;
     5 
     6     switch( uMsg ) {
     7     case WM_CREATE:        lRes = OnCreate(uMsg, wParam, lParam, bHandled); break;
     8     case WM_CLOSE:         lRes = OnClose(uMsg, wParam, lParam, bHandled); break;
     9     case WM_DESTROY:       lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break;
    10     case WM_NCACTIVATE:    lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break;
    11     case WM_NCCALCSIZE:    lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break;
    12     case WM_NCPAINT:       lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break;
    13     case WM_NCHITTEST:     lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break;
    14     case WM_SIZE:          lRes = OnSize(uMsg, wParam, lParam, bHandled); break;
    15     case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break;
    16      case WM_SYSCOMMAND:    lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break;
    17     default:
    18         bHandled = FALSE;
    19     }
    20 
    21     if( bHandled ) return lRes;
    22     if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
    23 
    24     return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    25 }

    在这里处理 WM_CREATE 等消息

    未处理的消息则 在这里处理  if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;

    bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes) 

    这个消息处理函数比较复杂,下次再单独拿出来分析分析。

    创建窗口时会收到 WM_CREATE消息:

     1 LRESULT CBasicWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     2 {
     3     LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);
     4     styleValue &= ~WS_CAPTION;
     5     styleValue &= ~WS_THICKFRAME;
     6     ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
     7     m_WndShadow.Create(m_hWnd);
     8     OnSetShadowSize();
     9     m_WndShadow.SetPosition(0, 0);
    10     Init();
    11     return 0;
    12 }

    这里会进入 Init(); 进行初始化操作,然后就到了我们比较熟悉的流程。

    3. ::ShowWindow(*pMainframeUI, SW_SHOW); 这里的*pMainframeUI 应该访问的是通过::CreateWindowEx返回的HWND句柄。
    在CWindowWnd类中定义的变量类型,第一个是m_hWnd。

    估计pMainframeUI指向的第一个数据就是 m_hWnd, 所以::ShowWindow 参数可以 *pMainframeUI 来访问。

     1 class DUILIB_API CWindowWnd
     2 {
     3 public:
     4     CWindowWnd();
     5 
     6     HWND GetHWND() const;
     7     operator HWND() const;
     8 
     9     bool RegisterWindowClass();
    10     bool RegisterSuperclass();
    11 
    12     HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu = NULL);
    13     HWND Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT, HMENU hMenu = NULL);
    14     HWND CreateDuiWindow(HWND hwndParent, LPCTSTR pstrWindowName,DWORD dwStyle =0, DWORD dwExStyle =0);
    15     HWND Subclass(HWND hWnd);
    16     void Unsubclass();
    17     void ShowWindow(bool bShow = true, bool bTakeFocus = true);
    18     UINT ShowModal();
    19     void Close(UINT nRet = IDOK);
    20     void CenterWindow();    // 居中,支持扩展屏幕
    21     void SetIcon(UINT nRes);
    22 
    23     LRESULT SendMessage(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0L);
    24     LRESULT PostMessage(UINT uMsg, WPARAM wParam = 0, LPARAM lParam = 0L);
    25     void ResizeClient(int cx = -1, int cy = -1);
    26 
    27 protected:
    28     virtual LPCTSTR GetWindowClassName() const = 0;
    29     virtual LPCTSTR GetSuperClassName() const;
    30     virtual UINT GetClassStyle() const;
    31 
    32     virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
    33     virtual void OnFinalMessage(HWND hWnd);
    34 
    35     static LRESULT CALLBACK __WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    36     static LRESULT CALLBACK __ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    37 
    38 protected:
    39     HWND m_hWnd;
    40     WNDPROC m_OldWndProc;
    41     bool m_bSubclassed;
    42 };

    4. 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);
     8             }
     9         }
    10     }
    11 }

    这是一个while循环,从线程消息队列获取消息,然后处理,不过微软不太建议这样写,因为GetMessage返回值可能是0,-1,非0;

    如果收到WM_QUIT消息,就会返回0退出,表示程序退出。

    建议这样写,自已编译源码的时候可以改下,不过问题应该也不大的。

     1 BOOL bRet;
     2 
     3 while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
     4 { 
     5     if (bRet == -1)
     6     {
     7         // handle the error and possibly exit
     8     }
     9     else
    10     {
    11         TranslateMessage(&msg); 
    12         DispatchMessage(&msg); 
    13     }
    14 }

    下面看看 CPaintManagerUI::TranslateMessage 是如何处理消息的

     1 bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
     2 {
     3     // Pretranslate Message takes care of system-wide messages, such as
     4     // tabbing and shortcut key-combos. We'll look for all messages for
     5     // each window and any child control attached.
     6     UINT uStyle = GetWindowStyle(pMsg->hwnd);
     7     UINT uChildRes = uStyle & WS_CHILD;    
     8     LRESULT lRes = 0;
     9     if (uChildRes != 0)
    10     {
    11         HWND hWndParent = ::GetParent(pMsg->hwnd);
    12         //code by redrain 2014.12.3,解决edit和webbrowser按tab无法切换焦点的bug
    13         //        for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) 
    14         for( int i = m_aPreMessages.GetSize() - 1; i >= 0 ; --i ) 
    15         {
    16             CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);        
    17             HWND hTempParent = hWndParent;
    18             while(hTempParent)
    19             {
    20 
    21                 if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow())
    22                 {
    23                     if (pT->TranslateAccelerator(pMsg))
    24                         return true;
    25 
    26                     pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes);
    27                     //                     if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) 
    28                     //                         return true;
    29                     // 
    30                     //                     return false;  
    31                 }
    32                 hTempParent = GetParent(hTempParent);
    33             }
    34 
    35         }
    36     }
    37     else
    38     {
    39         for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) 
    40         {
    41             int size = m_aPreMessages.GetSize();
    42             CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
    43             if(pMsg->hwnd == pT->GetPaintWindow())
    44             {
    45                 if (pT->TranslateAccelerator(pMsg))
    46                     return true;
    47 
    48                 if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) 
    49                     return true;
    50 
    51                 return false;
    52             }
    53         }
    54     }
    55     return false;
    56 }

    处理子窗口情况或者处理当前窗口,先处理快捷键操作,将快捷键转换成消息发送出去。

    如果不是快捷键,则找到对应的窗口调用 PreMessageHandler处理

    m_aPreMessages 是创建的所有 CPaintManagerUI 实例(相当于遍历所有窗口)

     1 bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
     2 {
     3     for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) 
     4     {
     5         bool bHandled = false;
     6         LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
     7         if( bHandled ) {
     8             return true;
     9         }
    10     }
    11     switch( uMsg ) {
    12     case WM_KEYDOWN:
    13         {
    14            // Tabbing between controls
    15            if( wParam == VK_TAB ) {
    16                if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), DUI_CTR_RICHEDIT) != NULL ) {
    17                    if( static_cast<CRichEditUI*>(m_pFocus)->IsWantTab() ) return false;
    18                }
    19                SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0);
    20                return true;
    21            }
    22         }
    23         break;
    24     case WM_SYSCHAR:
    25         {
    26            if( m_pRoot == NULL ) return false;
    27            // Handle ALT-shortcut key-combinations
    28            FINDSHORTCUT fs = { 0 };
    29            fs.ch = toupper((int)wParam);
    30            CControlUI* pControl = m_pRoot->FindControl(__FindControlFromShortcut, &fs, UIFIND_ENABLED | UIFIND_ME_FIRST | UIFIND_TOP_FIRST);
    31            if( pControl != NULL ) {
    32                pControl->SetFocus();
    33                pControl->Activate();
    34                return true;
    35            }
    36         }
    37         break;
    38     case WM_SYSKEYDOWN:
    39         {
    40            if( m_pFocus != NULL ) {
    41                TEventUI event = { 0 };
    42                event.Type = UIEVENT_SYSKEY;
    43                event.pSender = m_pFocus;
    44                event.chKey = (TCHAR)wParam;
    45                event.ptMouse = m_ptLastMousePos;
    46                event.wKeyState = MapKeyState();
    47                event.dwTimestamp = ::GetTickCount();
    48                m_pFocus->Event(event);
    49            }
    50         }
    51         break;
    52     }
    53     return false;
    54 }

    m_aPreMessageFilters 是自定义的消息处理

    实现接口 virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0;

    优先调用自定义的消息处理函数进行处理,然后再处理一下按键值消息

    m_aPreMessageFilters 需要手动调用 bool CPaintManagerUI::AddPreMessageFilter(IMessageFilterUI* pFilter) 添加

    否则 m_aPreMessageFilters 的 size 是为0的

  • 相关阅读:
    Linux下Oracle client客户端安装
    深度学习的batch_size
    Ubuntu下CUDA8.0卸载
    Numpy 定义矩阵的方法
    python 按照自然数排序遍历文件 python os.listdir sort by natural sorting
    linux 将终端进行换行
    从LeNet到SENet——卷积神经网络回顾
    神经网络权值初始化方法-Xavier
    FaceAlignment blog
    tensorflow模型量化压缩
  • 原文地址:https://www.cnblogs.com/george-cw/p/14286362.html
Copyright © 2011-2022 走看看