这篇讲得很清楚,就转过来了,原文如下:

因项目需要,最近在学习MFC,下午在一篇教程中提到了临时窗口、永久窗口,作者让读者自行查阅MSDN,了解临时窗口与永久窗口的概念,出于好奇,出于方便,直接百度一下,看到了几篇题为解释临时窗口与永久窗口的文章,随后网友在论坛中附上了MSDN中的原文,仔细翻译一下,发现网上查到的几篇文章,对这个概念的解释是有问题和不足的。 
首先我要说明一点的就是窗口是没有临时与永久之分的,窗口是一种资源,随着CreateWindow的调用而产生,随着DestroyWindow的调用而被销毁(资源所占据的内存被回收),在windows程序设计中通过句柄(这里可以将句柄看成是C++中的指针)来标识资源,通过句柄来操作窗口,控制窗口,也就是说真正表示窗口的是窗口句柄HWND类型变量,为了方便开发、提高开发效率,MFC帮我们封装了与窗口有关的操作(就是对win32 API的间接调用而已),并将标识窗口的句柄的HWND的变量,封装到了CWnd类中,作为其数据成员,所以我们在调用MFC类中与Win32同名的成员函数的时候,就少去了句柄这一参数,是因为CWnd类维护了这一HWND类型的变量,归根结底MFC的成员函数还是调用了win32函数,说这么多的原因主要是想说明MFC 中的C++窗口对象只是我们操作窗口的一层封装,一种手段,MSDN中说的是临时窗口对象的概念,而不是临时窗口与永久窗口,因为根本就没有这两个概念(创建窗口之后,只要不直接、间接调用destroywindow那么窗口就一直占据内存资源)
如上所述,在MFC中,都是以C++对象来操作窗口,而窗口是用句柄来标识的,这样就需要将窗口和C++对象关联起来。通过C++对象的成员变量m_hWnd(HWND类型)来建立这种联系.
首先解释一下何为永久窗口对象:
在MFC中建立一个窗口的步骤:
CWnd win;
win.Create();(篇幅所限此处略去Create函数的参数,create函数负责创建窗口并将其关联到win这个对象上,说白了就是给m_hWnd这个成员变量赋值);
此时win这个对象就是永久窗口对象,接着调用这段代码 CWnd* pWnd = CWnd::FromHandle(win.m_hWnd)返回的就是指向win这个永久窗口对象的指针,如果我们接着做如下操作:HWND hwnd = win.Detach();
接着再调用上面的代码:pWnd = CWnd::FromHandle(hwnd),此时pWnd指向的对象就是临时窗口对象
以下是MSDN中对CWnd::FromHandle函数返回值的说明

Returns a pointer to a CWnd object when given a handle to a window. If a CWnd object is not attached to the handle, a temporary CWnd object is created and attached.

The pointer may be temporary and should not be stored for later use

对于临时窗口对象,windows程序会在线程出于空闲时间的时候(消息队列为空),自动调用CWinThread::DeleteTempMap()函数把临时对象从他关联的窗口句柄上卸载下来,取消这种关联,并删除这个临时窗口对象,但注意,这个窗口句柄还是存在的,因为窗口这个资源并没有销毁,销毁的只是封装窗口句柄的这个临时的C++对象,所以我们不能存储这个对象的指针,在其他地方调用,因为它随时会被回收,变成无效指针,同样在不同的线程中也是不能传递C++窗口对象的,此处 不管该C++窗口对象是不是临时的,如果我们要在其它地方操作这个窗口,应该存储代表窗口的句柄,而非C++对象。

以下是MSDN原文对临时窗口对象与永久窗口对象的说明:

TN003: Mapping of Windows Handles to Objects
This note describes the MFC routines that support mapping Windows object handles to C++ objects.

The Problem

Windows objects are normally represented by HANDLEs. The MFC classes wrap Windows object handles with C++ objects. The handle wrapping functions of the MFC class library provide a way to find the C++ object that is wrapping the Windows object with a particular handle. There are times when a Windows object does not have a C++ wrapper object, however, and at these times a temporary object is created to act as the C++ wrapper.

