再次强调,duilib只不过是一种思想!

  在上一节中,我剖析了duilib中窗口类的注册,其中遗留两个小问题没有细说的?

  第一个问题:过程函数中__WndProc()中有这么一小段代码:

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

  有这么一段代码,请看我分析一下流程,首先第一句,从USER_DATA中取出this指针,接着判断是否是WM_NCDESTROY消息,这段有什么用呢?众所周知,这个WM_NCDESTROY消息是Windows程序退出时所发送的最后一个消息,那么拦截这个消息就是留给我们一个清场的机会,看到pThis->OnFinalMessage(hWnd)没?这个函数就是个虚函数,我们可以重写它,从而处理程序退出时应该做的后续工作,比如资源释放等.

  第二个问题:就是我上一节提到的duilib另外一套处理消息的机制

  接着看过程函数__WndProc()的代码:

  

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);returnlRes;}}if( pThis != NULL ) {return pThis->HandleMessage(uMsg, wParam, lParam);} else {return::DefWindowProc(hWnd, uMsg, wParam, lParam);}
}

  请看红色标记部分.

  我之所以说,duilib采用的消息处理机制,和MFC稍微有些不同,是因为duilib的消息处理是在另一个类当中处理的,CPaintManagerUI 就是这个类!

  那么duilib为什么要设计这样一个类来处理消息, 消息处理为什么不放在CWindowWnd类中直接处理,而要放在CPaintManagerUI类中?这些归功于duilib设计者们的良苦用心!为什么?

  如果我们在不看duilib源码的基础上,也去设计一个这样的duilib,也许我们会这样设计:我们对消息的处理是在用户构建的类CDuiFrameWnd类(派生自CWindowWnd),为了在控件中可以实时刷新,那么每个控件都必须强制的保存一个变量---m_hWnd(主窗口句柄),而且,我们在每次处理消息时,都必然做一些重复性的工作(代码),那么我们将这些控件都具有的变量与操作都封装起来,这就是CPaintManagerUI类的由来!当然从名字看来,它主要处理的是绘图方面的消息,因为整个应用程序只有一个句柄(主窗口句柄),所以CPaintManagerUI作为管理者,承载了很多.看代码:

class CDuiFrameWnd : public CWindowWnd, publicINotifyUI
{public:virtual LPCTSTR GetWindowClassName() const{return _T("DUIMainFrame"); }virtual void    Notify(TNotifyUI&msg) {if (msg.sType == _T("click")){if (msg.pSender->GetName() == _T("btnClick")){::MessageBox(NULL, _T("我是按钮"), _T("点击了按钮"), NULL);}}}virtualLRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){LRESULT lRes= 0;if( uMsg ==WM_CREATE ) {CControlUI*pWnd = newCButtonUI;pWnd->SetName(_T("btnClick"));pWnd->SetText(_T("My First DUI"));   //设置文字pWnd->SetBkColor(0xFF808080);       //设置背景色
m_PaintManager.Init(m_hWnd);//主窗口句柄
m_PaintManager.AttachDialog(pWnd);m_PaintManager.AddNotifier(this);returnlRes;}if( m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes) ) {return lRes;}return__super::HandleMessage(uMsg, wParam, lParam);}protected:CPaintManagerUI m_PaintManager;
};

  这就是我们的CDuiFrameWnd类,其中就有类CPaintManagerUI的对象m_PaintManager. 红色标记部分就是m_PaintManager处理绘图消息.如果没有被处理的其他消息,则给基类处理;

不过CPaintManager为我们处理了最重要的两个消息:命令消息 和 通知消息 ,请看代码:

  

    caseWM_NOTIFY:{LPNMHDR lpNMHDR=(LPNMHDR) lParam;if( lpNMHDR != NULL ) lRes = ::SendMessage(lpNMHDR->hwndFrom, OCM__BASE +uMsg, wParam, lParam);return true;}break;caseWM_COMMAND:{if( lParam == 0 ) break;HWND hWndChild=(HWND) lParam;lRes= ::SendMessage(hWndChild, OCM__BASE +uMsg, wParam, lParam);return true;}

  从代码上看,SendMessage()中的参数和MFC源码中是如出一辙,没什么两样,话不多说,上图,正所谓源码面前,了无秘语--j.j.hou

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{//OnWndMsg does most of the work, except for DefWindowProc callLRESULT lResult = 0;if (!OnWndMsg(message, wParam, lParam, &lResult))lResult=DefWindowProc(message, wParam, lParam);returnlResult;
}

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT*pResult)
{LRESULT lResult= 0;union MessageMapFunctions mmf;mmf.pfn= 0;CInternalGlobalLock winMsgLock;//special case for commandsif (message ==WM_COMMAND){if(OnCommand(wParam, lParam)){lResult= 1;gotoLReturnTrue;}returnFALSE;}//special case for notifiesif (message ==WM_NOTIFY){NMHDR* pNMHDR = (NMHDR*)lParam;if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))gotoLReturnTrue;returnFALSE;}
...//代码太多,请自己查看余下的部分,这里只挑出我要讲的部分,代码在wincore.cpp中
}

