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
sedscript 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
wxWidgetsfrom vcpkg- Add a
vcpkgsubmodule to your git repository - Add a
wxwidgets(note: lowercase) dependency to yourvcpkg.jsonmanifest - Configure CMake with the
CMAKE_TOOLCHAIN_FILEfrom vcpkg to get dependencies. - Add
find_package(wxwidgets CONFIG REQUIRED)to your CMake project
- Add a
- Add
wx::baseas a dependency. This forces a_UNICODEbuild.
- Adapt any ANSI MFC
charstyle string code toTCHARMFC style string code.- Use
CT2Ato convertTCHAR/CStringstyle strings to ANSIcharstrings. - Use
CA2Tto convertchar/std::stringstyle strings to UnicodeTCHARstrings. - Use
TCHARrelated string functions, such as_tcscpy,_stprintfand 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
TCHARand the associatedtstring 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
Wsuffixed member or theAsuffixed member based on the definition of theUNICODEmacro.
- 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
CStringto uses ofwxString, one class at a time- Pay attention to the differences between the member function
CString::Formatand thestaticmember functionwxString::Format. - Pay attention to the semantics of other string member functions.
CString::Delete(0)is not the same aswxString::Remove(0)wxStringdoes not haveTrimLeftorTrimRight, but you can usewxString::find_first_not_ofandwxString::find_last_not_ofmembers to locate the inner portion of the string you wish to keep and then usewxString::SubStringto extract the inner portion.
- When you have completed this step, you will have an MFC program using
wxStringfor internal storage and member functions. You can interoperate with MFC functions through conversions toLPCTSTRandLPTSTR. - 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
_Tmacro towxTmacro and uses ofTCHARtype to thewxChartype. - While
wxChargives you a portable datatype for character buffers, wxWidgets doesn't provide the corresponding string functions such as_tcscpyprovided by the Windows header<tchar.h>. - Replace C style (t)string manipulation functions with corresponding
wxStringinstances and member functions.
- Change uses of
ASSERTtowxASSERT - Change uses of
TRACEtowxLogDebug
- MFC uses
CFile,CStdioFileandCMemFileto 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::ifstreamandstd::ofstreamare options; both MFC and wxWidgets predate a standard library and evolved file handling classes independently.- wxWidgets uses
wxFile,wxFFileandwxTextFilefor basic file I/O. - Switch usages of
CStdioFileto eitherwxFileorwxTextFile, depending on the usage pattern. - Switch code that uses that base class abstraction
CFiletowxFile.
- Add a dependency on the CMake imported target
wx::corefor code that useswxMessageBox - Convert uses of
MessageBoxtowxMessageBox - Convert uses of
MessageBeeptowxBell - Convert use of standard modal dialogs
CFileDialogis replaced withwxFileDialog
CWinApp::SetRegistryKeysets the registry key used for application settings, including the most recently used (MRU) file list- Use
wxConfigin wxWidgets applications to store user settings. - Use
wxFileHistoryto 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
CWinAppderived class is declared - Change the base application class from
CWinApptowxMFCWinApp
- Include this header in the source file where your
<wx/nativewin.h>declareswxNativeWindowandwxNativeContainerWindowwxNativeWindowis for using native windows inside wxWidgets windowswxNativeContainerWindowis for creating other wxWindows inside it
- MFC applications can include images as resources in
.rcfiles that are loaded programmatically - wxWidgets has an XML based resource system stored in
.xrcfiles - Migrate the image resources from MFC resource scripts to wxWidgets xrc files and load
wxImageorwxBitmapobjects
- Migrate standard controls to their equivalent wxWidgets controls
- Host the wxWidgets controls in a
wxNativeContainerWindowconstructed 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
wxProcessclasswxProcesscallsCreateProcesson WindowswxProcesscallsposix_spawnon 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