如何“正确关闭 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() 函数(到CreateShow 这些对话框)。

基本思想是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 无模式对话框并修复资源泄漏”的主要内容,如果未能解决你的问题,请参考以下文章

Markdown公式用法大全

shell编程-如何定义函数如何调用函数如何调试shell

[精选] Mysql分表与分库如何拆分,如何设计,如何使用

四连问:前后端分离接口应该如何设计?如何保证安全?如何签名?如何防重?

集合元素如何添加?如何删除?如何遍历?

2021-10-16 如何如何如何。