Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Porting MFC applications to Linux

A step-by-step guide to using wxWindows

Markus Neifer (howlingmad@users.sourceforge.net), Software Engineer, Freelance
Markus Neifer first programmed with the help of the LOGO turtle and used various flavors of BASIC after that. During his studies of geoinformatics he learned some C but moved quickly to C++ and Java because of their object-oriented nature. He has worked in R&D where he has published articles on object-oriented development of scientific software. Currently he's working as a software engineer in the field of geographic information systems.

Summary:  Porting Windows applications to Linux doesn't have to involve a retraining nightmare. Markus Neifer shows how to port MFC using wxWindows, giving a user's guide to this open source GUI toolkit and providing a complete, step-by-step porting example.

Date:  01 Apr 2002
Level:  Introductory
Also available in:   Japanese

Activity:  30228 views
Comments:  

Still maintaining that legacy Windows application built using Microsoft Foundation Classes (MFC), but now you have clients requesting a Linux version? You may have highly skilled MFC developers on your team, but how do you come up to speed with Linux development? Don't panic; this article is for you. With the help of wxWindows, a portable GUI toolkit for C++ and Python, I'll show you how to port a Windows-only MFC application to Linux using a Multiple Document Interface (MDI) text editor as an example. A small application like this helps focus the discussion on the nuts and bolts of porting the framework and prevents us from getting lost in a sea of code. Complete source code for both the MFC application and the wxWindows application is available in the Resources section later in this article.

Document/view overview

The application I'll illustrate uses the well-known document/view architecture, since it deals with documents as most applications do. Even if your application does not use the document/view architecture, I recommend you read on. You might want to add this feature as long as you're already switching the framework.

In my previous article about wxWindows, I pointed out some similarities between MFC and wxWindows. The string classes CString and wxString and the event system are very close to each other. But the similarities do not end here. The wxWindows toolkit also provides MFC-like support for the document/view architecture.

I'll start with a comparison of the core classes. The following table lists the classes involved in the document/view architecture for both frameworks.

Table 1. Document/view class comparison

Class MFC class wxWindows class
DocumentCDocumentwxDocument
ViewCViewwxView
Edit viewCEditViewn/a
Template classCMultiDocTemplatewxDocTemplate
MDI parent frameCMDIFrameWndwxDocMDIParentFrame
MDI child frameCMDIChildWndwxDocMDIChildFrame
Document managern/awxDocManager

Except for the edit view class, each MFC class has its wxWindows counterpart. (The last item is empty for MFC, because MFC does not have a separate document manager class. The documents are handled internally by the application class CWinApp.) The following UML diagrams show the relationships between the classes:


Figure 1. MFC classes
MFC classes

Figure 2. wxWindows classes
wxWindows classes

The application

Each framework provides a class that represents the application itself. The MFC application class declares a constructor, a method for initialization, a method for event handling, and a message map. You need the message map declaration and the event handling method, because the about dialog of the application will be handled by this class.


Application class: MFC

class CPortMeApp : public CWinApp
{
public:
    CPortMeApp();
    virtual BOOL InitInstance();
    afx_msg void OnAppAbout();
    DECLARE_MESSAGE_MAP()
};

Note: I used the application wizard included in Microsoft Visual Studio to initially create the MFC application, but I won't show the sometimes-confusing wizard-generated comments (//{{AFX_MSG and the like) in my code snippets. See the ZIP archive for full source code.

The wxWindows counterpart looks slightly different. It too declares a constructor and a method for initialization but needs nothing for message handling. As you'll see later, the about dialog is handled in the main frame class.


Application class: wxWindows

class PortedApp : public wxApp
{
  public:
    PortedApp();
    bool OnInit();
    int OnExit();
  protected:
    wxDocManager* m_docManager;
};

This class needs a wxDocManager attribute to handle the templates created in the initialization method OnInit(), as described below. The cleanup method OnExit() will delete this wxDocManager object when the application quits.

Every application needs its entry point (also known as main() or WinMain()). The two frameworks have slightly different approaches to this. In MFC you create a static object of your application class like this:

CPortMeApp theApp;

In wxWindows you use the IMPLEMENT_APP() macro like this:

IMPLEMENT_APP(PortedApp)

