Warning: calling DestroyWindow in CWnd::~CWnd OnDestroy or PostNcDestroy in derived class will not be called


或是


Detected memory leaks! Dumping objects -> f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(4500) : {495} client block at 0x002DF7F8, subtype c0, 56 bytes long. a CObject object at $002DF7F8, 56 bytes long {487} client block at 0x002DE208, subtype c0, 184 bytes long. a CDialog1 object at $002DE208, 184 bytes long {481} client block at 0x002DA550, subtype c0, 68 bytes long. a CThread2 object at $002DA550, 68 bytes long Object dump complete. 程序“[15524] MPF.exe: 本机”已退出,返回值为 2 (0x2)。


之所以出现这样的错误呢,原因就是我们没有删除我们自己new的窗口对象,这里要注意窗口对象与窗口句柄,窗口对象是一个c++对象,窗口句柄是一个系统资源。两者大家要分开,窗口句柄是窗口对象的一个成员变量,保存了我们创建窗口的时候返回的句柄值。ok,言归正传,平时我们常常都是使用mfc向导生成的基于文档或是对话框的窗口,而且一般我们初学者都是使用的单线程,而这样的窗口在销毁的时候执行了自动清理,所以,我们并没有管关于窗口对象的销毁问题,所以也没有什么问题,即使我们需要一个除了主窗口之外的另外一个窗口,但是另外一个窗口对象都是作为一个c++对象嵌入的主窗口类中使用的,这样任然是一个单线程的程序。但是如果我们需要另外一个线程建立另外一个窗口来执行任务的时候,如果大家想了解mfc多线程,在msdn中查找AfxBeginThread函数,这里不做详细阐述。继续,当我们在另外一个线程的InitInstance函数中创建一个窗口的时候,这里我们用了一次new,创建了一个窗口对象指针,如下图:


BOOL CThread2::InitInstance()
{
	// TODO: 在此执行任意逐线程初始化
	CDialog1 *d=new CDialog1;
	d->Create(IDD_DIALOG1);
	this->m_pMainWnd=d;
	d->ShowWindow(SW_SHOW);
	return TRUE;
}
那么如果我们在什么地方销毁它呢,如果不销毁,就会出现文章开头出现的错误警告或是内存泄露。mfc专门有这样一个虚函数,我们可以重载来删除这个指针,这个虚函数就是CWnd::PostNcDestroy,但是注意的时,这个函数是销毁c++窗口对象,在此之前,我们还应该调用DestroyWindow来销毁窗口句柄,释放窗口资源,这个和SDK是一样的。然后再在这个虚函数里delete this。这样就可以解决问题了


//


下面再补充一点,对于上面所阐述,有一些需要注意的,首先一点就是这条规则对于模式对话框除外,因为模式对话框不需要new,直接创建一个栈中对象,如下:


CMPFDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();


因为程序要等到它返回之后才会继续执行,所以这个局部对象直到完成任务也不会超出这个函数体,但是对于一般窗口和非模式对话框,就必须要创建一个堆中对象,并把指针保存在线程的m_pMainWnd成员变量中。所以又new就应该有销毁的操作。


还要注意一点的,就是一定要注意,上面说的在虚函数中CWnd::PostNcDestroy删除窗口对象,只是相对于线程的主窗口,如果是嵌入的窗口对象,如我们常常在对话框中用到的控件CStatic, CEdit, CListBox对象就不属于上面所说的,如果是指针,就在关闭函数中直接删除就可以了。


下面还有最后一点,就是在对话框中,我们点击确定或是取消,如果是模式对话框,没有问题,但是如果再在非模式对话框,我们就要像下面那样


void CDlg::OnCancel()
{
	// TODO: 在此添加专用代码和/或调用基类
	this->DestroyWindow();
	//CDialogEx::OnCancel();
}

void CDlg::OnOK()
{
	// TODO: 在此添加专用代码和/或调用基类
	this->DestroyWindow();
	//CDialogEx::OnOK();
}
我们要把原来的引起来,因为这是针对模式对话框的,如果我们的是非模式对话框,那么就要调用DestroyWindow函数来销毁窗口。