Нарушение прав доступа при вызове деструктора из CMainFrame после завершения приложения mfc

Я работаю над приложением mfc для встроенного компакта. На данный момент я пытаюсь добавить диалог, который предлагает закрыть программу. Моя проблема в том, что я получаю нарушение прав доступа при вызове деструктора из моего CMainFrame. Чтобы было понятнее, сначала кусок кода. Это начальная точка моего приложения:

SWinApp.h

class SWinApp : public CWinApp
{
public:
    SWinApp();
    ~SWinApp(){};
public:
    virtual BOOL InitInstance();

protected:
    DECLARE_MESSAGE_MAP()
};

SWinApp.cpp

SWinApp::SWinApp():CWinApp()
{
}

BOOL SWinApp::InitInstance()
{
    CRuntimeClass* pRuntimeClass = RUNTIME_CLASS( SMainFrame );
    CObject* pObject = pRuntimeClass->CreateObject();
    ASSERT( pObject->IsKindOf( RUNTIME_CLASS( SMainFrame ) ) );
    m_pMainWnd = (SMainFrame*)pObject;
    m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
    return FALSE; // this is the next executed line after the access violation
}

BEGIN_MESSAGE_MAP(SWinApp, CWinApp)
END_MESSAGE_MAP()

SMainFrame.h

class SMainFrame : public CFrameWnd
{
    DECLARE_DYNCREATE(SMainFrame)
protected:
    SMainFrame();           
    ~SMainFrame();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

    afx_msg LRESULT OnDialogReady(WPARAM wParam, LPARAM lParam);

    afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);

    CurrentData* GetCurrentData(){return this->currentData;};
    Configuration* GetConfiguration(){return this->configuration;};
private:
    BOOL m_shown;   
    CurrentData* currentData;
    Configuration* configuration;
    std::map<UINT, CDialog*> views;
    UINT currentID;
};

SMainFrame.cpp

IMPLEMENT_DYNCREATE(SMainFrame, CFrameWnd)

    SMainFrame::SMainFrame()
{
    CString appName;
    appName.LoadStringW(IDS_APP_NAME);
    Create(NULL, appName);
    m_shown = FALSE;
    this->configuration = new Configuration();
    this->currentData = new CurrentData();
    this->currentID = IDD_MAIN_MENU_DIALOG;
    views.insert(std::make_pair(IDD_MAIN_MENU_DIALOG, new MainMenuDialog(this->configuration, this->currentData)));
    // There are more dialogs but for tests one is enough

}

SMainFrame::~SMainFrame()
{
    for(auto iterator:views)
    {
        delete iterator.second;
    }
    delete this->configuration;
    delete this->currentData; //Is executed properly
} // After this line an access violation occurres...


BEGIN_MESSAGE_MAP(SMainFrame, CFrameWnd)
    ON_MESSAGE(WM_USER_DIALOG_READY, &SMainFrame::OnDialogReady)
    ON_WM_CREATE()
    ON_WM_ACTIVATE()
END_MESSAGE_MAP()

// SMainFrame message handlers

int SMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ModifyStyle(WS_CAPTION, 0, SWP_DRAWFRAME | SWP_NOZORDER );

    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    return 0;
}
afx_msg LRESULT SMainFrame::OnDialogReady(WPARAM wParam, LPARAM lParam)
{
    DialogThreadParams* params = (DialogThreadParams*)lParam;
    this->currentID = params->nextDialogID;
    m_shown = FALSE;
    this->ActivateFrame();
    return 0;
}

BOOL SMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if( !CFrameWnd::PreCreateWindow(cs) )
        return FALSE;

    cs.dwExStyle &= ~WS_EX_CLIENTEDGE | SWP_DRAWFRAME; 
    cs.style  =  WS_EX_CLIENTEDGE; 

    return TRUE;
}

void SMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{

    if(m_shown == FALSE)
    {
        m_shown = TRUE; 
        if(IsWindow(views[currentID]->m_hWnd))
        {
            views[currentID]->SetFocus();
        }
        else
        {
            views[currentID]->DoModal();
        }
    }
    CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
}

MainMenuDialog — это класс, унаследованный от StandardDialog, который унаследован от CDialog. Сейчас я буду публиковать только фрагменты кода, так как этот пост уже слишком длинный (если вам нужно больше, скажите мне, какая часть может быть интересна)... Диалоговое окно, которое предлагает возможность закрыть приложение, называется ShutdownDialog (унаследовано только от CDialog) и сохраняется как частная переменная MainMenuDialog:

ShutdownDialog* shutdownDialog;

Создано в конструкторе MainMenuDialog:

this->shutdownDialog = new ShutdownDialog();

Я показываю диалог по нажатию кнопки:

void MainMenuDialog::OnClickedShutdownButton()
{
    shutdownDialog->DoModal();
}

И удаляется в деструкторе MainMenuDialog:

MainMenuDialog::~MainMenuDialog()
{
    delete shutdownDialog;
}

В ShutdowDialog я закрываю приложение с помощью этого фрагмента кода:

AfxGetMainWnd()->PostMessage(WM_CLOSE);
EndDialog( 0 );

До этого все работает нормально. Приложение начинает уничтожать объекты. Но после завершения вызова деструктора SMainFrame я получаю нарушение прав доступа. Программа не останавливается, просто строка в окне вывода. Он продолжается оператором «вернуть FALSE;» в SWinApp InitInstance().

Я знаю, что нарушение прав доступа происходит при двойном удалении объекта или использовании указателя, когда зависимый объект уже был уничтожен, но я не могу понять, что здесь происходит не так. Кроме того, я должен сказать, что SWinApp и SMainFrame были созданы коллегой, и я изменил части с помощью диалогов в SMainFrame. Я думал, что m_pMainWnd может быть проблемой, так как после вызова деструктора SMainFrame это должен быть недопустимый указатель. Итак, я попытался:

SMainFrame::~SMainFrame()
{
    for(auto iterator:views)
    {
        delete iterator.second;
    }
    delete this->configuration;
    delete this->currentData;
    AfxGetApp()->m_pMainWnd = NULL;
}

Но нарушение все еще происходит... Я искал окно стека вызовов, но не смог найти его на вкладке просмотра... Прошу прощения за такой длинный пост! И извините, если это слишком специфично... Но я борюсь уже несколько часов и понятия не имею, что я могу попробовать... Любая помощь приветствуется!


person sadermader    schedule 25.03.2015    source источник
comment
Не делайте AfxGetApp()->m_pMainWnd = NULL; Фреймворк сделает это за вас.   -  person Andrew Komiagin    schedule 25.03.2015
comment
Можете ли вы запустить его в режиме отладки, чтобы увидеть, где именно происходит сбой?   -  person Andrew Komiagin    schedule 25.03.2015
comment
Попробуйте изменить AfxGetMainWnd()->PostMessage(WM_CLOSE); в ::PostQuitMessage(0);   -  person Andrew Komiagin    schedule 25.03.2015
comment
@Андрей Комягин: Большое спасибо! PostQuitMessage решает мою проблему :) Если вы опубликуете его еще раз в качестве ответа, я приму его. Теперь я получаю только сообщение: Предупреждение: уничтожение не-NULL m_pMainWnd. Но то, о чем я читал, это то, что это не вина, а просто информация :)   -  person sadermader    schedule 25.03.2015


Ответы (1)


Изменить AfxGetMainWnd()->PostMessage(WM_CLOSE); на ::PostQuitMessage( 0 );

person Andrew Komiagin    schedule 25.03.2015