If you're interested in what this macro does, take a look at its definition in header file wx/app.h, which you'll find in the downloadable source code. Basically, it will insert the appropriate entry point function for the platform at hand. After an object of the application class is created, you need to initialize this object. For MFC, Microsoft recommends not using the application object's constructor to initialize the object. Instead, you should use its InitInstance() method. To perform any cleanup, implement the ExitInstance() method.

While application initialization has a lot to it, I'll focus on document/view-related code here. To set up the document/view framework, the InitInstance() method has to create a CMultiDocTemplate, like so:


Setting up the document/view code: MFC

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
    IDR_PORTMETYPE,
    RUNTIME_CLASS(CPortMeDoc),
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CPortMeView));

The wxWindows application provides an OnInit() method to do any initialization work and an OnExit() method for cleanup. To set up the document/view framework in our wxWindows application, the OnInit() method has to create a wxDocTemplate like this:


Setting up the document/view code: wxWindows

m_docManager = new wxDocManager();

wxDocTemplate* pDocTemplate;
pDocTemplate = new wxDocTemplate(
    m_docManager, "Pom", "*.pom", "", "pom", "Pom Doc", "Text View",
    CLASSINFO(PortedDoc),
    CLASSINFO(PortedView));

What happens is basically the same for MFC and wxWindows. The framework needs information about which document relates to which view and what type of document this combination handles. The type includes a descriptive name for the document and the file extension for such documents. Both frameworks use a template to handle this (please note that this has nothing to do with Standard C++ templates).

The MFC CMultiDocTemplate also holds information about what child frame relates to a document, and the template is added to the application object that manages the templates. The wxWindows wxDocTemplate additionally needs a wxDocManager object that manages the templates. Remember that the wxDocManager is an attribute of the application class for the wxWindows application.

After document/view framework initialization is complete, the main frame for the application is created. In the MFC application's InitInstance() method, you create a main frame as follows:


Creating a main frame: MFC

CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
    return FALSE;

m_pMainWnd = pMainFrame;

pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();

The call pMainFrame->LoadFrame(IDR_MAINFRAME) loads all information about the main frame from a resource file.

In wxWindows you can set up your menus, dialogs, and controls from a resource file or you can create them in your code. I prefer to create them in the code, but if you like to separate your code and your resources, you should have a look at the wxWindows resource system (see topic overviews in the wxWindows documentation).


Creating a main frame: wxWindows

m_mainFrame = new MainFrame(m_docManager, (wxFrame*) NULL, "DocView Demo",
                            wxPoint(0, 0), wxSize(500, 400),
                            wxDEFAULT_FRAME_STYLE);

// Set up menu bar...

m_mainFrame->SetMenuBar(menu_bar);
m_mainFrame->Centre(wxBOTH);
m_mainFrame->Show(TRUE);

SetTopWindow(m_mainFrame);

Before I look at what happens after application initialization is complete, let me show you the document and view classes for each framework.


The document

The document holds the file-based data that your application works with. It is responsible for loading this data from file and saving it back to file if necessary. The declaration for the MFC class looks like this:


Document class declaration: MFC

class CPortMeDoc : public CDocument
{
  protected:
    CPortMeDoc();
    DECLARE_DYNCREATE(CPortMeDoc)

  public:
    virtual BOOL OnNewDocument();
    virtual void Serialize(CArchive& ar);

    virtual ~CPortMeDoc();
    DECLARE_MESSAGE_MAP()
};

Please note that this class has a protected constructor. You will never create any objects of this class directly; instead, the framework uses serialization to create a document. Therefore, you need to use the DECLARE_DYNCREATE() macro. The declaration for our wxWindows class looks like this:


Document class declaration: wxWindows

class PortedDoc : public wxDocument
{
  public:
    virtual bool OnSaveDocument(const wxString& filename);
    virtual bool OnOpenDocument(const wxString& filename);
    virtual bool IsModified() const;
    virtual void Modify(bool mod);

  private:
    DECLARE_DYNAMIC_CLASS(PortedDoc)
};

The DECLARE_DYNAMIC_CLASS() macro is necessary, because wxWindows will create objects of this class dynamically just like MFC does.


The view

If your MFC application deals with text documents, it's a very good idea to derive your view class from CEditView. This class already offers basic editing functionality and a text control inside its client window. I follow my own advice here, and the declaration for the MFC view class looks like this:


View class declaration: MFC

class CPortMeView : public CEditView
{
  protected:
    CPortMeView();
    DECLARE_DYNCREATE(CPortMeView)

