如何“正确关闭 MFC 无模式对话框并修复资源泄漏”
Posted
技术标签:
【中文标题】如何“正确关闭 MFC 无模式对话框并修复资源泄漏”【英文标题】:How to 'properly close MFC Modeless Dialogs and fixing resource leak' 【发布时间】:2019-06-07 05:27:34 【问题描述】:我创建了一个 MFC 应用程序,其中包含一个 Base Dialog
(派生自 CDialog
类)、一个 Setting Dialog
(派生自 CBaseDlg
和一个 App Dialog
(也派生自 CBaseDlg
)。然后我创建了一个名为CScrMng
(又名屏幕管理器)的类,它包含ShowDialog()
函数(到Create
和Show
这些对话框)。
基本思想是ScrMng
将管理我所有的无模式对话框,并且任何时候我想打开一个对话框,我只需要在BaseDlg.cpp
中输入CScrMng::ShowDialog()
,对话框就会显示出来。
这种方法导致资源到处泄漏。我对覆盖 PostNcDestroy()
进行了一些研究,但我不清楚在哪里调用它。
我应该使用什么函数来正确关闭这些无模式对话框?
我想从Base Dialog
打开Setting Dialog
,然后当我点击Cancel
按钮时,它应该让我回到Base Dialog
屏幕,以便我可以打开另一个对话框。
现在我正在使用EndDialog()
。我知道这是错误的,但是调用DestroyWindow()
会立即退出程序,这不是我想要的。
源代码
MFCApplication.cpp
#include...
BEGIN_MESSAGE_MAP(CMFCApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
CMFCApp::CMFCApp()
// support Restart Manager
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
CMFCApp theApp;
BOOL CMFCApp::InitInstance()
...
CWinApp::InitInstance();
CShellManager *pShellManager = new CShellManager;
CScrMng::GetInstance()->ShowDialog(IDD_MAINDLG);
SetRegistryKey(_T("Local Applications"));
if (pShellManager != NULL)
delete pShellManager;
return TRUE;
CScrMng.cpp
#include...
CScrMng* CScrMng::m_pInstance = NULL;
CScrMng* CScrMng::GetInstance()
if (m_pInstance == NULL)
m_pInstance = new CScrMng();
return m_pInstance;
CScrMng::CScrMng()
void CScrMng::ShowDialog(int ID)
CMainDlg* m_pDlg = NULL;
switch (ID)
case IDD_MAINDLG:
m_pDlg = new CMainDlg();
theApp.m_pMainWnd = m_pDlg;
m_pDlg->Create(IDD_MAINDLG);
m_pDlg->ShowWindow(SW_SHOW);
m_pDlg->UpdateWindow();
break;
case ...
break;
case IDD_SETTINGDLG:
m_pDlg = new CSettingDlg();
m_pDlg->Create(ID,NULL);
m_pDlg->ShowWindow(SW_SHOW);
m_pDlg->UpdateWindow();
break;
CMainDlg.cpp
#include...
CMainDlg::CMainDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMainDlg::IDD, pParent) m_hIcon = AfxGetApp()-> LoadIcon(IDR_MAINFRAME);
void CMainDlg::DoDataExchange(CDataExchange* pDX) ...
void CMainDlg::PostNcDestroy() //Added these
CDialog::PostNcDestroy();
delete this;
BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
...
END_MESSAGE_MAP()
BOOL CMainDlg::OnInitDialog()
CDialog::OnInitDialog();
SetIcon(m_hIcon, FALSE);
return TRUE;
void CMainDlg::OnPaint() ...
void CMainDlg::OnBnClickedOpenAppdlg()
CScrMng::GetInstance()->ShowDialog(IDD_APPDLG);
void CMainDlg::OnBnClickedOpenSettingdlg()
CScrMng::GetInstance()->ShowDialog(IDD_SETTINGDLG);
void CMainDlg::OnBnClickedExit()
DestroyWindow(); //replaced CDialog::OnCancel() with this.
更新:修改SettingDlg.cpp中的代码后,遇到Debug Assertion Failed!
问题:
void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)
ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL)); //Breakpoint triggered
if (m_pCtrlSite == NULL)
::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint);
else
m_pCtrlSite->MoveWindow(x, y, nWidth, nHeight);
这是我在 .cpp 文件中所做的更改: 设置Dlg.cpp
void CSettingDlg::PostNcDestroy()
CMainDlg::PostNcDestroy();
void CSettingDlg::OnBnClickedSettingcancel()
DestroyWindow(); //Using destroyWindow rather than EndDialog();
【问题讨论】:
你不调用PostNCDestroy
,框架调用它。阅读this 和this
@Jabberwocky 你能给我一些关于我应该如何关闭我的应用程序中的无模式对话框的建议吗?
一种常用于无模式对话框的方法是创建它,然后显示或隐藏它,而不是创建和删除它。所以无模式对话框被创建一次,然后用户看到的是无模式对话框要么显示要么不显示。另一方面,这可能只是对无模式对话框的练习和调查,因此请随意忽略此评论。
函数CScrMng::ShowDialog()
中的这行源代码theApp.m_pMainWnd = m_pBaseDlg;
不应该存在。 theApp.m_pMainWnd
应该由框架自动填充,您不应更改它,因为这是应用程序的主窗口。
@RichardChambers 这些代码行不是我的。它来自我公司的 MFC 讲师(我是实习生)。他为我编写了这个代码。那么,如果你能教我如何修复上面的代码而不用做太多改动呢?
【参考方案1】:
关闭和删除无模式对话框。 正确的方法是:为无模式对话框覆盖 PostNcDestroy、OnOk() 和 OnCancel()
void CBaseDlg::PostNcDestroy()
CDialog::PostNcDestroy();
delete this;
。
void CBaseDlg::OnOk()
if(!UpdateData(TRUE))
return;
DestroyWindow();
.
void CBaseDlg::OnCancel()
DestroyWindow();
【讨论】:
所以当我想关闭一个对话框时,我应该调用CBaseDlg::OnCancel()
方法,对吧?
可以,也可以直接调用 DestroyWindow()。如果您有任何要检查和更新的成员变量,请使用 OnOk()。
我刚遇到一个新的错误,你能帮我解释一下吗? (以上编辑)
向我们展示调用堆栈。如果你销毁一个窗口,这个窗口就不再有效,你不想移动一个被销毁的窗口。
@David 是的,MFC 还活着 ;)) 我仍然不明白为什么主流进入 :NET 世界。以上是关于如何“正确关闭 MFC 无模式对话框并修复资源泄漏”的主要内容,如果未能解决你的问题,请参考以下文章