DestroyWindow

销毁窗口

一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。 
删除窗口最直接方法是调用CWnd::DestroyWindow或::DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd::DestroyWindow来删除主框架窗口,当用户在对话框内按了OK或Cancel按钮时,MFC会自动调用CWnd::DestroyWindow来删除对话框及其控件。 
窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。 
对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习C++编程时,对new操作符的使用往往不太踏实,因为用new在堆中创建对象,就不能忘记用delete删除对象。读者在学习MFC的例程时,可能会产生这样的疑问,为什么有些程序用new创建了一个窗口对象,却未显式的用delete来删除它呢?问题的答案就是有些MFC窗口对象具有自动清除的功能。 
如前面讲述非模态对话框时所提到的,当调用CWnd::DestroyWindow或::DestroyWindow删除一个窗口时,被删除窗口的PostNcDestroy成员函数会被调用。缺省的PostNcDestroy什么也不干,但有些MFC窗口类会覆盖该函数并在新版本的PostNcDestroy中调用delete this来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用new操作符创建在堆中的,但程序员不必操心用delete操作符去删除它们,因为一旦调用DestroyWindow删除窗口,对应的窗口对象也会紧接着被删除。
不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。 
所有标准的Windows控件类。 
1. 从CWnd类直接派生出来的子窗口对象(如用户定制的控件)。 
2. 切分窗口类CSplitterWnd。 
3. 缺省的控制条类(包括工具条、状态条和对话条)。 
4. 模态对话框类。

具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。 
1. 主框架窗口类(直接或间接从CFrameWnd类派生)。 
2. 视图类(直接或间接从CView类派生)。

读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。 
综上所述,对于MFC窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用DestroyWindow来删除窗口对象封装的窗口,也不必显式地用delete操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的,MFC的运行机制就可以保证窗口对象的彻底删除。 
如果需要手工删除窗口对象,则应该先调用相应的函数(如CWnd::DestroyWindow)删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用delete来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用CWnd::DestroyWindow即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用delete操作符来删除窗口对象.

考虑单窗口情况: 
假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create。则销毁窗口的调用次序: 
1. 手工调用pWnd->DestroyWindow(); 
2. DestroyWindow会发送WM_DESTROY; 
3. WM_DESTROY对应的消息处理函数是OnDestroy(); 
4. DestroyWindow会发送WM_NCDESTROY; 
5. WM_NCDESTROY对应的消息处理函数是OnNcDestroy; 
6. OnNcDestroy最后会调用PostNcDestroy; 
7. PostNcDestroy经常被用户重载以提供释放内存操作。例如可以使用delete this; 
通过这种方式,窗口对象对应的窗口和窗口对象本身都被释放了。 
如果含有子窗口: 
如果含有子窗口,则调用父窗口的DestroyWindow时,它会向子窗口发送WM_DESTROY和WM_NCDESTROY消息。 
具体调用顺序参考下文的例子。 
DestroyWindow对delete的影响: 
应该说前者对后者并没有什么影响。但经常在DestroyWindow间接导致执行的PostNcDestroy中delete窗口对象指针,即delete this。 
CView::PostNcDestroy中唯一的操作就是delete this;CframeWnd::PostNcDestory也是如此。而默认的CWnd::PostNcDestroy是空操作,CDialog中也没有对其进行重载,即也是空。 
delete对Destroy的影响: 
delete会导致析构函数。CWnd的析构函数中有对DestroyWindow的调用,但必须保证: 
m_hWnd != NULL && 
this != (CWnd*) &wndTop &&this != (CWnd*)&wndBottom && 
this != (CWnd*)&wndTopMost &&this != (CWnd*)&wndNoTopMost。 
Cdialog的析构函数中也有对DestroyWindow的调用,但条件比较松,只需要m_hWnd != NULL。另外Cdialog::DoModal也会调用DestroyWindow。 
CFrameWnd的OnClose中会调用DestroyWindow,但其析构中不会调用DestroyWindow。 
CView的析构也不会调用DestroyWindow。 
一个SDI程序的销毁过程 
有CMainFrame类、CMyView类。并且CMyView有两个子窗口CMyDlg和CmyWnd的实例。 
点击退出按钮,CMainFrame会收到WM_CLOSE消息。CFrameWnd(CMainFrame的父类)间接会调用CWnd::DestroyWindow;它首先向CMyView发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyDlg发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyWnd发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数。 
具体的执行顺序是: 
1. 调用CMainFrame::DestroyWindow 
2. CFrameWnd::OnDestroy 
3. CMyView::OnDestroy 
4. CmyWnd::OnDestroy 
5. CmyDlg::OnDestroy 
6. CmyWnd::PostNcDestroy 
7. CmyWnd的析构 
8. CmyDlg::OnDestroy 
9. CmyDlg的析构 
10. CMyView::PostNcDestroy 
11. CmyView的析构 
12. CMainFrame的析构 
13. CMainFrame::DestroyWindow退出 
上面情况是假设我们在CmyWnd和CmyDlg的PostNcDestroy中添加了delete this。如果没有添加,则7,10不会执行。 
因为CView::PostNcDestroy中调用了delete this,所以然后会执行CMyView的析构操作。因为CframeWnd::PostNcDestroy中调用了delete this,所以最后执行CMainFrame的析构操作。 
如果自己的CmyDlg和CmyWnd在PostNcDestroy中有delete this;则二者会被析构。否则内存泄漏。当然delete也可以放在CMyView的析构中做,只是不够OO。 
总结 
可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过DestroyWindow。这是比较好的方法,因为最后MFC会自动相应WM_CLOSE导致CframWnd::DestroyWindow被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在PostNcDestroy中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以delete this可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。 
另一种是delete。Delete一个窗口对象指针有的窗口类(如CWnd,Cdialog)会间接调用DestroyWindow,有的窗口类(如CView,CframeWn)不会调用DestroyWindow。所以要小心应对。 
二者是相互调用的,很繁琐。