  public:
    CPortMeDoc* GetDocument();
    virtual void OnDraw(CDC* pDC);
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

    virtual ~CPortMeView();
    DECLARE_MESSAGE_MAP()
};

In wxWindows there's no special edit view (yet). But it's easy to create your own. You just need a wxTextCtrl-derived text control inside the views frame. The view class handles both the control and the frame as class attributes. Therefore the wxWindows declaration looks like this:


View class declaration: wxWindows

class PortedView : public wxView
{
  public:
    MyTextCtrl* textsw;

    PortedView();

    bool OnCreate(wxDocument* doc, long flags);
    void OnDraw(wxDC* dc);
    bool OnClose(bool deleteWindow = TRUE);

  private:
    wxMDIChildFrame* CreateChildFrame(wxDocument* doc, wxView* view);

    wxFrame* frame;

    DECLARE_DYNAMIC_CLASS(PortedView)
};

In addition to the usual event handlers for window creation, window redraw, and window closing, this class has a method that creates the frame and populates its menu bar.

While it's not a good idea to have public attributes, I'm doing it for simplicity here. You should avoid this in your applications (and I will remove this in a future version).


The main frame

Now that you have the document class and the view class to handle and show the data, and you also have an application class to handle the document/view framework, you need a main frame class to interact with the user. Again, both frameworks offer similar functionality while the actual implementations are slightly different. The MFC main frame class holds a status bar and a toolbar as attributes, offers methods to handle window creation, and declares a message map. Please note that this class derives from CMDIFrameWnd because this application has a MDI interface.


Main frame class: MFC

class CMainFrame : public CMDIFrameWnd
{
public:
	CMainFrame();
	virtual ~CMainFrame();

	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

protected:
	CStatusBar  m_wndStatusBar;
	CToolBar    m_wndToolBar;

	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

	DECLARE_MESSAGE_MAP()

private:
	DECLARE_DYNAMIC(CMainFrame)
};

The wxWindows main frame class holds its menu as an attribute, has methods to create its toolbar, and declares an event table plus a method to handle the application's about dialog. Because this application has a MDI interface, this class derives from wxDocMDIParentFrame.


Main frame class: wxWindows

class MainFrame : public wxDocMDIParentFrame
{
  public:
    wxMenu* editMenu;

    MainFrame(wxDocManager* manager, wxFrame* frame, const wxString& title,
              const wxPoint& pos, const wxSize& size, long type);

    void OnAbout(wxCommandEvent& event);
    void RecreateToolbar();

  private:
    DECLARE_CLASS(MainFrame);
    DECLARE_EVENT_TABLE();
};


How it all works

The port is finished. I'll review what was needed to get to this point. The MFC application class (CWinApp-derived) was ported to a wxApp-derived application class, including initialization code. The MFC document class (CDocument-derived) was ported to a wxDocument-derived document class. The MFC view class (CView-derived) was ported to a wxView-derived document class. Besides these document/view-related classes, the MFC main frame class (CMDIFrameWnd-derived) was ported to a wxDocMDIParentFrame-derived class.

In its current version, the application already does quite a bit. You can work with multiple text files. You can open, edit, and save them, and the application maintains a history of recently opened files. If you had to code this functionality from scratch, many more lines of code would be needed -- and more lines of code means more lines to test and maintain. In both frameworks, special command identifiers are used to handle the common document/view-related commands. Common file commands are new, open, and save. Common edit commands are cut, copy, and paste. Other often-used commands are the print command, which is not implemented in this application, and the help command. The following table lists the command identifiers involved in the document/view architecture for both frameworks.

Table 2. Standard command identifiers

MFC wxWindows
ID_FILE_OPENwxID_OPEN
ID_FILE_CLOSEwxID_CLOSE
ID_FILE_NEWwxID_NEW
ID_FILE_SAVEwxID_SAVE
ID_FILE_SAVE_ASwxID_SAVEAS
ID_EDIT_CUTwxID_CUT
ID_EDIT_COPYwxID_COPY
ID_EDIT_PASTEwxID_PASTE
ID_APP_EXITwxID_EXIT
ID_EDIT_UNDOwxID_UNDO
ID_EDIT_REDOwxID_REDO
ID_HELP_INDEXwxID_HELP
ID_FILE_PRINTwxID_PRINT
ID_FILE_PRINT_SETUPwxID_PRINT_SETUP
ID_FILE_PRINT_PREVIEWwxID_PREVIEW

