From acf53800fca55a50f39659c5d92111134593aa27 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 3 Dec 2017 17:50:18 +0100 Subject: [PATCH] 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__