diff --git a/include/wx/msw/mfc.h b/include/wx/msw/mfc.h new file mode 100644 index 0000000000..830db351ac --- /dev/null +++ b/include/wx/msw/mfc.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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: + // If default ctor is used, Attach() must be called later. + wxMFCWnd() + { + } + + // Combines default ctor and Attach(). + explicit wxMFCWnd(wxWindow* w) + { + Attach(w); + } + + void Attach(wxWindow* w) + { + CWnd::Attach(w->GetHWND()); + } + + ~wxMFCWnd() + { + // Prevent MFC from destroying the wxWindow. + Detach(); + } +}; + +// ---------------------------------------------------------------------------- +// MFC application class forwarding everything to wxApp +// ---------------------------------------------------------------------------- + +// 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 ( !BaseApp::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; + m_pMainWnd = NULL; + + if ( wxTheApp ) + wxTheApp->OnExit(); + + wxEntryCleanup(); + + return BaseApp::ExitInstance(); + } + + // Override this to provide messages pre-processing for wxWidgets windows. + BOOL PreTranslateMessage(MSG *msg) wxOVERRIDE + { + // 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); + } + + BOOL OnIdle(LONG lCount) wxOVERRIDE + { + BOOL moreIdle = BaseApp::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); + + // 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. + wxTheApp->SetExitOnFrameDelete(true); + + return TRUE; + } + +private: + void OnMainWindowDestroyed(wxWindowDestroyEvent& event) + { + event.Skip(); + + delete m_pMainWnd; + m_pMainWnd = NULL; + } +}; + +typedef wxMFCApp wxMFCWinApp; + +// ---------------------------------------------------------------------------- +// 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/include/wx/msw/winundef.h b/include/wx/msw/winundef.h index b363e7e94e..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); @@ -420,26 +428,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 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__