Window managers

If you're serious about Linux development, you may already have done some research and found out that there are different window managers available for Linux. As a former MFC developer, you might find this odd. When developing for Windows, you don't need to worry about different window managers because there's only one available.

When starting out with wxWindows development, you might wonder whether your application will run on both leading window managers, K Desktop Environment (KDE) and GNOME. I have used the wxWindows toolkit on Microsoft Windows NT 4.0, Sun Solaris 2.6 using the Common Desktop Environment (CDE), and Linux 2.2 using KDE without problems. As wxWindows is just a high-level layer build on top of other lower-level GUI toolkits, you have some options for how to build your Linux application. You can use the Motif version (or better yet, the free Lesstif) or the GTK+ version of wxWindows. GTK+ is the base widget toolkit GNOME uses, but it also works well under KDE. I recommend that you use the GTK+ version, because it is usually more current, has more developers working on it, and has more users using it. Therefore, you can expect more help if you ask questions about the GTK+ version on the mailing list or in the newsgroup.

Below are some before-and-after screenshots to give you an idea of what the ported application looks like.


Figure 3. Original MFC application
Original MFC application

Figure 4. wxWindows application on Windows
wxWindows application on Windows

Figure 5. wxWindows application on Linux/KDE
wxWindows application on Linux/KDE

Real-world stories

The wxWindows toolkit is not just another toy for nerds. It's mature and stable, and people are using it in real-world applications to solve real-world problems.

wxWindows project founder Julian Smart, currently with Red Hat, has ported the eCos Configuration Tool to wxWindows. The eCos Configuration Tool is a graphic tool for configuring the eCos embedded operating system. The original MFC application has been ported to wxWindows and Linux in particular (see Resources). The folks at SciTech Software have also used wxWindows to completely redevelop the front-end GUI for their SciTech Display Doctor product. IBM has licensed a special version of SciTech Display Doctor, which is now distributed by IBM as the primary display driver for all of IBM's OS/2 Warp-based operating systems, including the Warp Client, Workspace On Demand, and Warp Server for e-business. SciTech Software is an active contributor to the wxWindows community and has submitted the extensions wxApplet, wxUniversal, and wxMGL. With wxMGL, wxWindows apps can even run on DOS and other embedded operating systems such as QNX, RT-Target, SMX, and others. SciTech Software has fully funded both the wxUniversal and wxMGL projects (see Resources).


Conclusion

This article has shown the basic principles of porting Windows applications that use the MFC document/view framework to Linux using the wxWindows toolkit. The wxWindows toolkit offers several similarities to the MFC framework to help MFC developers quickly come up to speed on Linux development. Armed with this basic knowledge, you should now be able to port your cool killer application to Linux. But never forget: wxWindows is not a Linux-only thing. Maybe it's time to think about a version of your application that runs on other UNIXes or even on the Macintosh.


Resources

  • Download complete source code for both the original and ported applications presented in this article from Markus' home page.



  • For an overview of wxWindows, read Markus' article Looking through wxWindows (developerWorks, February 2001).



  • The wxWindows home page is the main place to go for wxWindows community members. It provides information on and support for wxWindows, including downloads, mailing lists, sample applications, and wxWindows-related tools.



  • Coding with KParts discusses the KParts architecture for graphical components for the K Desktop Environment (developerWorks, February 2002).



  • For more on the K Desktop Environment, go to the KDE home page.



  • Check out the GNOME home page for more information on GNOME.



  • More on the Red Hat eCos Configuration Tool can be found on the Red Hat site.



  • You can get more information on SciTech Display Doctor at the SciTech Software site.



  • More information on the OS/2 Device Driver Pak (which uses SciTech Display Doctor) can be found on the OS/2 Warp page.



  • Find more Linux articles in the developerWorks Linux zone.

About the author

Markus Neifer first programmed with the help of the LOGO turtle and used various flavors of BASIC after that. During his studies of geoinformatics he learned some C but moved quickly to C++ and Java because of their object-oriented nature. He has worked in R&D where he has published articles on object-oriented development of scientific software. Currently he's working as a software engineer in the field of geographic information systems.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=11208
ArticleTitle=Porting MFC applications to Linux
publish-date=04012002
author1-email=howlingmad@users.sourceforge.net
author1-email-cc=