现在,可以比较深入地对CWnd类的封装机制进行剖析了。

在建立窗口句柄映射方面,CWnd使用了一个未公开的类CHandleMap进行管理。使用CWnd及派生类创建窗口时,建立了句柄映射,在窗口销毁时删除映射。一个在MFC内部创建的CHandleMap对象管理所有CWnd实例与窗口句柄的映射,该对象通过一个内部使用的全局函数afxMapHWND()创建并取得。

6.3.1  使用操作映射的函数
CHandleMap主要包括3个成员函数:SetPermanent()(建立映射)、RemoveHandle()(删除映射)、LookupPermanent()(在映射中查找与指定句柄对应的对象指针)。下面代码是操作句柄映射的3个CWnd成员函数:

//根据指定的窗口句柄,在映射中查找对应的CWnd对象指针

CWnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd)

{                 //取得映射管理对象

CHandleMap* pMap = afxMapHWND();

CWnd* pWnd = NULL;

if (pMap != NULL)

{        //在映射中寻找窗口对象

pWnd = (CWnd*)pMap->LookupPermanent(hWnd);

ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);

}

return pWnd;

}

/*根据指定的窗口句柄,首先在映射中查找对应的CWnd对象指针,如果在映射中查找失败,则建立一个临时的CWnd对象,与句柄关联,再将对象指针返回。如果不锁定临时对象,在空闲时将自动删除之。*/

CWnd* PASCAL CWnd::FromHandle(HWND hWnd)

{

//参数为TRUE,如果映射对象没有创建,则自动创建

CHandleMap* pMap = afxMapHWND(TRUE);

ASSERT(pMap != NULL);

//先查找永久映射,失败则返回临时对象

CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);

#ifndef _AFX_NO_OCC_SUPPORT

pWnd->AttachControlSite(pMap);

#endif

ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);

return pWnd;

}

//建立当前CWnd对象与指定句柄的映射

BOOL CWnd::Attach(HWND hWndNew)

{

//如果建立映射,保证二者是一一对应的

ASSERT(m_hWnd == NULL);  // 对象是否已建立映射

ASSERT(FromHandlePermanent(hWndNew) == NULL);//句柄是否已建立映射

if (hWndNew == NULL)

return FALSE;

//参数为TRUE,如果映射对象没有创建,则自动创建

CHandleMap* pMap = afxMapHWND(TRUE);

ASSERT(pMap != NULL);

//建立映射关系

pMap->SetPermanent(m_hWnd = hWndNew, this);

#ifndef _AFX_NO_OCC_SUPPORT

AttachControlSite(pMap);

#endif

return TRUE;

}

CWnd myWnd;myWnd.Attach(hWnd);

这会建立起一个项目,这个项目是永久性的关联myWnd hWnd的一个映射。调用CWnd::FromHandle(hWnd) 将会返回一个指向myWnd的指针。当myWnd 被删除后,析构函数会自动的通过窗口函数DestroyWindow 销毁hWnd。如果你并不愿意这么做,那么hWnd 必须在myWnd 的对象被销毁之前同myWnd 相分离。(通常离开myWnd 定义的作用域中)
成员函数Detach 做这些工作。

myWnd.Detach();

//删除当前CWnd对象已建立的句柄映射

HWND CWnd::Detach()

{

HWND hWnd = m_hWnd;

if (hWnd != NULL)

{                 CHandleMap* pMap = afxMapHWND();

if (pMap != NULL)

//删除映射

pMap->RemoveHandle(m_hWnd);

m_hWnd = NULL;

}

#ifndef _AFX_NO_OCC_SUPPORT

m_pCtrlSite = NULL;

#endif

return hWnd;

}

6.3.2  CWnd如何处理窗口消息
在窗口消息处理方面,CWnd使用了窗口子类化和消息映射机制,关于消息映射的知识将在第9章详述,下面着重阐述CWnd是如何应用子类化处理窗口消息的。其实,在6.2节的示例中,CBaseWnd已经使用了与CWnd类似的子类化方法处理窗口消息。成员函数CBaseWnd::SubWindowClass()将窗口过程子类化为CBaseWnd::MyBaseWndProc(),在这个窗口过程中调用CBaseWnd::WindowProc()处理窗口消息。原始的窗口过程存入成员m_Super WndProc中,在默认处理中调用。

CWnd在窗口建立之初,使用AfxWndProc()子类化窗口,在AfxWndProc()中同样调用CWnd::WindowProc()处理窗口消息。不同的是,对于具体的消息处理,CWnd::WindowProc()使用消息映射机制,而不是调用固定的虚拟函数。原始的窗口过程存储在CWnd::m_pfnSuper中,在默认的处理过程CWnd::DefWindowProc()中调用。

下面是与消息处理相关的几个CWnd成员函数:

//用于子类化的窗口过程

