This is a step-by-step guide for porting an application from MFC to wxWidgets based on my experience in porting Hippy.
- wxWidgets documentation for detailed class references.
- wxWidgets wiki for general wxWidgets programming guides
- Compiling using MS VC++
- Shows you basic compilation settings needed for wxWidgets
- Helpers For Automated Rescue From MFC
- provides a
sed
script to convert between matching functionality in MFC and wxWidgts - If you are on Windows you can perform the substitutions manually in Visual Studio with Replace in Files...
- provides a
- WxWidgets For MFC Programmers
- Compiling using MS VC++
- The wxWidgets wxmfc sample has been adapted to use
vcpkg to obtain wxWidgets and build with CMake. This sample demonstrates:
- Hosting wxWidget controls in an MFC container
- Creating top-levle wxWidgets frames from an MFC application
Try to keep your application building and running with small changes for as long as possible. If your application has custom control and frame classes, at some point you will have to bite the bullet and change code in several places at once to wxWidgets. This will involve many changes, so try to get as many other small changes done as possible before making the switch.
If you have complex interactions between controls in a dialog or frame, consider first using the Mediator Pattern to decouple the interaction logic from MFC. You can then write unit tests for the mediator to confirm existing application behavior before porting to wxWidgets. Decoupling the logic from the UI toolkit, in this case MFC, will make it less likely that the interactions will change during the port to the wxWidgets UI toolkit.
An example of decoupling user-interface logic from the UI toolkit via the mediator pattern can be seen by comparing two Winlamb example programs: 3-dialog_controls which shows interacting controls on a dialog with inteaction logic directly in event handlers and 4-dialog_controls_mediator which shows the same interaction achieved through a decoupling mediator. These examples are discussed in the video Writing Native Win32 Applications with WinLamb and Modern C++.
- Created a CMake build for your MFC project if you don't already have one.
- Set global options relevant to MFC:
add_definitions(-D_AFXDLL -D_WIN32_WINNT=_WIN32_WINNT_WIN7 -DWINVER=_WIN32_WINNT_WIN7)
- Pick a different Windows API version if you require more recent functionality than Windows 7; consult your existing Visual Studio project and source files for the relevant version.
set(CMAKE_MFC_FLAG 2)
- Examine other settings in your Visual Studio project files, if you have them.
- When configuring and building with CMake, use a build directory outside your source directory.
- Keep your existing Visual Studio solution and project files for comparison as you work on the port.
- See the modified wxmfc sample for an example of the submodule and CMake changes described above.
- Set global options relevant to MFC:
- Obtain
wxWidgets
from vcpkg- Add a
vcpkg
submodule to your git repository - Add a
wxwidgets
(note: lowercase) dependency to yourvcpkg.json
manifest - Configure CMake with the
CMAKE_TOOLCHAIN_FILE
from vcpkg to get dependencies. - Add
find_package(wxwidgets CONFIG REQUIRED)
to your CMake project
- Add a
- Add
wx::base
as a dependency. This forces a_UNICODE
build.
- Adapt any ANSI MFC
char
style string code toTCHAR
MFC style string code.- Use
CT2A
to convertTCHAR
/CString
style strings to ANSIchar
strings. - Use
CA2T
to convertchar
/std::string
style strings to UnicodeTCHAR
strings. - Use
TCHAR
related string functions, such as_tcscpy
,_stprintf
and so-on. - See Text and Strings in Visual C++
for an explanation of how preprocessor defines switch between multi-byte character strings (MBCS),
Unicode character strings and ASCII character strings with
TCHAR
and the associatedt
string functions.
- Use
- If you don't have unit tests for your application logic, consider adding them before porting.
- Attempt to
#include <wx/wx.h>
in your source files as the last included header- If you get errors about member functions being undefined when they previously built without problems, it's likely that the member functions in the MFC headers share a name with a Win32 API function.
- Win32 API functions accepting or returning string arguments are declared with a macro that is conditionally compiled into an ANSI or Unicode name, depending on build flags.
- When this error occurs many times, the simplest thing is to revert the inclusion of
<wx/wx.h>
and move on to the next file.- This is only a temporary reprieve as you will need to include wxWidgets headers eventually.
- If the error occurs only once or twice, another option is to conditionally call the
W
suffixed member or theA
suffixed member based on the definition of theUNICODE
macro.
- Move
#include <wx/wx.h>
from source files to header files for classes so wx-related types can be used in the member functions
- Convert uses of
CString
to uses ofwxString
, one class at a time- Pay attention to the differences between the member function
CString::Format
and thestatic
member functionwxString::Format
. - Pay attention to the semantics of other string member functions.
CString::Delete(0)
is not the same aswxString::Remove(0)
wxString
does not haveTrimLeft
orTrimRight
, but you can usewxString::find_first_not_of
andwxString::find_last_not_of
members to locate the inner portion of the string you wish to keep and then usewxString::SubString
to extract the inner portion.
- When you have completed this step, you will have an MFC program using
wxString
for internal storage and member functions. You can interoperate with MFC functions through conversions toLPCTSTR
andLPTSTR
. - If a class only used MFC headers for
CString
, then drop the inclusion of MFC headers from the class's header file.
- Pay attention to the differences between the member function
- Convert uses of the
_T
macro towxT
macro and uses ofTCHAR
type to thewxChar
type. - While
wxChar
gives you a portable datatype for character buffers, wxWidgets doesn't provide the corresponding string functions such as_tcscpy
provided by the Windows header<tchar.h>
. - Replace C style (t)string manipulation functions with corresponding
wxString
instances and member functions.
- Change uses of
ASSERT
towxASSERT
- Change uses of
TRACE
towxLogDebug
- MFC uses
CFile
,CStdioFile
andCMemFile
to encapsulate reading and writing from files.- More advanced usages encapsulate socket communication as a file stream.
- wxWidgets has its own networking stream classes to cover those usages.
std::ifstream
andstd::ofstream
are options; both MFC and wxWidgets predate a standard library and evolved file handling classes independently.- wxWidgets uses
wxFile
,wxFFile
andwxTextFile
for basic file I/O. - Switch usages of
CStdioFile
to eitherwxFile
orwxTextFile
, depending on the usage pattern. - Switch code that uses that base class abstraction
CFile
towxFile
.
- Add a dependency on the CMake imported target
wx::core
for code that useswxMessageBox
- Convert uses of
MessageBox
towxMessageBox
- Convert uses of
MessageBeep
towxBell
- Convert use of standard modal dialogs
CFileDialog
is replaced withwxFileDialog
CWinApp::SetRegistryKey
sets the registry key used for application settings, including the most recently used (MRU) file list- Use
wxConfig
in wxWidgets applications to store user settings. - Use
wxFileHistory
to manage the MRU file list.
- Use
<wx/msw/mfc.h>
provides some classes to interoperate between wxWidgets and MFC.- Include this header in the source file where your
CWinApp
derived class is declared - Change the base application class from
CWinApp
towxMFCWinApp
- Include this header in the source file where your
<wx/nativewin.h>
declareswxNativeWindow
andwxNativeContainerWindow
wxNativeWindow
is for using native windows inside wxWidgets windowswxNativeContainerWindow
is for creating other wxWindows inside it
- MFC applications can include images as resources in
.rc
files that are loaded programmatically - wxWidgets has an XML based resource system stored in
.xrc
files - Migrate the image resources from MFC resource scripts to wxWidgets xrc files and load
wxImage
orwxBitmap
objects
- Migrate standard controls to their equivalent wxWidgets controls
- Host the wxWidgets controls in a
wxNativeContainerWindow
constructed from the MFCm_hWnd
- Start from the inner-most controls and work outwards
- Use wxWidgets sizer classes to arrange controls within panels
- Once a source file representing a control no longer depends on MFC, remove inclusion of any MFC headers from its source and header files.
- Remove any inclusion of
<wx/msw/mfc.h>
- Once all the child controls are migrated to wxWidget controls, migrate the top-level frame to a wxWidgets frame
- Once a source file representing a top-level frame no longer depends on MFC, remove inclusion of any MFC headers and its source and header files.
- Remove any inclusion of
<wx/msw/mfc.h>
- Change your application class to derive from
wxApp
- Change the application class to create the first top-level frame as a wxWidgets frame.
- Remove inclusion of any MFC headers from application source and header files.
- Remove any inclusion of
<wx/msw/mfc.h>
- Remove the CMake variable setting for
CMAKE_MFC_FLAG
- You may be using other aspects of the Win32 API that are not encapsulated by MFC, e.g. direct use of
CreateProcess
- Look for existing wxWidgets classes that offer the same functionality
- Look for user-contributed components to wxWidgets that offer the same functionality
- If it's missing from wxWidgets directly, chances are someone else may have already solved the same problem
- Create your own wxWidgets oriented class that abstracts the functionality, e.g. your own
wxProcess
classwxProcess
callsCreateProcess
on WindowswxProcess
callsposix_spawn
on linux and macOS
- Create a continuous integration build for your application on linux
- Do this even if you only plan to ship on Windows
- Address any new compilation warnings or errors that result from using gcc/clang to build your code
- Create a continuous integration build for your application on macOS
Thanks for your share.
I have post a message on your youtube channel. I think this is the right place to ask question.
After some time dig in, I have found the problem. The wxMfc sample use "wxMFCWinApp::PreTranslateMessage" to filter windows message between wx between mfc , which not fit into my situation. In my program, as a plugin to rhino application, the CWinApp instance never trigger "PreTranslateMessage" member function, result in there is no simple way to create a normal modal "wxDialog". I guess QtWinMigrate try to avoid this situation by hooking the windows message proc. Now I am trying to use "wxNativeContainer" in CDialog, However I haven't find any example and guides, which makes things hard.