接着看

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)//return TRUE if command invocation was attempted
{UINT nID=LOWORD(wParam);HWND hWndCtrl=(HWND)lParam;int nCode =HIWORD(wParam);//default routing for command messages (through closure table)if (hWndCtrl ==NULL){//zero IDs for normal commands are not allowedif (nID == 0)returnFALSE;//make sure command has not become disabled before routing
CTestCmdUI state;state.m_nID=nID;OnCmdMsg(nID, CN_UPDATE_COMMAND_UI,&state, NULL);if (!state.m_bEnabled){TRACE(traceAppMsg,0, _T("Warning: not executing disabled command %d\n"), nID);returnTRUE;}//menu or acceleratornCode =CN_COMMAND;}else //请注意这里的控件反射消息{//control notificationASSERT(nID == 0 ||::IsWindow(hWndCtrl));if (_afxThreadState->m_hLockoutNotifyWindow ==m_hWnd)return TRUE;        //locked out - ignore control notification//reflect notification to child window controlif(ReflectLastMsg(hWndCtrl))return TRUE;    //eaten by child//zero IDs for normal commands are not allowedif (nID == 0)returnFALSE;}#ifdef _DEBUGif (nCode < 0 && nCode != (int)0x8000)TRACE(traceAppMsg,0, _T("Implementation Warning: control notification = $%X.\n"),nCode);#endifreturnOnCmdMsg(nID, nCode, NULL, NULL);
}

再看通知消息的处理

BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT*pResult)
{ASSERT(pResult!=NULL);NMHDR* pNMHDR = (NMHDR*)lParam;HWND hWndCtrl= pNMHDR->hwndFrom;//get the child ID from the window itselfUINT_PTR nID =_AfxGetDlgCtrlID(hWndCtrl);int nCode = pNMHDR->code;ASSERT(hWndCtrl!=NULL);ASSERT(::IsWindow(hWndCtrl));if (_afxThreadState->m_hLockoutNotifyWindow ==m_hWnd)return TRUE;        //locked out - ignore control notification//reflect notification to child window controlif(ReflectLastMsg(hWndCtrl, pResult))return TRUE;        //eaten by child
AFX_NOTIFY notify;notify.pResult=pResult;notify.pNMHDR=pNMHDR;return OnCmdMsg((UINT)nID, MAKELONG(nCode, WM_NOTIFY), &notify, NULL);
}

  我复制了这么多代码干嘛?我想让大家引起注意的是我用蓝色加粗部分的ReflectLastMsg(hWndCtrl, pResult)函数,别着急,接着往下看:

BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT*pResult)
{//get the map, and if no map, then this message does not need reflectionCHandleMap* pMap =afxMapHWND();if (pMap ==NULL)returnFALSE;//check if in permanent map, if it is reflect it (could be OLE control)CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);ASSERT(pWnd== NULL || pWnd->m_hWnd ==hWndChild);if (pWnd ==NULL){
#ifndef _AFX_NO_OCC_SUPPORT//check if the window is an OLE controlCWnd* pWndParent = (CWnd*)pMap->LookupPermanent(::GetParent(hWndChild));if (pWndParent != NULL && pWndParent->m_pCtrlCont !=NULL){//If a matching control site exists, it's an OLE controlCOleControlSite* pSite = (COleControlSite*)pWndParent->m_pCtrlCont->m_siteMap.GetValueAt(hWndChild);if (pSite !=NULL){CWnd wndTemp(hWndChild);wndTemp.m_pCtrlSite=pSite;LRESULT lResult=wndTemp.SendChildNotifyLastMsg(pResult);wndTemp.m_hWnd=NULL;return lResult != 0;}}#endif //!_AFX_NO_OCC_SUPPORTreturnFALSE;}//only OLE controls and permanent windows will get reflected msgsASSERT(pWnd !=NULL);return pWnd->SendChildNotifyLastMsg(pResult);
}

