From acf53800fca55a50f39659c5d92111134593aa27 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 3 Dec 2017 17:50:18 +0100 Subject: [PATCH 1/8] Extract reusable part of the MFC sample in a header Allow reusing this functionality from outside the library, it can he useful if an MFC window needs to be embedded into a wx application (or vice versa). Also use a better wxEntryStart() overload as a side effect, this should, in particular, fix the problem with command line arguments processing in mixed MFC/wx applications pointed out in a comment in the sample previously. --- include/wx/msw/mfc.h | 137 ++++++++++++++++++++++++++++++++++++++++ samples/mfc/mfctest.cpp | 109 +++----------------------------- samples/mfc/mfctest.h | 46 ++++++-------- 3 files changed, 166 insertions(+), 126 deletions(-) create mode 100644 include/wx/msw/mfc.h diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h new file mode 100644 index 0000000000..49974bb9ea --- /dev/null +++ b/include/wx/msw/mfc.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/mfc.h +// Purpose: Helpers for applications using both wxWidgets and MFC +// Author: Julian Smart, Vadim Zeitlin +// Created: 2017-12-01 (mostly extracted from samples/mfc) +// Copyright: (c) 2017 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_MSW_MFC_H_ +#define _WX_MSW_MFC_H_ + +#ifndef __AFXWIN_H__ + #error "MFC headers must be included before including this file." +#endif + +#include "wx/app.h" +#include "wx/evtloop.h" +#include "wx/window.h" +#include "wx/msw/winundef.h" + +// ---------------------------------------------------------------------------- +// MFC window class wrapping a window created by wxWidgets +// ---------------------------------------------------------------------------- + +class wxMFCWnd : public CWnd +{ +public: + explicit wxMFCWnd(wxWindow* w) + { + Attach(w->GetHWND()); + } + + ~wxMFCWnd() + { + // Prevent MFC from destroying the wxWindow. + Detach(); + } +}; + +// ---------------------------------------------------------------------------- +// MFC application class forwarding everything to wxApp +// ---------------------------------------------------------------------------- + +class wxMFCWinApp : public CWinApp +{ +public: + BOOL InitInstance() wxOVERRIDE + { + if ( !CWinApp::InitInstance() ) + return FALSE; + + if ( !wxEntryStart(m_hInstance) ) + return FALSE; + + if ( !wxTheApp || !wxTheApp->CallOnInit() ) + return FALSE; + + if ( !InitMainWnd() ) + return FALSE; + + return TRUE; + } + + int ExitInstance() wxOVERRIDE + { + delete m_pMainWnd; + + if ( wxTheApp ) + wxTheApp->OnExit(); + + wxEntryCleanup(); + + return CWinApp::ExitInstance(); + } + + // Override this to provide wxWidgets message loop compatibility + BOOL PreTranslateMessage(MSG *msg) wxOVERRIDE + { + wxEventLoop * const + evtLoop = static_cast(wxEventLoop::GetActive()); + if ( evtLoop && evtLoop->PreProcessMessage(msg) ) + return TRUE; + + return CWinApp::PreTranslateMessage(msg); + } + + BOOL OnIdle(LONG lCount) wxOVERRIDE + { + BOOL moreIdle = CWinApp::OnIdle(lCount); + + if ( wxTheApp && wxTheApp->ProcessIdle() ) + moreIdle = TRUE; + + return moreIdle; + } + +protected: + // This virtual method can be overridden to create the main window using + // MFC code. The default implementation relies on wxApp::OnInit() creating + // a top level window which is then wrapped in an MFC window and used as + // the main window. + virtual BOOL InitMainWnd() + { + wxWindow* const w = wxTheApp->GetTopWindow(); + if ( !w ) + return FALSE; + + // We need to initialize the main window to let the program continue + // running. + m_pMainWnd = new wxMFCWnd(w); + + // And we need to let wxWidgets know that it should exit the + // application when this window is closed, as OnRun(), which does this + // by default, won't be called when using MFC main message loop. + wxTheApp->SetExitOnFrameDelete(true); + + return TRUE; + } +}; + +// ---------------------------------------------------------------------------- +// wxWidgets application class to be used in MFC applications +// ---------------------------------------------------------------------------- + +class wxAppWithMFC : public wxApp +{ +public: + void ExitMainLoop() wxOVERRIDE + { + // There is no wxEventLoop to exit, tell MFC to stop pumping messages + // instead. + ::PostQuitMessage(0); + } +}; + +#endif // _WX_MSW_MFC_H_ diff --git a/samples/mfc/mfctest.cpp b/samples/mfc/mfctest.cpp index 9263040ceb..1c00df1c9e 100644 --- a/samples/mfc/mfctest.cpp +++ b/samples/mfc/mfctest.cpp @@ -17,8 +17,8 @@ // created subsequently. // // (1) Make MyApp::OnInit not create a main window. -// (2) Make MFC's InitInstance create a main window, and remove -// creation of CDummyWindow. +// (2) Define a class deriving from wxMFCWinApp and override its InitMainWnd() +// to create the main window in MFC code. // // This can be accomplished by setting START_WITH_MFC_WINDOW to 1 below. @@ -73,6 +73,8 @@ #include "wx/nativewin.h" #include "wx/spinctrl.h" +#include "wx/msw/mfc.h" + #include "resource.h" #include "mfctest.h" @@ -82,19 +84,15 @@ // theApp: // Just creating this application object runs the whole application. // -CTheApp theApp; +SampleMFCWinApp theApp; // wxWidgets elements -// Define a new application type -class MyApp: public wxApp +// Define a new application type inheriting from wxAppWithMFC +class MyApp: public wxAppWithMFC { public: - virtual bool OnInit(); - - // we need to override this as the default behaviour only works when we're - // running wxWidgets main loop, not MFC one - virtual void ExitMainLoop(); + virtual bool OnInit() wxOVERRIDE; wxFrame *CreateFrame(); }; @@ -238,73 +236,6 @@ ON_COMMAND( IDM_TEST, OnTest ) //}}AFX_MSG_MAP END_MESSAGE_MAP() -BOOL CTheApp::InitInstance() -{ - if ( !CWinApp::InitInstance() ) - return FALSE; - - // TODO: cmd line parsing - WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst); - wxSetInstance(m_hInstance); - wxApp::m_nCmdShow = m_nCmdShow; - int argc = 0; - wxChar **argv = NULL; - wxEntryStart(argc, argv); - if ( !wxTheApp || !wxTheApp->CallOnInit() ) - return FALSE; - -#if START_WITH_MFC_WINDOW - // Demonstrate creation of an initial MFC main window. - m_pMainWnd = new CMainWindow(); - m_pMainWnd->ShowWindow( m_nCmdShow ); - m_pMainWnd->UpdateWindow(); -#else - // Demonstrate creation of an initial wxWidgets main window. - // Wrap wxWidgets window in a dummy MFC window and - // make the main window. - if (wxTheApp && wxTheApp->GetTopWindow()) - { - m_pMainWnd = new CDummyWindow((HWND) wxTheApp->GetTopWindow()->GetHWND()); - } -#endif - - return TRUE; -} - -int CTheApp::ExitInstance() -{ -#if !START_WITH_MFC_WINDOW - delete m_pMainWnd; -#endif - - if ( wxTheApp ) - wxTheApp->OnExit(); - wxEntryCleanup(); - - return CWinApp::ExitInstance(); -} - -// Override this to provide wxWidgets message loop compatibility -BOOL CTheApp::PreTranslateMessage(MSG *msg) -{ - wxEventLoop * const - evtLoop = static_cast(wxEventLoop::GetActive()); - if ( evtLoop && evtLoop->PreProcessMessage(msg) ) - return TRUE; - - return CWinApp::PreTranslateMessage(msg); -} - -BOOL CTheApp::OnIdle(LONG lCount) -{ - BOOL moreIdle = CWinApp::OnIdle(lCount); - - if ( wxTheApp && wxTheApp->ProcessIdle() ) - moreIdle = TRUE; - - return moreIdle; -} - /********************************************************************* * wxWidgets elements ********************************************************************/ @@ -315,22 +246,12 @@ bool MyApp::OnInit() return false; #if !START_WITH_MFC_WINDOW - // as we're not inside wxWidgets main loop, the default logic doesn't work - // in our case and we need to do this explicitly - SetExitOnFrameDelete(true); - (void) CreateFrame(); #endif return true; } -void MyApp::ExitMainLoop() -{ - // instead of existing wxWidgets main loop, terminate the MFC one - ::PostQuitMessage(0); -} - wxFrame *MyApp::CreateFrame() { MyChild *subframe = new MyChild(NULL, wxT("Canvas Frame"), wxPoint(10, 10), wxSize(300, 300), @@ -451,17 +372,3 @@ void MyChild::OnActivate(wxActivateEvent& event) if (event.GetActive() && canvas) canvas->SetFocus(); } - -// Dummy MFC window for specifying a valid main window to MFC, using -// a wxWidgets HWND. -CDummyWindow::CDummyWindow(HWND hWnd) -{ - Attach(hWnd); -} - -// Don't let the CWnd destructor delete the HWND -CDummyWindow::~CDummyWindow() -{ - Detach(); -} - diff --git a/samples/mfc/mfctest.h b/samples/mfc/mfctest.h index 2c49911339..b12036d913 100644 --- a/samples/mfc/mfctest.h +++ b/samples/mfc/mfctest.h @@ -9,11 +9,7 @@ #ifndef __MFCTEST_H__ #define __MFCTEST_H__ -///////////////////////////////////////////////////////////////////////////// - -// CMainWindow: -// See hello.cpp for the code to the member functions and the message map. -// +// CMainWindow: just a normal MFC window class. class CMainWindow : public CFrameWnd { public: @@ -31,30 +27,30 @@ private: class wxNativeContainerWindow* m_containerWX; }; -// A dummy CWnd pointing to a wxWindow's HWND -class CDummyWindow: public CWnd +#if START_WITH_MFC_WINDOW + +// There is no need to define an application class if the default behaviour of +// using the wxWindow created in wxApp::OnInit() as main window is acceptable, +// but if we want to create the initial window in MFC, we need this class in +// order to override InitMainWnd() in it. +class SampleMFCWinApp : public wxMFCWinApp { - public: - CDummyWindow(HWND hWnd); - ~CDummyWindow(void); +protected: + BOOL InitMainWnd() wxOVERRIDE + { + // Demonstrate creation of an initial MFC main window. + m_pMainWnd = new CMainWindow(); + m_pMainWnd->ShowWindow( m_nCmdShow ); + m_pMainWnd->UpdateWindow(); + + return TRUE; + } }; -///////////////////////////////////////////////////////////////////////////// +#else // !START_WITH_MFC_WINDOW -// CTheApp: -// -class CTheApp : public CWinApp -{ -public: - BOOL InitInstance(); - int ExitInstance(); +typedef wxMFCWinApp SampleMFCWinApp; - // Override this to provide wxWidgets message loop - // compatibility - BOOL PreTranslateMessage(MSG *msg); - BOOL OnIdle(LONG lCount); -}; - -///////////////////////////////////////////////////////////////////////////// +#endif // START_WITH_MFC_WINDOW/!START_WITH_MFC_WINDOW #endif // __MFCTEST_H__ From 9dd83933f3b870cbe939a49438d51586afa1659d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 6 Dec 2017 18:25:20 +0100 Subject: [PATCH 2/8] Remove redundant sections from wx/msw/winundef.h Somehow we redefined GetFirstChild() and GetNextSibling() twice in a row for the last ~18 years. --- include/wx/msw/winundef.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/include/wx/msw/winundef.h b/include/wx/msw/winundef.h index b363e7e94e..b5722ff98d 100644 --- a/include/wx/msw/winundef.h +++ b/include/wx/msw/winundef.h @@ -420,26 +420,6 @@ // For ming and cygwin -// GetFirstChild -#ifdef GetFirstChild - #undef GetFirstChild - inline HWND GetFirstChild(HWND h) - { - return GetTopWindow(h); - } -#endif - - -// GetNextSibling -#ifdef GetNextSibling - #undef GetNextSibling - inline HWND GetNextSibling(HWND h) - { - return GetWindow(h, GW_HWNDNEXT); - } -#endif - - #ifdef Yield #undef Yield #endif From 042d922e88da988875d043b837dc548eb4b930b0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 8 Dec 2017 16:40:42 +0100 Subject: [PATCH 3/8] Allow predefining wxUSE_UNICODE_WINDOWS_H in wxMSW builds This can be used in order to use normal Unicode build of wxWidgets with an application that needs to use ANSI versions of Win32 functions, e.g. because it doesn't compile with UNICODE defined. --- include/wx/msw/winundef.h | 46 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/include/wx/msw/winundef.h b/include/wx/msw/winundef.h index b5722ff98d..aedf4340d3 100644 --- a/include/wx/msw/winundef.h +++ b/include/wx/msw/winundef.h @@ -15,6 +15,14 @@ #define _WX_WINUNDEF_H_ */ +#ifndef wxUSE_UNICODE_WINDOWS_H + #ifdef _UNICODE + #define wxUSE_UNICODE_WINDOWS_H 1 + #else + #define wxUSE_UNICODE_WINDOWS_H 0 + #endif +#endif + // ---------------------------------------------------------------------------- // windows.h #defines the following identifiers which are also used in wxWin so // we replace these symbols with the corresponding inline functions and @@ -34,7 +42,7 @@ HWND hwndParent, DLGPROC pDlgProc) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return CreateDialogW(hInstance, pTemplate, hwndParent, pDlgProc); #else return CreateDialogA(hInstance, pTemplate, hwndParent, pDlgProc); @@ -62,7 +70,7 @@ DWORD family, LPCTSTR facename) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return CreateFontW(height, width, escapement, orientation, weight, italic, underline, strikeout, charset, outprecision, clipprecision, quality, @@ -90,7 +98,7 @@ HINSTANCE hInstance, LPVOID lpParam) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return CreateWindowW(lpClassName, lpWndClass, dwStyle, x, y, w, h, hWndParent, hMenu, hInstance, lpParam); #else @@ -107,7 +115,7 @@ inline HMENU LoadMenu(HINSTANCE instance, LPCTSTR name) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return LoadMenuW(instance, name); #else return LoadMenuA(instance, name); @@ -122,7 +130,7 @@ inline HWND APIENTRY FindText(LPFINDREPLACE lpfindreplace) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return FindTextW(lpfindreplace); #else return FindTextA(lpfindreplace); @@ -136,7 +144,7 @@ #undef GetCharWidth inline BOOL GetCharWidth(HDC dc, UINT first, UINT last, LPINT buffer) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return GetCharWidthW(dc, first, last, buffer); #else return GetCharWidthA(dc, first, last, buffer); @@ -148,7 +156,7 @@ #ifdef FindWindow #undef FindWindow - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline HWND FindWindow(LPCWSTR classname, LPCWSTR windowname) { return FindWindowW(classname, windowname); @@ -165,7 +173,7 @@ #ifdef PlaySound #undef PlaySound - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline BOOL PlaySound(LPCWSTR pszSound, HMODULE hMod, DWORD fdwSound) { return PlaySoundW(pszSound, hMod, fdwSound); @@ -182,7 +190,7 @@ #ifdef GetClassName #undef GetClassName - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline int GetClassName(HWND h, LPWSTR classname, int maxcount) { return GetClassNameW(h, classname, maxcount); @@ -199,7 +207,7 @@ #ifdef GetClassInfo #undef GetClassInfo - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline BOOL GetClassInfo(HINSTANCE h, LPCWSTR name, LPWNDCLASSW winclass) { return GetClassInfoW(h, name, winclass); @@ -216,7 +224,7 @@ #ifdef LoadAccelerators #undef LoadAccelerators - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline HACCEL LoadAccelerators(HINSTANCE h, LPCWSTR name) { return LoadAcceleratorsW(h, name); @@ -233,7 +241,7 @@ #ifdef DrawText #undef DrawText - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline int DrawText(HDC h, LPCWSTR str, int count, LPRECT rect, UINT format) { return DrawTextW(h, str, count, rect, format); @@ -252,7 +260,7 @@ #ifdef StartDoc #undef StartDoc - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline int StartDoc(HDC h, CONST DOCINFOW* info) { return StartDocW(h, (DOCINFOW*) info); @@ -271,7 +279,7 @@ #undef GetObject inline int GetObject(HGDIOBJ h, int i, LPVOID buffer) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return GetObjectW(h, i, buffer); #else return GetObjectA(h, i, buffer); @@ -285,7 +293,7 @@ #undef GetMessage inline int GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return GetMessageW(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); #else return GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); @@ -298,7 +306,7 @@ #undef LoadIcon inline HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return LoadIconW(hInstance, lpIconName); #else // ANSI return LoadIconA(hInstance, lpIconName); @@ -311,7 +319,7 @@ #undef LoadBitmap inline HBITMAP LoadBitmap(HINSTANCE hInstance, LPCTSTR lpBitmapName) { - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H return LoadBitmapW(hInstance, lpBitmapName); #else // ANSI return LoadBitmapA(hInstance, lpBitmapName); @@ -323,7 +331,7 @@ #ifdef LoadLibrary #undef LoadLibrary - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline HINSTANCE LoadLibrary(LPCWSTR lpLibFileName) { return LoadLibraryW(lpLibFileName); @@ -339,7 +347,7 @@ // FindResource #ifdef FindResource #undef FindResource - #ifdef _UNICODE + #if wxUSE_UNICODE_WINDOWS_H inline HRSRC FindResource(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType) { return FindResourceW(hModule, lpName, lpType); From 3960e630f18c39b4e4a1be62593c9cd4fa6c5e5d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 8 Dec 2017 16:42:34 +0100 Subject: [PATCH 4/8] Make it possible to use an existing base class for wxMFCWinApp When porting an existing MFC codebase to wxWidgets, it may be useful to continue using the existing CWinApp-derived application class, so allow deriving wxMFCApp from it instead of always deriving it from CWinApp itself. --- include/wx/msw/mfc.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h index 49974bb9ea..894918f194 100644 --- a/include/wx/msw/mfc.h +++ b/include/wx/msw/mfc.h @@ -42,12 +42,17 @@ public: // MFC application class forwarding everything to wxApp // ---------------------------------------------------------------------------- -class wxMFCWinApp : public CWinApp +// The template parameter here is an existing class deriving from CWinApp or, +// if there is no such class, just CWinApp itself. +template +class wxMFCApp : public T { public: + typedef T BaseApp; + BOOL InitInstance() wxOVERRIDE { - if ( !CWinApp::InitInstance() ) + if ( !BaseApp::InitInstance() ) return FALSE; if ( !wxEntryStart(m_hInstance) ) @@ -71,7 +76,7 @@ public: wxEntryCleanup(); - return CWinApp::ExitInstance(); + return BaseApp::ExitInstance(); } // Override this to provide wxWidgets message loop compatibility @@ -82,12 +87,12 @@ public: if ( evtLoop && evtLoop->PreProcessMessage(msg) ) return TRUE; - return CWinApp::PreTranslateMessage(msg); + return BaseApp::PreTranslateMessage(msg); } BOOL OnIdle(LONG lCount) wxOVERRIDE { - BOOL moreIdle = CWinApp::OnIdle(lCount); + BOOL moreIdle = BaseApp::OnIdle(lCount); if ( wxTheApp && wxTheApp->ProcessIdle() ) moreIdle = TRUE; @@ -119,6 +124,8 @@ protected: } }; +typedef wxMFCApp wxMFCWinApp; + // ---------------------------------------------------------------------------- // wxWidgets application class to be used in MFC applications // ---------------------------------------------------------------------------- From 636219bd355bd64a5b1507e4d37e033fe50969f3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 11 Dec 2017 22:06:13 +0100 Subject: [PATCH 5/8] Always pre-process messages for wx windows in mixed wx/MFC apps Still use the active event loop if there is one, just in case it customizes messages pre-processing, but fall back on the standard pre-processing code even if there is no active wx event loop and we're only running the MFC one, as without doing this there are just too many things that don't work (e.g. menu accelerators didn't work at all in mixed wx/MFC applications previously). --- include/wx/msw/mfc.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h index 894918f194..f3fc23e44d 100644 --- a/include/wx/msw/mfc.h +++ b/include/wx/msw/mfc.h @@ -79,12 +79,19 @@ public: return BaseApp::ExitInstance(); } - // Override this to provide wxWidgets message loop compatibility + // Override this to provide messages pre-processing for wxWidgets windows. BOOL PreTranslateMessage(MSG *msg) wxOVERRIDE { - wxEventLoop * const - evtLoop = static_cast(wxEventLoop::GetActive()); - if ( evtLoop && evtLoop->PreProcessMessage(msg) ) + // Use the current event loop if there is one, or just fall back to the + // standard one otherwise, but make sure we pre-process messages in any + // case as otherwise many things would break (e.g. keyboard + // accelerators). + wxGUIEventLoop* + evtLoop = static_cast(wxEventLoop::GetActive()); + wxGUIEventLoop evtLoopStd; + if ( !evtLoop ) + evtLoop = &evtLoopStd; + if ( evtLoop->PreProcessMessage(msg) ) return TRUE; return BaseApp::PreTranslateMessage(msg); From 44c31d2700874ece57bcb7dcd3b7a56f1c5b820a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 11 Dec 2017 23:07:33 +0100 Subject: [PATCH 6/8] Destroy MFC main window when its wx counterpart is destroyed This avoids using m_pMainWnd after its HWND becomes invalid, as this resulted in assert failures from CWnd::WalkPreTranslateTree() called with this HWND as its hWndStop argument from PreTranslateMessage() which was used to pre-translate a WM_NULL message the application sometimes received while closing down. --- include/wx/msw/mfc.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h index f3fc23e44d..14b1a1d5d4 100644 --- a/include/wx/msw/mfc.h +++ b/include/wx/msw/mfc.h @@ -122,6 +122,11 @@ protected: // running. m_pMainWnd = new wxMFCWnd(w); + // We also need to reset m_pMainWnd when this window will be destroyed + // to prevent MFC from using an invalid HWND, which is probably not + // fatal but can result in at least asserts failures. + w->Bind(wxEVT_DESTROY, &wxMFCApp::OnMainWindowDestroyed, this); + // And we need to let wxWidgets know that it should exit the // application when this window is closed, as OnRun(), which does this // by default, won't be called when using MFC main message loop. @@ -129,6 +134,15 @@ protected: return TRUE; } + +private: + void OnMainWindowDestroyed(wxWindowDestroyEvent& event) + { + event.Skip(); + + delete m_pMainWnd; + m_pMainWnd = NULL; + } }; typedef wxMFCApp wxMFCWinApp; From 6c7e5a92008e731ec555f5884dd3009373105e95 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 12 Dec 2017 18:46:29 +0100 Subject: [PATCH 7/8] Allow attaching a wxWindow to CWnd later in wxMFCWnd When using two-step creation, as when loading from resources, for example, it can be convenient to create wxMFCWnd as part of the (parent) wxWindow object, but only attach it to the real HWND later, once it becomes available. --- include/wx/msw/mfc.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h index 14b1a1d5d4..8cab2ee37f 100644 --- a/include/wx/msw/mfc.h +++ b/include/wx/msw/mfc.h @@ -26,9 +26,20 @@ class wxMFCWnd : public CWnd { public: + // If default ctor is used, Attach() must be called later. + wxMFCWnd() + { + } + + // Combines default ctor and Attach(). explicit wxMFCWnd(wxWindow* w) { - Attach(w->GetHWND()); + Attach(w); + } + + void Attach(wxWindow* w) + { + CWnd::Attach(w->GetHWND()); } ~wxMFCWnd() From 35a0ba4fcee1539817e096ee2b3f11d8a844f1df Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 17 Jan 2018 11:32:07 +0100 Subject: [PATCH 8/8] Avoid crash on abnormal exit in wx+MFC combined applications Reset m_pMainWnd in wxMFCApp::ExitInstance() to avoid crash when deleting it again in OnMainWindowDestroyed() that could happen if ExitInstance() was called not because the main window was closed (normal case) but because wxApp::ExitMainLoop() was called, as it happens when an unhandled exception is thrown. --- include/wx/msw/mfc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h index 8cab2ee37f..830db351ac 100644 --- a/include/wx/msw/mfc.h +++ b/include/wx/msw/mfc.h @@ -81,6 +81,7 @@ public: int ExitInstance() wxOVERRIDE { delete m_pMainWnd; + m_pMainWnd = NULL; if ( wxTheApp ) wxTheApp->OnExit();