MFC CView(CFormView)破坏崩溃

Sma*_*ash 11 c++ mfc destructor

根据这个stackoverflow问题:

以编程方式退出MFC应用程序的正确方法是什么?

我在用 AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0);退出MFC程序.(SDI,CFrameWnd包含带有两个CFormViews的CSplitterWnd)

正如预期的那样DestroyWindow().

我面临的问题是在导出的CFormView销毁之后,根据MSDN:

在非自动清理对象上调用DestroyWindow后,C++对象仍然存在,但m_hWnd将为NULL.[ MSDN ]

现在,CView析构函数被调用,然后它就完成了

CDocument::RemoveView()...
CDocument::UpdateFrameCounts()
Run Code Online (Sandbox Code Playgroud)

它在以下断言上失败: ASSERT(::IsWindow(pView->m_hWnd));

我查了一下 m_hWnd之前调用的派生CView析构函数中已经设置为NULL.

我究竟做错了什么 ?

编辑:

这是一个图表,说明我想发送WM_CLOSE消息而不是WM_QUIT的原因.

在此输入图像描述

我认为这个答案在MSDN技术说明有所体现, but I can't figure it out.

编辑2:

事物被调用的顺序:

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

2- Derived CFrameWnd::OnClose()

3- CFrameWnd::OnClose()

哪个叫 CWinApp::CloseAllDocuments(BOOL bEndSession);

哪个叫 CDocManager::CloseAllDocuments(BOOL bEndSession)

哪个叫 CDocTemplate::CloseAllDocuments(BOOL)

哪个叫 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
}
Run Code Online (Sandbox Code Playgroud)

所以我们看到它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_hWndNULL...

编辑3:

我弄清楚问题是什么:

第一个视图的析构函数是删除未初始化的指针,即UB.这使析构函数挂起并永远不会完成.

Usually, the destructor of the second view is only called upon completion of the first one. But in this case it was still being executed although the first one never completed.

Since the first view base class destructors were never called, this function was never called for the first view:

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
}
Run Code Online (Sandbox Code Playgroud)

Where we can see that the view is removed from the m_viewList.

This means that when the second view destructor completes, in:

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

The pos is supposed to be NULL, but it is not. Which lead to the crash.

Sma*_*ash 0

问题已解决,请参阅问题中的编辑 3 以获取解决方案。

  • 答案不应该出现在问题中。 (2认同)