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的