LRESULT CALLBACK

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

// special message which identifies the window as using AfxWndProc

if (nMsg == WM_QUERYAFXWNDPROC)

return 1;

// all other messages route through message map

//通过句柄取得CWnd对象指针

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

ASSERT(pWnd != NULL);

ASSERT(pWnd->m_hWnd == hWnd);

return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

}

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,

WPARAM wParam = 0, LPARAM lParam = 0)

{  ……

//调用CWnd的虚拟成员函数处理消息

lResult = pWnd->WindowProc(nMsg, wParam, lParam);

……

return lResult;

}

//可在类向导中重载的虚拟函数

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

// OnWndMsg函数处理消息映射,如果在映射中没有发现当前消息的处理函数,则返回false

LRESULT lResult = 0;

if (!OnWndMsg(message, wParam, lParam, &lResult))

//如果当前消息没被映射处理,调用默认处理函数

lResult = DefWindowProc(message, wParam, lParam);

return lResult;

}

//默认的消息处理函数,同Default()

LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)

{

if (m_pfnSuper != NULL)

//调用原始的窗口过程

return::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

WNDPROC pfnWndProc;

if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)

//调用默认窗口处理过程

return::DefWindowProc(m_hWnd, nMsg, wParam, lParam);

else

return::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);

}

除以上几个相关成员函数外,CWnd类还定义了两个公共成员,SubclassWindow()和UnsubclassWindow(),前者用于动态地关联并使用AfxWndProc()子类化Windows窗口,后者执行相反过程。这意味着,可以随时将一个使用WIN32 API创建的窗口,通过调用SubclassWindow()关联到CWnd实例上。因为同时执行了子类化操作,所以此时就如同使用CWnd的Create()函数创建了这个Windows窗口一样。函数的代码如下:

BOOL CWnd::SubclassWindow(HWND hWnd)

{

//hWnd应该是一个没有与任何CWnd实例关联的Windows窗口

if (!Attach(hWnd)) //先建立关联,再子类化

return FALSE;

//在子类化前调用这个虚函数,给用户提供编程接口

PreSubclassWindow();

WNDPROC* lplpfn = GetSuperWndProcAddr();/*取得原始窗口函数。如果当前类尚未子类化窗口,返回NULL*/

//使用AfxWndProc()子类化该窗口

WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,

(DWORD)AfxGetAfxWndProc());//取得AfxWndProc()子类窗口函数

ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());

if (*lplpfn == NULL)

*lplpfn = oldWndProc; //保存原始窗口函数,在默认处理时调用

return TRUE;

}

HWND CWnd::UnsubclassWindow()

{

ASSERT(::IsWindow(m_hWnd));

//得到原始窗口过程

WNDPROC* lplpfn = GetSuperWndProcAddr();

//恢复窗口过程

SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)*lplpfn);

*lplpfn = NULL;

//分离窗口对象和窗口句柄

return Detach();

}

virtual void CWnd::PreSubclassWindow()

{//该虚拟函数在建立窗口时,子类化前被调用,在CWnd::SubclassWindow中也被调用

//它不执行任何操作,可以重载它,根据需要执行子类化。那样,子类化过程会成为原始的窗口过程,在消息默认处理时被调用

}

对窗口操作的封装是很好理解的,成员CWnd::m_hWnd存储映射的窗口句柄,不同的窗口操作成员函数一般封装同名的WIN32 API,函数通过m_hWnd调用同名API即可。下面列举几个相关的成员函数。

void CWnd::SetWindowText(LPCTSTR lpszString)

{ ASSERT(::IsWindow(m_hWnd)); ::SetWindowText(m_hWnd, lpszString); }

BOOL CWnd::IsIconic() const

{ ASSERT(::IsWindow(m_hWnd)); return ::IsIconic(m_hWnd); }

void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)

{ ASSERT(::IsWindow(m_hWnd)); ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint); }

BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)

{ ASSERT(::IsWindow(m_hWnd));

return ::SetWindowPos(m_hWnd, pWndInsertAfter->GetSafeHwnd(),     x, y, cx, cy, nFlags); }

CDC* CWnd::GetWindowDC()

{ ASSERT(::IsWindow(m_hWnd)); return CDC::FromHandle(::GetWindowDC(m_hWnd)); }

int CWnd::ReleaseDC(CDC* pDC)

{ ASSERT(::IsWindow(m_hWnd)); return ::ReleaseDC(m_hWnd, pDC->m_hDC); }

void CWnd::UpdateWindow()

{ ASSERT(::IsWindow(m_hWnd)); ::UpdateWindow(m_hWnd); }

关于CWnd的子窗口管理部分,将在第7章阐述