The Windows objects that use handle maps are: 
HWND (CWnd and CWnd-derived classes)
HDC (CDC and CDC-derived classes)
HMENU (CMenu)
HPEN (CGdiObject)
HBRUSH (CGdiObject)
HFONT (CGdiObject)
HBITMAP (CGdiObject)
HPALETTE (CGdiObject)
HRGN (CGdiObject)
 HIMAGELIST (CImageList)
SOCKET (CSocket)  
Given a handle to any of these objects, you can find the MFC object that wraps the handle by calling the static member function FromHandle. For example, given an HWND called hWnd:

CWnd::FromHandle(hWnd)

will return a pointer to the CWnd that wraps the hWnd. If that hWnd does not have a specific wrapper object, then a temporary CWnd is created to wrap the hWnd. This makes it possible to get a valid C++ object from any handle.

Once you have a wrapper object, you can get to its handle through a public member variable. In the case of an CWnd, m_hWnd contains the HWND for that object.

Attaching Handles to MFC Objects

Given a newly created handle-wrapper object and a handle to a Windows object, you can associate the two by calling Attach. For example:

CWnd myWnd;
myWnd.Attach(hWnd);

This makes an entry in the permanent map associating myWnd and hWnd. Calling CWnd::FromHandle(hWnd) will now return a pointer to myWnd. When myWnd is deleted, the destructor will automatically destroy the hWnd by calling the Windows DestroyWindow function. If this is not desired, the hWnd must be detached from myWnd before the myWnd object is destroyed (normally when leaving the scope at which myWnd was defined). The Detach member function does this.

myWnd.Detach();

More About Temporary Objects

Temporary objects are created whenever FromHandle is given a handle that does not already have a wrapper object. These temporary objects are detached from their handle and deleted by the DeleteTempMap functions. The default OnIdle processing in CWinThread automatically calls DeleteTempMap for each class that supports temporary handle maps. This means that you cannot assume a pointer to a temporary object will be valid past the point of exit from the function where the pointer was obtained, as the temporary object will be deleted during the Windows message-loop idle time.

Wrapper Objects and Multiple Threads

Both temporary and permanent objects are maintained on a per-thread basis. That is, one thread cannot access another threads C++ wrapper objects, regardless of whether it is temporary or permanent. As stated above, temporary objects are deleted when the thread which that temporary object belongs enters OnIdle.

To pass these objects from one thread to another, always send them as their native HANDLE type. Passing a C++ wrapper object from one thread to another will often result in unexpected results

