我想通过360demo的学习,大概就能把握duilib的一般用法,同时引申出一些普遍问题,和普遍解决方法。并在此分享一些链接和更多内容的深入学习。。。。。
原谅我是一个菜鸟,什么都想知道得清清楚楚。。。。god,还有一堆书要看,看完是否就会有豁然开朗的感觉呢?
stdafx.h:
1 #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) 2 #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ 3 4 #pragma once 5 6 #define WIN32_LEAN_AND_MEAN 7 #define _CRT_SECURE_NO_DEPRECATE 8 9 #include <windows.h> 10 #include <objbase.h> 11 #include <zmouse.h> 12 13 #include "..DuiLibUIlib.h" 14 15 using namespace DuiLib; 16 17 #ifdef _DEBUG 18 # ifdef _UNICODE 19 # pragma comment(lib, "..\bin\DuiLib_ud.lib") 20 # else 21 # pragma comment(lib, "..\bin\DuiLib_d.lib") 22 # endif 23 #else 24 # ifdef _UNICODE 25 # pragma comment(lib, "..\bin\DuiLib_u.lib") 26 # else 27 # pragma comment(lib, "..\bin\DuiLib.lib") 28 # endif 29 #endif 30 31 //{{AFX_INSERT_LOCATION}} 32 // Microsoft Visual C++ will insert additional declarations immediately before the previous line. 33 34 #endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
第1,2,34行:
1 #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
2 #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_
自动生成的吧,大概就是为了避免stdafx.h被重复包含吧。
第4行:#pragma once
这个不多说了,我另一篇随笔有说
第6行:#define WIN32_LEAN_AND_MEAN
参考:更快的生成和更小的头文件
非mfc应用程序可定义这个宏
第7行:#define _CRT_SECURE_NO_DEPRECATE
参考:CRT Security Enhancements (Windows Embedded CE 6.0)
这里说得非常详细,主要是涉及一些旧crt api的安全问题,例如strcpy没有溢出检查等。。。
推荐方法是使用取而代之的strcpy_s等带安全检查的函数,定义上述的宏是为了消除编译警告。。。
第17-29:duilib不同版本的调用方法
参考:这里
stdafx.cpp:
1 #include "stdafx.h" 2 3 #if defined _M_IX86 4 #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'"") 5 #elif defined _M_IA64 6 #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'"") 7 #elif defined _M_X64 8 #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'"") 9 #else 10 #pragma comment(linker, "/manifestdependency:"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"") 11 #endif
主要处理的是产生窗口的样式问题
ControlEx.h:
1 #pragma once 2 3 class ComputerExamineUI : public CContainerUI 4 { 5 public: 6 ComputerExamineUI() 7 { 8 CDialogBuilder builder; 9 CContainerUI* pComputerExamine = static_cast<CContainerUI*>(builder.Create(_T("ComputerExamine.xml"), (UINT)0)); 10 if( pComputerExamine ) { 11 this->Add(pComputerExamine); 12 } 13 else { 14 this->RemoveAll(); 15 return; 16 } 17 } 18 }; 19 20 class CDialogBuilderCallbackEx : public IDialogBuilderCallback 21 { 22 public: 23 CControlUI* CreateControl(LPCTSTR pstrClass) 24 { 25 if( _tcscmp(pstrClass, _T("ComputerExamine")) == 0 ) return new ComputerExamineUI; 26 return NULL; 27 } 28 };
这里主要演示了自定义控件的使用方法,这部分可以等到分析控件生成过程的时候再详细说一下
360safe.cpp:
1 #include "stdafx.h" 2 #include <exdisp.h> 3 #include <comdef.h> 4 #include "ControlEx.h" 5 6 class C360SafeFrameWnd : public CWindowWnd, public INotifyUI 7 { 8 public: 9 C360SafeFrameWnd() { }; 10 LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };/*overrride from CWindowWnd,内部RegisterSuperclass,RegisterWindowClass,::CreateWindowEx会用到的窗口类名*/ 11 UINT GetClassStyle() const { return CS_DBLCLKS; };//override from CWindowWnd,内部调用RegisterWindowClass会用到 12 void OnFinalMessage(HWND /*hWnd*/) { delete this; };//见解释1 13 // 14 void Init() { 15 m_pCloseBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("closebtn")));/通过xml中定义的控件名,调用CPaintManager的findControl去查询hash表得到控件对象。通过静态类型转化绑定到程序控件上,见解释2*/ 16 m_pMaxBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("maxbtn"))); 17 m_pRestoreBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("restorebtn"))); 18 m_pMinBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("minbtn"))); 19 } 20 21 void OnPrepare() { 22 } 23 24 void Notify(TNotifyUI& msg)//override form INotifyUI,详见解释3 25 { 26 if( msg.sType == _T("windowinit") ) OnPrepare(); 27 else if( msg.sType == _T("click") ) { 28 if( msg.pSender == m_pCloseBtn ) { 29 PostQuitMessage(0); 30 return; 31 } 32 else if( msg.pSender == m_pMinBtn ) { 33 SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); return; } 34 else if( msg.pSender == m_pMaxBtn ) { 35 SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); return; } 36 else if( msg.pSender == m_pRestoreBtn ) { 37 SendMessage(WM_SYSCOMMAND, SC_RESTORE, 0); return; } 38 } 39 else if(msg.sType==_T("selectchanged")) 40 { 41 CStdString name = msg.pSender->GetName(); 42 CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(m_pm.FindControl(_T("switch"))); 43 if(name==_T("examine")) 44 pControl->SelectItem(0); 45 else if(name==_T("trojan")) 46 pControl->SelectItem(1); 47 else if(name==_T("plugins")) 48 pControl->SelectItem(2); 49 else if(name==_T("vulnerability")) 50 pControl->SelectItem(3); 51 else if(name==_T("rubbish")) 52 pControl->SelectItem(4); 53 else if(name==_T("cleanup")) 54 pControl->SelectItem(5); 55 else if(name==_T("fix")) 56 pControl->SelectItem(6); 57 else if(name==_T("tool")) 58 pControl->SelectItem(7); 59 } 60 } 61 62 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 63 { 64 LONG styleValue = ::GetWindowLong(*this, GWL_STYLE); 65 styleValue &= ~WS_CAPTION;// 去掉标题栏 66 ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);//设置窗口重叠时的绘制方式 67 68 m_pm.Init(m_hWnd);//设置父窗口句柄,详见解释4 69 CDialogBuilder builder; 70 CDialogBuilderCallbackEx cb; 71 CControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT)0, &cb, &m_pm);/*加载xml,创建界面,详见解释5,路径是从main函数那里调用CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));设置的*/ 72 ASSERT(pRoot && "Failed to parse XML"); 73 m_pm.AttachDialog(pRoot);/*添加根节点指针到hash表,绘制过程应该回遍历这个hash表,绘制过程待续*/ 74 m_pm.AddNotifier(this);/添加界面消息通知,联合上面的Notify,和下面的HandleMessge不难看出这是一个观察者模式,详见解释3*/ 75 76 Init();//控件绑定,见上面的解释 77 return 0; 78 } 79 80 LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 81 { 82 bHandled = FALSE; 83 return 0; 84 } 85 86 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 87 { 88 ::PostQuitMessage(0L); 89 90 bHandled = FALSE; 91 return 0; 92 } 93 94 LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 95 { 96 if( ::IsIconic(*this) ) bHandled = FALSE; 97 return (wParam == 0) ? TRUE : FALSE; 98 } 99 100 LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 101 { 102 return 0; 103 } 104 105 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 106 { 107 return 0; 108 } 109 110 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//由于隐藏了windows的标题栏,所以自己处理点击事件 111 { 112 POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); 113 ::ScreenToClient(*this, &pt); 114 115 RECT rcClient; 116 ::GetClientRect(*this, &rcClient); 117 // 这段屏蔽了鼠标拖动区域响应 118 // if( !::IsZoomed(*this) ) { 119 // RECT rcSizeBox = m_pm.GetSizeBox(); 120 // if( pt.y < rcClient.top + rcSizeBox.top ) { 121 // if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT; 122 // if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT; 123 // return HTTOP; 124 // } 125 // else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) { 126 // if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT; 127 // if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT; 128 // return HTBOTTOM; 129 // } 130 // if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT; 131 // if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT; 132 // } 133 134 RECT rcCaption = m_pm.GetCaptionRect(); 135 if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right 136 && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) { 137 CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(pt)); 138 if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != 0 && 139 _tcscmp(pControl->GetClass(), _T("OptionUI")) != 0 && 140 _tcscmp(pControl->GetClass(), _T("TextUI")) != 0 ) 141 return HTCAPTION; 142 } 143 144 return HTCLIENT; 145 } 146 147 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//这里应该是为了边框圆角化 148 { 149 SIZE szRoundCorner = m_pm.GetRoundCorner(); 150 if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) { 151 CRect rcWnd; 152 ::GetWindowRect(*this, &rcWnd); 153 rcWnd.Offset(-rcWnd.left, -rcWnd.top); 154 rcWnd.right++; rcWnd.bottom++; 155 HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy); 156 ::SetWindowRgn(*this, hRgn, TRUE); 157 ::DeleteObject(hRgn); 158 } 159 160 bHandled = FALSE; 161 return 0; 162 } 163 164 LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//控制窗体的显示区域,放大缩小等的范围 165 { 166 MONITORINFO oMonitor = {}; 167 oMonitor.cbSize = sizeof(oMonitor); 168 ::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor); 169 CRect rcWork = oMonitor.rcWork; 170 rcWork.Offset(-rcWork.left, -rcWork.top); 171 172 LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam; 173 lpMMI->ptMaxPosition.x = rcWork.left; 174 lpMMI->ptMaxPosition.y = rcWork.top; 175 lpMMI->ptMaxSize.x = rcWork.right; 176 lpMMI->ptMaxSize.y = rcWork.bottom; 177 178 bHandled = FALSE; 179 return 0; 180 } 181 182 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 183 { 184 // 有时会在收到WM_NCDESTROY后收到wParam为SC_CLOSE的WM_SYSCOMMAND 185 if( wParam == SC_CLOSE ) { 186 ::PostQuitMessage(0L); 187 bHandled = TRUE; 188 return 0; 189 } 190 BOOL bZoomed = ::IsZoomed(*this); 191 LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam); 192 if( ::IsZoomed(*this) != bZoomed ) { 193 if( !bZoomed ) { 194 CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("maxbtn"))); 195 if( pControl ) pControl->SetVisible(false); 196 pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("restorebtn"))); 197 if( pControl ) pControl->SetVisible(true); 198 } 199 else { 200 CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("maxbtn"))); 201 if( pControl ) pControl->SetVisible(true); 202 pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("restorebtn"))); 203 if( pControl ) pControl->SetVisible(false); 204 } 205 } 206 return lRes; 207 } 208 209 // override from CWindowWnd 210 LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)//详见解释3 211 { 212 LRESULT lRes = 0; 213 BOOL bHandled = TRUE; 214 switch( uMsg ) { 215 case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; 216 case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; 217 case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; 218 case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; 219 case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; 220 case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; 221 case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; 222 case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; 223 case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; 224 case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; 225 default: 226 bHandled = FALSE; 227 } 228 if( bHandled ) return lRes; 229 if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; 230 return CWindowWnd::HandleMessage(uMsg, wParam, lParam); 231 } 232 233 public: 234 CPaintManagerUI m_pm;//为什么要是public。。。。 235 236 private: 237 CButtonUI* m_pCloseBtn; 238 CButtonUI* m_pMaxBtn; 239 CButtonUI* m_pRestoreBtn; 240 CButtonUI* m_pMinBtn; 241 //... 242 }; 243 244 245 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow) 246 { 247 CPaintManagerUI::SetInstance(hInstance);//绑定实例句柄 248 CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));//设置资源路径,里面应该是个zip包 249 CPaintManagerUI::SetResourceZip(_T("360SafeRes.zip"));//设置zip包名称,资源都用zip打包好的 250 251 HRESULT Hr = ::CoInitialize(NULL);//com初始化 252 if( FAILED(Hr) ) return 0; 253 254 C360SafeFrameWnd* pFrame = new C360SafeFrameWnd(); 255 if( pFrame == NULL ) return 0; 256 pFrame->Create(NULL, _T("360安全卫士"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 572);//详见解释6 257 pFrame->CenterWindow(); 258 ::ShowWindow(*pFrame, SW_SHOW); 259 260 CPaintManagerUI::MessageLoop();//开始消息循环,详见解释3 261 262 ::CoUninitialize();//注销com 263 return 0; 264 }
解释1:void OnFinalMessage(HWND /*hWnd*/) { delete this; };
override from CWindowWnd
调用来自注册窗体的回调函数:
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);//这里用到了onFinanlMessage 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 }
主要是在接收到WM_NCDESTROY消息的时候,执行最后的一些处理,这里是调用delete this,销毁对象,禁止进一步对对象成员的访问,参考:这里
附:
WM_NCCLIENT 消息在 WM_CREATE 之前,
WM_NCDESTROY 在 WM_DESTROY 之后,
包括标题栏、窗口边框、最大、最小按钮、滚动条等都属于 non-client 区域。
引用自:http://bbs.csdn.net/topics/350112762
解释2:控件这块,会有后续章节,更深入地去了解
解释3:消息循环,也会有后续章节深入了解
解释4:m_pm.Init(m_hWnd);
1 void CPaintManagerUI::Init(HWND hWnd) 2 { 3 ASSERT(::IsWindow(hWnd)); 4 // Remember the window context we came from 5 m_hWndPaint = hWnd; 6 m_hDcPaint = ::GetDC(hWnd); 7 // We'll want to filter messages globally too 8 m_aPreMessages.Add(this);/*添加当前paintmanager对象指针到类静态成员中,用于一些消息的处理,这部分如果有机会说paintManager应该会说说,各位也可以自己看吧*/ 9 }
当前窗口句柄保存到paintManager
解释5:ControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT)0, &cb, &m_pm);
如果有时间说builder的xml解析,会深入了解
解释6:pFrame->Create(NULL, _T("360安全卫士"), UI_WNDSTYLE_FRAME, 0L, 0, 0, 800, 572);
内部还是调用windows窗体注册过程
1 HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu) 2 { 3 return Create(hwndParent, pstrName, dwStyle, dwExStyle, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hMenu); 4 } 5 6 HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu) 7 { 8 if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL; 9 if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL; 10 m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this); 11 ASSERT(m_hWnd!=NULL); 12 return m_hWnd; 13 }