CWnd类与Windows窗口的关系-3、CWnd类如何封装Windows窗口相关推荐

  1. 快速读懂UML类图,搞懂类之间的6大关系,轻松绘制UML类图

    快速读懂UML类图,搞懂类之间的6大关系,轻松绘制UML类图 前言 一.UML类图简介 二.类之间的六大关系及UML类图 1.依赖关系及UML类图表示 2.泛化关系及UML类图表示 3.实现关系及UM ...

  2. 笔记35 笨办法学python练习42对象、类、从属关系和部件关系

    笔记35 笨办法学python练习42对象.类.从属关系和部件关系 一.类.对象与从属关系 这个练习是一个有点哲学意味的练习,讨论对象和类的关系,也就是从属关系.对象不就是下属于某个类的子类,或者个体 ...

  3. 【免杀前置课——Windows编程】五、窗口控件——什么是控件、Windolws 窗口两大类、Windows标准控件/通用控件、控件响应的接收、创建窗口制作不同控件

    窗口控件 WINDOW控件 什么是控件? 控件是常见的窗口上的交互元素.例如:一个按钮,一个复选框,一个列表框等.当控件的特定功能被触发后,会主动发送消息通知父窗口,父窗口可以通过发送消息给控件控制控 ...

  4. C++ Windows窗口程序:子窗口控件之按钮类button

    Windows窗口程序设计中,按钮.文本编辑框等控件都作为一个子窗口在WM_CREATE事件中创建的.其中按钮类button有多种类型和风格,常见的单选钮.复选钮.分组框也在此类中,见下表: 子窗口控 ...

  5. Window 窗口层次关系

    相信在Window 下面编程的很多兄弟们都不是很清楚Window 中窗口的层次关系是怎么样的,这个东西很久已经研究过一下,后来又忘记了,今天又一次遇到了这个问题,所以便整理一下.下面就说说Window ...

  6. C/C++编写Windows窗口应用程序(Win32程序),非黑漆漆的控制台窗口

     Windows应用程序是基于消息驱动的,任何线程只要注册窗口类都会有一个消息队列用于接收用户输入的消息和系统消息. 一. 窗口的创建流程 创建一个完整的窗口需要经过下面四个操作步骤: 设计一个窗口类 ...

  7. 解决内存泄漏更加清楚的认识到Java匿名类与外部类的关系

    1.事件起因 在做项目的时候,通过Android Studio的Memory Monitor窗口观察程序内存使用情况,发现当程序退出的时候,有一部分应该释放掉的内存没有释放掉,知道程序中应该有内存泄漏 ...

  8. BERT gated multi-window attention network for relation extraction 用于关系抽取的BERT门控多窗口注意力网络

    BERT gated multi-window attention network for relation extraction 用于关系抽取的BERT门控多窗口注意力网络 Abstract 实体关 ...

  9. java异常类子类和父类的关系

    java异常类子类和父类的关系 参考文章: (1)java异常类子类和父类的关系 (2)https://www.cnblogs.com/xiaoliangyuu/p/5596008.html 备忘一下 ...

最新文章

  1. mvn如何执行java代码
  2. 算法---------路径总和
  3. 2.1.1 进位计数制
  4. java11正式发布了,让java代码更完美
  5. linux 重启_四步见证linux系统重启过程,小心操作,防止后悔!
  6. HMAILSERVER集成WEB邮件系统(ROUNDCUBE WEBMAIL)
  7. 比较好用的mysql可视化工具-----pycharm连接mysql图文教程
  8. 基于python+qt5考研倒计时器
  9. PHP图片高清晰度无损压缩
  10. 独门秘籍 针式打印机换针小窍门
  11. 阿里医疗NLP实践与思考
  12. C语言中u8 u16 u32含义,有关stm32的问题,程序里面的u8、u16这些是什么意思啊
  13. 外来工作人员如何提取上海住房公积金
  14. 亚马逊云EC2助力5G产品测试
  15. 输入一个四位数将其加密后输出c语言,输入一个四位数,将其加密后输出.方法是将该数每一位的数字加9,然后除以10取余作为该位上的新数字,最后将千位上的数字和十...
  16. 前端:注册校验页面(html+css+javascript)
  17. 微信小程序云开发项目——多肉植物销售小程序
  18. python自学笔记+一个汇率计算PyQt实例
  19. Qt--QMetaObject
  20. Augustus操作指南

热门文章

  1. 图像处理-直方图均衡化
  2. 如何入门CTF夺旗赛
  3. 用计算机绘画教学反思,《电脑绘画》教学反思2篇
  4. a*算法matlab代码_MATLAB数学建模算法的代码模板大全
  5. Angular.js学习-入门
  6. python朋友圈切图代码
  7. bzoj1593 [Usaco2008 Feb]Hotel 旅馆(线段树)
  8. Linux下安装JDK7和TomCat7
  9. 大数据下的精准营销,媒介们将何去何从
  10. How to create and apply a patch with Git