Согласно этому 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.
Я думаю, что ответ лежит в этой 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
, но это не так. Это приводит к катастрофе.
Попробуйте отправить 'WM_SYSCOMMAND' с помощью wParam' SC_CLOSE'. –
Нет, точно такая же проблема, 'pView-> m_hWnd' уже' NULL', когда она попадает в 'ASSERT (:: IsWindow (pView-> m_hWnd));', это фактически 'CFormView', я отредактировал вопрос в случае, если он что-то изменит. – Smash
На этом этапе я бы добавил несколько инструкций трассировки, чтобы получить порядок, который происходит, когда вы нажимаете «X» самостоятельно или когда вы отправляете «WM_CLOSE». Это проследит процесс. –