BOOL CWnd::SendChildNotifyLastMsg(LRESULT*pResult)
{_AFX_THREAD_STATE* pThreadState =_afxThreadState.GetData();return OnChildNotify(pThreadState->m_lastSentMsg.message,pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);
}

BOOL CWnd::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT*pResult)
{
#ifndef _AFX_NO_OCC_SUPPORTif (m_pCtrlSite !=NULL){//first forward raw OCM_ messages to OLE control sourcesLRESULT lResult = SendMessage(OCM__BASE+uMsg, wParam, lParam);if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC &&(HBRUSH)lResult==NULL){//for WM_CTLCOLOR msgs, returning NULL implies continue routingreturnFALSE;}if (pResult !=NULL)*pResult =lResult;returnTRUE;}#endifreturnReflectChildNotify(uMsg, wParam, lParam, pResult);
}

BOOL CWnd::ReflectChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT*pResult)
{//Note: reflected messages are send directly to CWnd::OnWndMsg//and CWnd::OnCmdMsg for speed and because these messages are not//routed by normal OnCmdMsg routing (they are only dispatched)switch(uMsg){//normal messages (just wParam, lParam through OnWndMsg)caseWM_HSCROLL:caseWM_VSCROLL:
#ifndef _WIN32_WCEcaseWM_PARENTNOTIFY:#endif //!_WIN32_WCEcaseWM_DRAWITEM:caseWM_MEASUREITEM:caseWM_DELETEITEM:caseWM_VKEYTOITEM:caseWM_CHARTOITEM:caseWM_COMPAREITEM://reflect the message through the message map as WM_REFLECT_BASE+uMsg
return CWnd::OnWndMsg(WM_REFLECT_BASE+uMsg, wParam, lParam, pResult);// special case for WM_COMMAND
case WM_COMMAND:{// reflect the message through the message map as OCM_COMMANDint nCode = HIWORD(wParam);if (CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_COMMAND), NULL, NULL)){if (pResult != NULL)*pResult = 1;return TRUE;}}break;// special case for WM_NOTIFYcase WM_NOTIFY:{// reflect the message through the message map as OCM_NOTIFYNMHDR* pNMHDR = (NMHDR*)lParam;int nCode = pNMHDR->code;AFX_NOTIFY notify;notify.pResult = pResult;notify.pNMHDR = pNMHDR;return CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY), &notify, NULL);}//other special cases (WM_CTLCOLOR family)default:if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <=WM_CTLCOLORSTATIC){//fill in special struct for compatiblity with 16-bit WM_CTLCOLOR
AFX_CTLCOLOR ctl;ctl.hDC=(HDC)wParam;ctl.nCtlType= uMsg -WM_CTLCOLORMSGBOX;//ASSERT(ctl.nCtlType >= CTLCOLOR_MSGBOX);ASSERT(ctl.nCtlType <=CTLCOLOR_STATIC);//reflect the message through the message map as OCM_CTLCOLORBOOL bResult = CWnd::OnWndMsg(WM_REFLECT_BASE+WM_CTLCOLOR, 0, (LPARAM)&ctl, pResult);if ((HBRUSH)*pResult ==NULL)bResult=FALSE;returnbResult;}break;}return FALSE;   //let the parent handle it
}

  好了,我们终于到了最核心的部分,看到我用红色标记的部分没,这两个case就是WM_COMMAND和WM_NOTIFY消息的反射处理,我们可以看到MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY)..是不是和duilib的如出一辙?

  这些消息最终会流向基类CCmdTarget去查询消息映射表!

  反射消息处理路线,暂时先到这吧,东拉西扯了这么多,呵呵!

转载于:https://www.cnblogs.com/xiejiulong/p/3792671.html

