2015-09-18 2 views
11

Согласно этому StackOverflow вопрос:аварии MFC CView (CFormView) разрушение

What is the correct way to programmatically quit an MFC application?

Я использую AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0); для выхода из программы MFC. (SDI, CFrameWnd, содержащий CSplitterWnd с двумя CFormViews)

Как и ожидалось, это вызывает DestroyWindow().

Проблема я столкнулся в том, что после того, как производный разрушения CFormView, согласно MSDN:

После вызова DestroyWindow на объекте без автоматической очистки, C++ объект будет по-прежнему вокруг, но m_hWnd будет NULL. [MSDN]

Теперь CView деструктор и в точке это делает

CDocument::RemoveView()... 
CDocument::UpdateFrameCounts() 

он не на следующий утверждают: ASSERT(::IsWindow(pView->m_hWnd));

я проверил и m_hWnd уже установлен NULL в производном деструкторе CView, который был вызван раньше.

Что я делаю неправильно?

EDIT:

Вот диаграмма, иллюстрирующая, почему я хочу, чтобы отправить сообщение WM_CLOSE и не WM_QUIT.

enter image description here

Я думаю, что ответ лежит в этой MSDN Technical Note, но я не могу понять это.

EDIT 2:

Порядок, в котором все становится называется:

1- AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0);

2- Derived CFrameWnd::OnClose()

3- CFrameWnd::OnClose()

, который вызывает CWinApp::CloseAllDocuments(BOOL bEndSession);

который называет CDocManager::CloseAllDocuments(BOOL bEndSession)

который называет

который называет CDocument::OnCloseDocument()

Теперь, в этой функции

while (!m_viewList.IsEmpty()) 
{ 
    // get frame attached to the view 
    CView* pView = (CView*)m_viewList.GetHead(); 
    ASSERT_VALID(pView); 
    CFrameWnd* pFrame = pView->EnsureParentFrame(); 

    // and close it 
    PreCloseFrame(pFrame); 
    pFrame->DestroyWindow(); 
    // will destroy the view as well 
} 

Итак, мы видим, что CWnd::DestroyWindow() называется, так:

4 - Derived CFormView destructor

5- CScrollView::~CScrollView()

6- CView::~CView()

который называет CDocument::RemoveView(CView* pView)

который называет CDocument::OnChangedViewList()

который называет CDocument::UpdateFrameCounts()

Какой сбой здесь: ASSERT(::IsWindow(pView->m_hWnd));

потому что pView->m_hWnd является NULL ...

EDIT 3:

я понял, что проблема была:

Деструктор первого взгляда было удаление неинициализированный указатель, который UB. Это заставило деструктора повесить и никогда не завершить.

Обычно деструктор второго вида вызывается только после завершения первого. Но в этом случае он все еще исполнялся, хотя первый никогда не завершался.

С первых деструкторов вид базового класса никогда не называли, эта функция никогда не вызывалась для первого взгляда:

void CDocument::RemoveView(CView* pView) 
{ 
    ASSERT_VALID(pView); 
    ASSERT(pView->m_pDocument == this); // must be attached to us 

    m_viewList.RemoveAt(m_viewList.Find(pView)); 
    pView->m_pDocument = NULL; 

    OnChangedViewList(); // must be the last thing done to the document 
} 

Где мы можем видеть, что вид удаляется из m_viewList.

Это означает, что, когда второй вид деструктор завершается в:

void CDocument::UpdateFrameCounts() 
    // assumes 1 doc per frame 
{ 
    // walk all frames of views (mark and sweep approach) 
    POSITION pos = GetFirstViewPosition(); 
    while (pos != NULL) 
    { 
... 

позы должны быть NULL, но это не так. Это приводит к катастрофе.

+0

Попробуйте отправить 'WM_SYSCOMMAND' с помощью wParam' SC_CLOSE'. –

+0

Нет, точно такая же проблема, 'pView-> m_hWnd' уже' NULL', когда она попадает в 'ASSERT (:: IsWindow (pView-> m_hWnd));', это фактически 'CFormView', я отредактировал вопрос в случае, если он что-то изменит. – Smash

+0

На этом этапе я бы добавил несколько инструкций трассировки, чтобы получить порядок, который происходит, когда вы нажимаете «X» самостоятельно или когда вы отправляете «WM_CLOSE». Это проследит процесс. –

ответ

0

Проблема была решена, см. EDIT 3 в вопросе о решении.

+1

Ответ не должен быть в вопросе. – Mangs

1

Звоните ::PostQuitMessage(0);, чтобы закрыть приложение.

+0

Если я использую то, что вы предлагаете, то деструкторы полученных CFormViews никогда не вызываются ... – Smash

2

Я думаю, что способ закрытия рамки - это не проблема. Я предполагаю, что вы уничтожаете одно из видов вручную, тогда как вы должны позволить MFC удалить их (вы, вероятно, называли DestroyWindow на одном из них)

 Смежные вопросы

  • Нет связанных вопросов^_^