时间: 2024-10-12 21:54:26

DestroyWindow的相关文章

窗口 namedWindow(), destroyWindow(), destroyAllWindows()[OpenCV 笔记6]

void namedWindow(const string& winname, int flags=WINDOW_AUTOSIZE); 创建一个窗口.imshow直接指定窗口名,可以省去此函数,但如果显示图像之前需要其他窗口操作时,需要调用此函数. winname 窗口标识符/窗口名称 flags 窗口标志位,可为如下几种值 WINDOW_NORMAL (CV_WINDOW_NORMAL in OpenCV2) 用户可以改变窗口的大小: WINDOW_AUTOSIZE (CV_WINDOW_AU

解决:AppMsg - Warning: calling DestroyWindow in CWnd::~CWnd; OnDestroy or PostNcDestroy in derived class will not be called

类似的还有:AppMsg - Warning: Destroying non-NULL m_pMainWnd(这是因为你既没有自己delete,也没有调用DestroyWindow) 首先解决第一个,直接列代码: 1 class SCCApp : public CWinApp 2 3 class CMainWindow : public CFrameWnd 4 5 BOOL SCCApp::InitInstance() 6 { 7 m_pMainWnd = new CMainWindow; 8

DestroyWindow函数注意事项

最近遇到这样一个问题:将一个窗口句柄以参数的形式传递给一个线程,在线程中使用完之后要将窗口销毁,调用DestroyWindow销毁窗口是返回false,GetLastError的结果为5:拒绝访问,而在线程外则是可以销毁的. 查阅msdn,解释如下: A thread cannot use DestroyWindow to destroy a window created by a different thread. 此外,还有以下需要注意的点: If the specified window

WM_CLOSE、WM_DESTROY、WM_QUIT学习总结(点击关闭按钮会触发WM_CLOSE消息,DestroyWindow API会触发WM_DESTROY和WM_NCDESTROY消息,MSDN上写的很清楚)

WM_CLOSE:关闭应用程序窗口 WM_DESTROY:关闭应用程序 WM_QUIT:关闭消息循环 只有关闭了消息循环,应用程序的进程才真正退出(在任务管理器里消失). win32应用程序的完整退出过程:点击窗口右上角的关闭按钮,发送WM_CLOSE消息.此消息处理中调用DestroyWindow函数,发送WM_DESTROY消息.此消息处理中调用PostQuitMessage(0)函数,发送WM_QUIT消息到消息队列中.GetMessage捕获到WM_QUIT,返回0,退出循环(应用程序真

MFC DestroyWindow[转]

考虑单窗口情况: 假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create.则销毁窗口的调用次序: 1. 手工调用pWnd->DestroyWindow(): 2. DestroyWindow会发送WM_DESTROY: 3. WM_DESTROY对应的消息处理函数是OnDestroy(): 4. DestroyWindow会发送WM_NCDESTROY: 5. WM_NCDESTROY对应的消息处理函数是OnNcDestroy: 6. OnNcDestroy最后会调用Pos

python计算机视觉

机器学习的一个应用方向,是如何让机器去理解图像.包括对图像里物体的识别,跟踪和测量. 能做什么——无人驾驶汽车.人脸识别.车牌识别手势识别(游戏方向) PIL静态的库 OpenCV 动态的库 import pandas as pd import numpy as np from PIL import Image train=pd.read_csv('train.csv') for ind,row in train.iloc[1:10].iterrows(): #第一行是label i=row[0

outdated: 27.Shadows

这次的代码没有按照辅导代码中的来. VK_LEFT/VK_RIGHT/VK_UP/VK_DOWN控制长方体旋转,W/S/A/D/Q/E控制球移动,小键盘的8/5/4/6/7/9控制长方体移动,I/K/J/L/U/O控制光源移动. 在InitGLObject()函数中使用到的readObject()函数.setConnectivity()函数和calculatePlane()函数在3object.h中所写. readObject()函数在.txt文件中读取物体顶点数.顶点坐标.平面数.vertex

Windows应用程序

  Windows应用程序是由一系列的消息处理 代码来实现的.这和传统的过程式编程方法很不一样,编程者只能够 预测用户所利用应用程序用户界面对象所进行的操作以及为这些操作 编写处理代码,却不可以这些操作在什么时候发生或者是以什么顺序 来发生,也就是说,我们不可能知道什么消息会在什么时候以什么顺 序来临. Windows应用程序基本流程:   Windows API:(windowsAppliacation Programming Interface. Windows 应用程序编程接口), API

NativeWindow_01_CreateWindow(Ex)_VC6

1. 1 #include <windows.h> 2 3 4 LRESULT CALLBACK ProcWindow(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 5 void QuitWindow(); 6 7 HINSTANCE g_hInstance = 0; 8 HWND g_hWnd = 0; 9 10 //////////// //////////// //////////// //////////// ////////