diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 4217b043..12850ea0 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -236,6 +236,7 @@ int main(int argc, char** argv) { return wxEntry(argc, argv); } IMPLEMENT_DYNAMIC(CWinApp, CWinThread) BEGIN_MESSAGE_MAP(CWinApp, CWinThread) + ON_COMMAND(ID_APP_EXIT, &CWinApp::OnAppExit) ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, &CWinApp::OnOpenRecentFile) ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, &CWinApp::OnUpdateRecentFileMenu) END_MESSAGE_MAP() diff --git a/port/mfcwx/src/docview.cpp b/port/mfcwx/src/docview.cpp index 8f2477a8..a463f6e1 100644 --- a/port/mfcwx/src/docview.cpp +++ b/port/mfcwx/src/docview.cpp @@ -88,6 +88,14 @@ wxAuiNotebook* ClientNotebook(CMDIFrameWnd* frame) { return frame && frame->m_hWndMDIClient ? wxDynamicCast(ToWx(frame->m_hWndMDIClient), wxAuiNotebook) : nullptr; } +// Resolves a frame by its window at call time, so deferred calls never use a deleted frame object. +CMDIFrameWnd* LiveMDIFrame(HWND hWnd) { + if (!hWnd || !IsWindow(hWnd)) + return nullptr; + CWnd* wnd = PermanentWnd(ToWx(hWnd)); + return wnd && wnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd)) ? static_cast(wnd) : nullptr; +} + CMDIFrameWnd* MainMDIFrame() { CWinApp* app = AfxGetApp(); CWnd* main = app ? app->m_pMainWnd : nullptr; @@ -893,9 +901,11 @@ BOOL CMDIFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext*) { auto* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(book, 1, wxEXPAND); frame->SetSizer(sizer); - book->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, [this](wxAuiNotebookEvent& e) { + HWND frameWnd = m_hWnd; + book->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, [frameWnd](wxAuiNotebookEvent& e) { e.Skip(); - OnChildActivated(); + if (CMDIFrameWnd* mdi = LiveMDIFrame(frameWnd)) + mdi->OnChildActivated(); }); book->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, [book](wxAuiNotebookEvent& e) { e.Veto(); @@ -1117,11 +1127,13 @@ BOOL CMDIChildWnd::DestroyWindow() { book->RemovePage(static_cast(index)); } DestroyWxWindow(panel); - if (mdi) - PostToMainThread([mdi] { - if (mdi->m_hWnd) - mdi->OnChildActivated(); + if (mdi) { + HWND frameWnd = mdi->m_hWnd; + PostToMainThread([frameWnd] { + if (CMDIFrameWnd* live = LiveMDIFrame(frameWnd)) + live->OnChildActivated(); }); + } return TRUE; }); } diff --git a/port/mfcwx/src/snapshot.cpp b/port/mfcwx/src/snapshot.cpp index 3acd640b..9413efa0 100644 --- a/port/mfcwx/src/snapshot.cpp +++ b/port/mfcwx/src/snapshot.cpp @@ -129,6 +129,7 @@ wxWindow* ActiveDialog() { // Development aid: MFCWX_AUTOMATION=file runs one step per line on a timer, for UI tests: // command WM_COMMAND to the main window // button WM_COMMAND (BN_CLICKED) to the topmost dialog +// close WM_CLOSE to the main window // text sets the text of a control of the topmost dialog // tab / closetab clicks the n-th MDI tab or its close button // tabs print the MDI tabs @@ -161,6 +162,10 @@ class AutomationTimer : public wxTimer { target->PostMessage(WM_COMMAND, MAKEWPARAM(id, BN_CLICKED), 0); else fprintf(stderr, "mfcwx automation: no target window\n"); + } else if (op == "close") { + CWnd* main = AfxGetMainWnd(); + if (main && main->m_hWnd) + main->PostMessage(WM_CLOSE, 0, 0); } else if (op == "text") { int id = 0; in >> id;