duilib学习领悟(2)相关推荐

  1. duilib学习领悟(4)

    使用duilib创建的主窗口绘制工作全都发生在一个 真实存在的主窗口句柄当中,这个绘制过程稍稍有些复杂,但再复杂也逃不过WM_PAINT消息,所有的绘制工作也都由这个WM_PAINT消息完成.在上几篇 ...

  2. Duilib学习笔记《04》— 窗体显示

    在前面已经了解了duilib控件以及界面布局相关内容,接下来就要考虑该如何将xml中描述的布局通过界面展现出来.实际上在 Duilib学习笔记<01> 中我们已经简单提到过基本的流程及元素 ...

  3. Duilib学习笔记《03》— 控件使用

    在前面已经对duilib有个一个基本的了解,并且创建了简单的空白窗体.这仅仅只是一个开始,如何去创建一个绚丽多彩的界面呢?这就需要一些控件元素(按钮.文本框.列表框等等)来完善. 一. Duilib控 ...

  4. Duilib学习笔记《05》— 消息响应处理

    在Duilib学习笔记<04>中已经知道了如何将窗体显示出来,而如何处理窗体上的事件.消息呢? 一. 系统消息 窗体显示的时候我们就已经说了,窗体是继承CWindowWnd类的,对于窗体的 ...

  5. Duilib学习之仿酷狗开发(2)

    今天有时间了接着Duilib学习之仿酷狗开发(1),先说下我遇到的无问题和解决方法,我用的界面库是从Githut上下载的其中有些是需要使用者自己修改的,比如库Demo中有个QQ的Demo搜索好友信息使 ...

  6. duilib学习笔记

    前段时间对皮肤引擎比较感兴趣,于是在VS第一人称快的无法直视的dot大神推荐下,看了一个小巧又好用的皮肤引擎:duilib. 1. duilib简介 duilib是一个开源的DirectUI界面库,简 ...

  7. DuiLib学习笔记5——标题栏不能正常隐藏问题

    我之前代码都是照着官方那个Duilib入门文档.doc来学习的.但是遇到一个问题,虽然他隐藏了windows的自带标题栏,可以自己绘画一个标题栏了,但是在这个标题栏下方,用力乱戳,就可能把系统自带的, ...

  8. Duilib学习-窗口句柄获取

    duilib中的InitWindow()这个函数中去调用 this->GetHWND(); 把返回值放到自己定义 中的HWND句柄中就可以全局调用, 不能用m_hWnd

  9. DuiLib学习笔记1.编译运行demo

    c++中皮肤问题比较麻烦,MFC自带的太难用.DirectUI界面库就比较强大了,之前像skin++之类的基于DirectUI收费昂贵.DuiLib是基于DirectUI的界面库,可以将用户界面和处理 ...

最新文章

  1. 15.泡菜:pickle模块
  2. 【项目实战课】基于Pytorch的Pix2Pix黑白图片上色实战
  3. K8S部署工具:KubeOperator系统设置
  4. kafka系列九、kafka事务原理、事务API和使用场景
  5. maven 配置(安装)以及遇到的The JAVA_HOME environment variable is not defined correctly.
  6. 人民币小写金额转大写金额
  7. C++ setw和setfill
  8. [html] 说说你对HTML元素的显示优先级的理解
  9. unity着色器和屏幕特效开发秘笈_Oculus研发分享:开发移动VR内容时应避免的PC渲染技术...
  10. 【面向对象】可变对象和不可变对象
  11. OpenGL画坐标轴指示图
  12. 大数据Hadoop集群中常用的任务调度框架
  13. 大数据学习系列之八----- Hadoop、Spark、HBase、Hive搭建环境遇到的错误以及解决方法
  14. 面试必会 HashMap抄底不再怕
  15. SitePoint播客#160:坐在树上的Adobe和HTML
  16. 跳房子(二维表上的搜索)
  17. Paypal联手信用卡Discover 打压Square和星巴克威风
  18. Win10下运行复活之秦殇前传
  19. 常用运行命令win10
  20. 简单计算器(C 语言实现)

热门文章

  1. ssm(spring,springmvc,mybatis)
  2. Java将XML类型的文本转换为JSON
  3. ShopNC【B2B2C】多用户电商平台系统,带WAP,微商城,圈子,门户
  4. WMI远程访问问题解决方法
  5. 9 Characteristics of Free Software Users
  6. oauth2.0 学习案例demo_Vue3教程:用 Vue3 开发小程序,这里有一份实际的代码案例!...
  7. Mybatis SQL片段
  8. 开放源代码_如何使用开放源代码开展业务:热门阅读
  9. oracle 编程必读_现在学习的编程语言,网络监视工具,备份解决方案以及更多必读内容
  10. Linux用户:您上一次使用Windows已有多长时间了?