Theo câu hỏi stackoverflow này:MFC CView (CFormView) phá hủy tai nạn
What is the correct way to programmatically quit an MFC application?
Tôi đang sử dụng AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0);
để thoát khỏi một chương trình MFC. (SDI, CFrameWnd chứa CSplitterWnd với hai CFormView)
Như dự kiến, điều này gọi DestroyWindow()
.
Vấn đề tôi đang phải đối mặt là sau khi sự tàn phá CFormView có nguồn gốc, theo MSDN:
Sau khi gọi DestroyWindow trên một đối tượng không tự động dọn dẹp, C++ đối tượng sẽ còn thích hợp nữa, nhưng m_hWnd sẽ NULL. [MSDN]
Bây giờ CView
destructor được gọi và tại thời điểm nó làm
CDocument::RemoveView()...
CDocument::UpdateFrameCounts()
nó không thành công trên khẳng định sau đây: ASSERT(::IsWindow(pView->m_hWnd));
tôi đã kiểm tra và m_hWnd
đã được thiết lập để NULL trong hàm hủy CView có nguồn gốc được gọi ngay trước đây.
Tôi đang làm gì sai?
EDIT:
Đây là biểu đồ minh họa tại sao tôi muốn gửi tin nhắn WM_CLOSE chứ không phải WM_QUIT.
Tôi nghĩ câu trả lời hoàn toàn sụp đổ MSDN Technical Note này, nhưng tôi không thể tìm nó ra.
EDIT 2:
Trình tự rằng mọi thứ được gọi là:
1- AfxGetMainWnd()->PostMessage(WM_CLOSE,0,0);
2- Derived CFrameWnd::OnClose()
3- CFrameWnd::OnClose()
trong đó kêu gọi CWinApp::CloseAllDocuments(BOOL bEndSession);
trong đó kêu gọi CDocManager::CloseAllDocuments(BOOL bEndSession)
trong đó kêu gọi
trong đó kêu gọi CDocument::OnCloseDocument()
Bây giờ, trong chức năng này
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
}
Vì vậy, chúng ta thấy rằng CWnd::DestroyWindow()
được gọi, vì vậy:
4 - Derived CFormView destructor
5- CScrollView::~CScrollView()
6- CView::~CView()
trong đó kêu gọi CDocument::RemoveView(CView* pView)
trong đó kêu gọi CDocument::OnChangedViewList()
trong đó kêu gọi CDocument::UpdateFrameCounts()
nào bị treo ở đây: ASSERT(::IsWindow(pView->m_hWnd));
vì pView->m_hWnd
là NULL
...
EDIT 3:
tôi đã tìm ra những gì vấn đề là:
Destructor của cái nhìn đầu tiên đã được xóa một con trỏ chưa được khởi tạo, đó là UB. Điều này đã làm cho các destructor treo và không bao giờ hoàn thành.
Thông thường, trình hủy của chế độ xem thứ hai chỉ được gọi khi hoàn thành chế độ xem đầu tiên. Nhưng trong trường hợp này nó vẫn đang được thực hiện mặc dù người đầu tiên không bao giờ hoàn thành.
Kể từ khi destructor lớp xem cơ sở đầu tiên không bao giờ được gọi là, chức năng này chưa từng được gọi cho quan điểm thứ nhất:
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
}
đâu chúng ta có thể thấy rằng quan điểm được lấy ra từ các m_viewList
.
Điều này có nghĩa rằng khi xem destructor thứ hai hoàn thành, trong:
void CDocument::UpdateFrameCounts()
// assumes 1 doc per frame
{
// walk all frames of views (mark and sweep approach)
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
...
Các pos được coi là NULL
, nhưng nó không phải là. Dẫn đến vụ tai nạn.
Thay vào đó hãy thử đăng 'WM_SYSCOMMAND' bằng wParam của 'SC_CLOSE'. –
Không, chính xác cùng một vấn đề, 'pView-> m_hWnd' đã là' NULL' khi nó đến 'ASSERT (:: IsWindow (pView-> m_hWnd)); ', nó thực sự là' CFormView's, tôi đã chỉnh sửa câu hỏi trong trường hợp nó thay đổi bất cứ điều gì. – Smash
Tại thời điểm này, tôi đưa vào một số câu lệnh theo dõi để có được thứ tự xảy ra khi bạn nhấp vào "X" khi bạn gửi 'WM_CLOSE' thay thế. Điều đó sẽ làm sáng tỏ một số tiến trình. –