Как «правильно закрыть MFC Modeless Dialogs и устранить утечку ресурсов» - PullRequest
0 голосов
/ 07 июня 2019

Я создал приложение MFC с Base Dialog (производным от CDialog класса), Setting Dialog (производным от CBaseDlg и App Dialog (также производным от CBaseDlg). ЗатемЯ создал класс с именем CScrMng (он же Screen Manager), который содержит функцию ShowDialog() (для Create и Show этих диалогов).

Основная идея - ScrMng будет управлять всемимои немодальные диалоги, и в любое время, когда я хочу открыть диалоговое окно, мне просто нужно CScrMng::ShowDialog() в BaseDlg.cpp, и диалоговое окно отобразится.

Этот подход вызвал утечку ресурсов здесь и там.Я провел небольшое исследование о переопределении PostNcDestroy(), но у меня нет четкого представления о том, где его вызвать.

Какую функцию я должен использовать, чтобы правильно закрыть эти немодальные диалоги?

Я хочу открыть Setting Dialog из Base 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: SettingDlg.cpp

void CSettingDlg::PostNcDestroy()
{
    CMainDlg::PostNcDestroy();
}

void CSettingDlg::OnBnClickedSettingcancel()
{
    DestroyWindow(); //Using destroyWindow rather than EndDialog();
}

1 Ответ

2 голосов
/ 07 июня 2019

Закрыть и удалить немодальные диалоги.
Правильный способ: переопределить PostNcDestroy, OnOk () и OnCancel () для немодальных диалогов

void CBaseDlg::PostNcDestroy()
{
    CDialog::PostNcDestroy();
    delete this;
}

.

void CBaseDlg::OnOk()
{
   if(!UpdateData(TRUE))
       return;
    DestroyWindow();
}

.

void CBaseDlg::OnCancel()
{
    DestroyWindow();
}
...