我想通过360demo的学习,大概就能把握duilib的一般用法,同时引申出一些普遍问题,和普遍解决方法。并在此分享一些链接和更多内容的深入学习。。。。。
原谅我是一个菜鸟,什么都想知道得清清楚楚。。。。god,还有一堆书要看,看完是否就会有豁然开朗的感觉呢?
stdafx.h:
#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ #pragma once #define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_DEPRECATE #include <windows.h>
#include <objbase.h>
#include <zmouse.h> #include "..\DuiLib\UIlib.h" using namespace DuiLib; #ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "..\\bin\\DuiLib_ud.lib")
# else
# pragma comment(lib, "..\\bin\\DuiLib_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "..\\bin\\DuiLib_u.lib")
# else
# pragma comment(lib, "..\\bin\\DuiLib.lib")
# endif
#endif //{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line. #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:
#include "stdafx.h" #if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
主要处理的是产生窗口的样式问题
ControlEx.h:
#pragma once class ComputerExamineUI : public CContainerUI
{
public:
ComputerExamineUI()
{
CDialogBuilder builder;
CContainerUI* pComputerExamine = static_cast<CContainerUI*>(builder.Create(_T("ComputerExamine.xml"), (UINT)));
if( pComputerExamine ) {
this->Add(pComputerExamine);
}
else {
this->RemoveAll();
return;
}
}
}; class CDialogBuilderCallbackEx : public IDialogBuilderCallback
{
public:
CControlUI* CreateControl(LPCTSTR pstrClass)
{
if( _tcscmp(pstrClass, _T("ComputerExamine")) == ) return new ComputerExamineUI;
return NULL;
}
};
这里主要演示了自定义控件的使用方法,这部分可以等到分析控件生成过程的时候再详细说一下
360safe.cpp:
#include "stdafx.h"
#include <exdisp.h>
#include <comdef.h>
#include "ControlEx.h" class C360SafeFrameWnd : public CWindowWnd, public INotifyUI
{
public:
C360SafeFrameWnd() { };
LPCTSTR GetWindowClassName() const { return _T("UIMainFrame"); };/*overrride from CWindowWnd,内部RegisterSuperclass,RegisterWindowClass,::CreateWindowEx会用到的窗口类名*/
UINT GetClassStyle() const { return CS_DBLCLKS; };//override from CWindowWnd,内部调用RegisterWindowClass会用到
void OnFinalMessage(HWND /*hWnd*/) { delete this; };//见解释1
//
void Init() {
m_pCloseBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("closebtn")));/通过xml中定义的控件名,调用CPaintManager的findControl去查询hash表得到控件对象。通过静态类型转化绑定到程序控件上,见解释2*/
m_pMaxBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("maxbtn")));
m_pRestoreBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("restorebtn")));
m_pMinBtn = static_cast<CButtonUI*>(m_pm.FindControl(_T("minbtn")));
} void OnPrepare() {
} void Notify(TNotifyUI& msg)//override form INotifyUI,详见解释3
{
if( msg.sType == _T("windowinit") ) OnPrepare();
else if( msg.sType == _T("click") ) {
if( msg.pSender == m_pCloseBtn ) {
PostQuitMessage();
return;
}
else if( msg.pSender == m_pMinBtn ) {
SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, ); return; }
else if( msg.pSender == m_pMaxBtn ) {
SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, ); return; }
else if( msg.pSender == m_pRestoreBtn ) {
SendMessage(WM_SYSCOMMAND, SC_RESTORE, ); return; }
}
else if(msg.sType==_T("selectchanged"))
{
CStdString name = msg.pSender->GetName();
CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(m_pm.FindControl(_T("switch")));
if(name==_T("examine"))
pControl->SelectItem();
else if(name==_T("*"))
pControl->SelectItem();
else if(name==_T("plugins"))
pControl->SelectItem();
else if(name==_T("vulnerability"))
pControl->SelectItem();
else if(name==_T("rubbish"))
pControl->SelectItem();
else if(name==_T("cleanup"))
pControl->SelectItem();
else if(name==_T("fix"))
pControl->SelectItem();
else if(name==_T("tool"))
pControl->SelectItem();
}
} LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);
styleValue &= ~WS_CAPTION;// 去掉标题栏
::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);//设置窗口重叠时的绘制方式 m_pm.Init(m_hWnd);//设置父窗口句柄,详见解释4
CDialogBuilder builder;
CDialogBuilderCallbackEx cb;
CControlUI* pRoot = builder.Create(_T("skin.xml"), (UINT), &cb, &m_pm);/*加载xml,创建界面,详见解释5,路径是从main函数那里调用CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));设置的*/
ASSERT(pRoot && "Failed to parse XML");
m_pm.AttachDialog(pRoot);/*添加根节点指针到hash表,绘制过程应该回遍历这个hash表,绘制过程待续*/
m_pm.AddNotifier(this);/添加界面消息通知,联合上面的Notify,和下面的HandleMessge不难看出这是一个观察者模式,详见解释3*/ Init();//控件绑定,见上面的解释
return ;
} LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
return ;
} LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
::PostQuitMessage(0L); bHandled = FALSE;
return ;
} LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if( ::IsIconic(*this) ) bHandled = FALSE;
return (wParam == ) ? TRUE : FALSE;
} LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return ;
} LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return ;
} LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//由于隐藏了windows的标题栏,所以自己处理点击事件
{
POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam);
::ScreenToClient(*this, &pt); RECT rcClient;
::GetClientRect(*this, &rcClient);
// 这段屏蔽了鼠标拖动区域响应
// if( !::IsZoomed(*this) ) {
// RECT rcSizeBox = m_pm.GetSizeBox();
// if( pt.y < rcClient.top + rcSizeBox.top ) {
// if( pt.x < rcClient.left + rcSizeBox.left ) return HTTOPLEFT;
// if( pt.x > rcClient.right - rcSizeBox.right ) return HTTOPRIGHT;
// return HTTOP;
// }
// else if( pt.y > rcClient.bottom - rcSizeBox.bottom ) {
// if( pt.x < rcClient.left + rcSizeBox.left ) return HTBOTTOMLEFT;
// if( pt.x > rcClient.right - rcSizeBox.right ) return HTBOTTOMRIGHT;
// return HTBOTTOM;
// }
// if( pt.x < rcClient.left + rcSizeBox.left ) return HTLEFT;
// if( pt.x > rcClient.right - rcSizeBox.right ) return HTRIGHT;
// } RECT rcCaption = m_pm.GetCaptionRect();
if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \
&& pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) {
CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(pt));
if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != &&
_tcscmp(pControl->GetClass(), _T("OptionUI")) != &&
_tcscmp(pControl->GetClass(), _T("TextUI")) != )
return HTCAPTION;
} return HTCLIENT;
} LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//这里应该是为了边框圆角化
{
SIZE szRoundCorner = m_pm.GetRoundCorner();
if( !::IsIconic(*this) && (szRoundCorner.cx != || szRoundCorner.cy != ) ) {
CRect rcWnd;
::GetWindowRect(*this, &rcWnd);
rcWnd.Offset(-rcWnd.left, -rcWnd.top);
rcWnd.right++; rcWnd.bottom++;
HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy);
::SetWindowRgn(*this, hRgn, TRUE);
::DeleteObject(hRgn);
} bHandled = FALSE;
return ;
} LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)//控制窗体的显示区域,放大缩小等的范围
{
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor);
CRect rcWork = oMonitor.rcWork;
rcWork.Offset(-rcWork.left, -rcWork.top); LPMINMAXINFO lpMMI = (LPMINMAXINFO) lParam;
lpMMI->ptMaxPosition.x = rcWork.left;
lpMMI->ptMaxPosition.y = rcWork.top;
lpMMI->ptMaxSize.x = rcWork.right;
lpMMI->ptMaxSize.y = rcWork.bottom; bHandled = FALSE;
return ;
} LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// 有时会在收到WM_NCDESTROY后收到wParam为SC_CLOSE的WM_SYSCOMMAND
if( wParam == SC_CLOSE ) {
::PostQuitMessage(0L);
bHandled = TRUE;
return ;
}
BOOL bZoomed = ::IsZoomed(*this);
LRESULT lRes = CWindowWnd::HandleMessage(uMsg, wParam, lParam);
if( ::IsZoomed(*this) != bZoomed ) {
if( !bZoomed ) {
CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("maxbtn")));
if( pControl ) pControl->SetVisible(false);
pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("restorebtn")));
if( pControl ) pControl->SetVisible(true);
}
else {
CControlUI* pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("maxbtn")));
if( pControl ) pControl->SetVisible(true);
pControl = static_cast<CControlUI*>(m_pm.FindControl(_T("restorebtn")));
if( pControl ) pControl->SetVisible(false);
}
}
return lRes;
} // override from CWindowWnd
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)//详见解释3
{
LRESULT lRes = ;
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;
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_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break;
case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break;
case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break;
default:
bHandled = FALSE;
}
if( bHandled ) return lRes;
if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
} public:
CPaintManagerUI m_pm;//为什么要是public。。。。 private:
CButtonUI* m_pCloseBtn;
CButtonUI* m_pMaxBtn;
CButtonUI* m_pRestoreBtn;
CButtonUI* m_pMinBtn;
//...
}; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
CPaintManagerUI::SetInstance(hInstance);//绑定实例句柄
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin"));//设置资源路径,里面应该是个zip包
CPaintManagerUI::SetResourceZip(_T("360SafeRes.zip"));//设置zip包名称,资源都用zip打包好的 HRESULT Hr = ::CoInitialize(NULL);//com初始化
if( FAILED(Hr) ) return ; C360SafeFrameWnd* pFrame = new C360SafeFrameWnd();
if( pFrame == NULL ) return ;
pFrame->Create(NULL, _T("360安全卫士"), UI_WNDSTYLE_FRAME, 0L, , , , );//详见解释6
pFrame->CenterWindow();
::ShowWindow(*pFrame, SW_SHOW); CPaintManagerUI::MessageLoop();//开始消息循环,详见解释3 ::CoUninitialize();//注销com
return ;
}
解释1:void OnFinalMessage(HWND /*hWnd*/) { delete this; };
override from CWindowWnd
调用来自注册窗体的回调函数:
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);//这里用到了onFinanlMessage
return lRes;
}
}
if( pThis != NULL ) {
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
主要是在接收到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);
void CPaintManagerUI::Init(HWND hWnd)
{
ASSERT(::IsWindow(hWnd));
// Remember the window context we came from
m_hWndPaint = hWnd;
m_hDcPaint = ::GetDC(hWnd);
// We'll want to filter messages globally too
m_aPreMessages.Add(this);/*添加当前paintmanager对象指针到类静态成员中,用于一些消息的处理,这部分如果有机会说paintManager应该会说说,各位也可以自己看吧*/
}
当前窗口句柄保存到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窗体注册过程
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, const RECT rc, HMENU hMenu)
{
return Create(hwndParent, pstrName, dwStyle, dwExStyle, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hMenu);
} HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
ASSERT(m_hWnd!=NULL);
return m_hWnd;
}