MFC永久窗口对象与临时窗口对象的更多相关文章

  1. java:Hibernate框架1(环境搭建,Hibernate.cfg.xml中属性含义,Hibernate常用API对象,HibernteUitl,对象生命周期图,数据对象的三种状态,增删查改)

    1.环境搭建: 三个准备+7个步骤 准备1:新建项目并添加hibernate依赖的jar文件  准备2:在classpath下(src目录下)新建hibernate的配置文件:hibernate.cf ...

  2. MFC程序的启动过程——先全局对象theApp(第一入口),后WinMain(真正入口),会引爆pApp->InitInstance从而创建窗口(程序员入口)

    原文出自:http://blog.csdn.net/yuvmen/article/details/5877271 了解MFC程序的启动过程,对于初学者来讲,了学习MFC很有帮助:对于不常用VC的人来说 ...

  3. CWnd和HWND的区别(hWnd只是CWnd对象的一个成员变量,代表与这个对象绑定的窗口)

            所有控件类都是CWnd类的派生类,CWnd的所有成员函数在控件类中都可以使用.在MFC中,CWnd类是一个很重要的类,它封装了Windows的窗口句柄HWND.在Windows编程中, ...

  4. C#在父窗口中调用子窗口的过程(无法访问已释放的对象)异常,不存在从对象类型System.Windows.Forms.DateTimePicker到已知的托管提供程序本机类型的映射。

    一:C#在父窗口中调用子窗口的过程(无法访问已释放的对象)异常 其实,这个问题与C#的垃圾回收有关.垃圾回收器管 理所有的托管对象,所有需要托管数据的.NET语言(包括 C#)都受运行库的 垃圾回收器 ...

  5. (转)C#在父窗口中调用子窗口的过程(无法访问已释放的对象)

    C#在父窗口中调用子窗口的过程: 1. 创建子窗口对象 2. 显示子窗口对象   笔者的程序中,主窗体MainFrm通过菜单调用子窗口ChildFrm.在窗体中定义了子窗口对象,然后在菜单项点击事件中 ...

  6. C#中关闭子窗口而不释放子窗口对象的方法

    1 在主窗口中实例化子窗口 在主窗口中实例化子窗口,而不是在按钮中实例化子窗口对象. Form2 f2 = new Form2(); 2 通过按钮来显示主窗口 在按钮中需要实现的是窗口的显示 priv ...

  7. C#关闭子窗口而不释放子窗口对象的问题解决

    在网上找来一些方式,感觉还都不错,下面给出方式: 在线扫描相机的调试过程中,需要开辟调试界面来进行位置的配置.调试结束后,一种常用的方式是将调试参数保存并在下次启动时加载.另一种简单方式是直接使用该参 ...

  8. 从普通函数到对象方法 ------Windows窗口过程的面向对象封装

    原文地址:http://blog.csdn.net/linzhengqun/article/details/1451088 从普通函数到对象方法 ------Windows窗口过程的面向对象封装 开始 ...

  9. js中的window对象:打开窗口

    ~~ window.open():打开一个窗口 里面需要放三个参数: 1.打开窗口(网页)的位置: 2.打开的方式(自身页面,新开页面): 其中_blank新开一个窗口 3.打开网页的属性: wind ...

随机推荐

  1. Keil中如何消除UNCALLED SEGMENT,IGNORED FOR OVERLAY PROCESS警告

    在Keil C中,如果没有显式调用到定义过的函数,就会出现这样的的警告.当出现这样的警告时,可以不用管,因为不影响其它部分.但是,我们知道,即使没有调用这个函数,Keil仍然把它编译连接进整个程序,不 ...

  2. C# Exception类

    一.异常类 1.在C#中所有的异常都是使用一个异常类型的示例对象表示的,这些异常类型都是继承自System.Exception类型,或者直接使用System.Exception类型的实例对象: 2.在 ...

  3. 关于ArcGIS Rest API

    ArcGIS Rest API: 9.3版本: http://resources.esri.com/help/9.3/arcgisserver/apis/rest/index.html 10版本:ht ...

  4. yii安装配置

    Yii 的安装由如下两步组成: 从 yiiframework.com 下载 Yii 框架. 将 Yii 压缩包解压至一个 Web 可访问的目录. 提示: 安装在 Web 目录不是必须的,每个 Yii ...

  5. Linux系统编程(4)——文件与IO之ioctl函数

    ioctl是设备驱动程序中对设备的I/O通道进行管理的函数.所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率.马达的转速等等.它的参数个数如下:int ioctl(int ...

  6. bzoj1643 [Usaco2007 Oct]Bessie's Secret Pasture 贝茜的秘密草坪

    Description 农夫约翰已经从他的牧场中取得了数不清块数的正方形草皮,草皮的边长总是整数(有时农夫约翰割草皮的刀法不合适,甚至切出了边长为0的正方形草皮),他已经把草皮放在了一个奶牛贝茜已经知 ...

  7. LeeCode-Delete Node in a Linked List

    Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...

  8. pktgen使用详细教程

    网上有很多讲解pktgen的文章,但总是不够全面细致,看完之后自己还是不会写pktgen测试脚本,为此本文对pktgen进行详细的阐述,让大家看完本文后能够自己动手写pktgen shell. 1.p ...

  9. java Socket使用注意

    Socket s = new Socket(ia, port); BufferedOutputStream bufOut = new BufferedOutputStream(s.getOutputS ...

  10. Linux系统下定时上传文件至FTP服务器脚本

    环境:Red Hat Enterprise Linux Server release 6.4 需求:需要将Oracle数据库的定时备份上传至FTP服务器 1.干货,用户名:oracle,数据库名称:X ...