TN019: Updating existing MFC Applications to MFC 3.0

This technical note primarily provides guidelines for migrating MFC 1.0 applications to the MFC 2.0 tools. Additional differences between MFC 2.0, 2.5, and 3.0 are presented in the first section, below.

MFC 4.0/3.0 API Changes

There are no known changes to the documented MFC APIs that would cause existing code to require changes. There are, of course, many additional features you may want to take advantage of. For more information on these features, see Visual C++ Programmer's Guide.

MFC 2.5 API Changes

MFC 2.5 added two major features to the class library: OLE 2.0 support, which replaced the OLE 1.0 support and ODBC support which provides database access. It is the intention of this technote to cover the API changes that may affect your existing code. For information about these new features, not covered in this technote, see Visual C++ Programmer's Guide.

CFrameWnd::RecalcLayout has an additional parameter, BOOL bNotify. This specifies whether or not to notify any OLE servers that they layout has changed. It is normally TRUE and therefore that is the default. This function is virtual, so if your program provides an override of this function you will need to add the extra parameter to your function as well.

There were other changes made to undocumented functions in the MFC 2.5 libraries to support OLE 2.0 as well as ODBC. If your program uses undocumented MFC APIs, you should review all such uses to make sure they are still valid.

Migrating MFC 1.0 Applications to MFC 2.0

IMPORTANT: To understand and evaluate the two approaches presented below, you must be familiar with MFC 2.0 concepts, such as the Document and View architecture, and the tools. We suggest you at least work through the MFC Tutorial sample SCRIBBLE in Tutorials before beginning any migration of existing code.

There are two basic approaches to migrating existing applications from MFC version 1, released with Microsoft C7, to MFC version 2.

Minimal Migration

Using the "Minimal Migration" method, you make only the minimum changes required, so that:

This is the easiest approach, but it does not take full advantage of the rich features in the MFC 2.0 library. Even if you chose the "Full Migration" method, you will need to understand and exercise techniques for minimal migration.

Full Migration

If you perform a "Full Migration", you can take full advantage of the MFC 2.0 library. Using the minimal migration method, you will be able to edit your application using Visual C++ and ClassWizard. By using the full migration method, you gain the following MFC 2.0 support:

The overall full migration method is basically to emulate developing an MFC 2.0 application from scratch, starting with AppWizard. The difference from developing an MFC 2.0 application from scratch, of course, is that you will borrow as much of the MFC 1.0 code you wrote as makes sense.

Minimal Migration

The following subsections present detailed guidelines for performing a minimal migration.

Conforming to Windows 3.1 STRICT typedefs

The default builds of the MFC 2.0 library adhere to the Windows 3.1 STRICT typedefs which are explained in the Windows 3.1 SDK. MFC 1.0 had STRICT turned off, but now MFC 2.0 has STRICT turned on by default. This follows MFC's commitment to track the industry standard Windows API and to foster development practices that make developing robust applications easier. Just as the STRICT typedefs were helpful to the developers of the MFC 2.0 classes to produce robust software, so will that be true for you in developing your application code.

When using STRICT type checking for the first time, many compilation errors will typically result. Modifying your MFC 1.0 application to conform to Windows 3.1 STRICT typedefs may very well represent the bulk of your minimal migration effort.

Once your application complies with STRICT, you may be able to compile an executable with no further changes. For example, the ABOUT2 and FILEVIEW MFC 1.0 samples compile with no additional changes. They were already STRICT compliant and did not used any changed MFC APIs.

MFC 2.0 API Changes

Beyond conforming to STRICT, most of the effort of doing a minimal migration is to identify and change your application code to conform to the relatively few MFC 2.0 API changes.

Out of over 1800 MFC 1.0 APIs, only 20 of the APIs that were changed result in compile-time errors. These changes require only trivial modifications to existing MFC 1.0 applications. The most extensive changes are the architectural restructuring of the OLE classes. These changes are covered in Technical Note 18.

To anticipate which changes you'll need to make, see the section "Alphabetical API Changes" at the end of this technote. It provides a useful, brief summary of which MFC 1.0 APIs were modified in MFC 2.0.

If you do not make all the changes necessary to deal with MFC 2.0 code, you will get various compilation and linking errors. These errors are almost always easy to diagnose. To aid your diagnosis, we provide some guidelines in the section "Compiler Errors" at the end of this technote.

The following MFC APIs have been removed in MFC 2.0. We recommend alternative APIs where appropriate. This list does not include changes to undocumented implementation APIs.

CDC::GetDCOrg

GetDCOrg is not available in Win32. For Windows 3.x only applications, just call the Windows API ::GetDCOrg directly.

CRuntimeClass::m_pszClassName

This member variable is now an LPSTR rather than the memory model-dependent (char*). It is named m_lpszClassName in MFC 2.0.

CMDIChildWnd::m_pMDIFrameWnd

In MFC 1.0, this member variable pointed to the class's MDIFrame parent. This member variable has been replaced with a member function CMDIChildWnd::GetMDIFrame. If you are using multiple document interface (MDI) in MFC 2.0, most uses CMDIChildWnd::m_pMDIFrameWnd (or GetMDIFrame) are no longer necessary since the default MDI support handles all of the standard MDI Windows menu commands.

CFrameWnd::GetChildFrame

Use CMDIFrameWnd::MDIGetActive for MDI frames instead.

The following API has been left in MFC 2.0 to support 1 compatibility but is obsolete. It will be removed from future versions of MFC.

CMDIFrameWnd::CreateClient

This functionality has been replaced by the more general OnCreateClient mechanism that supports view creation and the improved MFC 2.0 MDI support. The original CreateClient can still be used for MDI applications that manage their own MDI frame window's menu bar (by using CMDIFrame::MDISetMenu). The MFC 2.0 MDI support will automatically switch the MDI frame window's menu bar to the menu for the currently active MDI child window.

Other API-related Changes

Two MFC classes have been moved from the afxwin.h to the afxext.h header file:

In your .cpp files that reference these classes add the following:

#include <afxext.h>

Many APIs have been changed so that they are stricter about the use of the 'const' modifier. These changes result in a more consistent use of the LPCSTR type name and the new LPCRECT type name. Note, there is no compile time issue with these changes, since any type can be promoted to a const version of that type when used as an argument. Like the STRICT change, this leads to more robust code when your code uses const data pointers.

The window Create functions listed below now have an additional parameter, but since the last parameter has a default value of NULL, existing code will work without modification. These functions are

The following functions were virtual in MFC 1.0 but are now nonvirtual in MFC 2.0:

If a derived class of your MFC 1.0 application overrides either of these functions, it is unlikely that the function in your derived class will be called in MFC 2.0. In addition, GetParentFrame was moved from CFrameWnd to CWnd to be a more generally useful API.

All static members of classes, as well as global operator/friend functions, now adhere to PASCAL calling conventions. All global functions are AFXAPI (PASCAL). Again, this is not a compile time issue but leads to faster and smaller generated code.

Many of the implementation-only classes and structures have been renamed to not use the 'C' prefix. For example, CExceptionContext has been renamed to AFX_EXCEPTION_CONTEXT. These classes are not documented and remain implementation details of the class library. It is unlikely that you have relied on these, and it is generally recommended that you do not rely on undocumented APIs of the class library since they are subject to change in future versions.

MFC 2.0 Default Behavior Changes

Dealing with MFC API changes is easy with the aid of errors reported by the compiler and linker. Not all library changes are revealed in the library header files, however. Some changes are revealed in run-time behavior of your application. These changes are generally not difficult to deal with, as long as you anticipate them. The following information is provided to help you anticipate such behavioral changes.

CDialog and CModalDialog have been merged into a single class. CModalDialog is now considered to be an obsolete class. However, for MFC 1.0 compatibility, all references to CModalDialog are still valid through a migration macro in afxwin.h:

#define CModalDialog CDialog

For many MFC 1.0 applications, this simple #define is sufficient. However, there are cases where this #define is not sufficient.

If you implemented a modeless dialog and relied on the default "do nothing" behavior for OnOK and OnCancel, then you must override these and the default behavior, since they now call EndDialog (for modal dialog processing).

CDialog::CreateIndirect still creates a modeless dialog. To create a modal dialog use CDialog::InitModalIndirect instead of the removed CModalDialog::CreateIndirect API.

Dialog box and message box background colors can now be globally set using the CWinApp::SetDialogBkColor API. The default parameter sets the color to light gray (not COLOR_BTNFACE) to produce gray backgrounds. You may specify other colors.

If SetDialogBkColor is not called in your CWinApp-derived InitInstance function, the default Window Background color (set in the Control Panel Color applet) is used.

In MFC 1.0, if a DLL contained a CWinApp object, it was necessary to provide a DllMain that included a call to AfxWinTerm. MFC 2.0 provides this DllMain, so any additional code included in the your DllMain should be migrated to the DLL's CWinApp::ExitInstance member function.

CMDIChildWnd::Create now correctly uses the dwStyle parameter. You must now specify a complete window style for the MDI child window. If you specify dwStyle = 0, you will now get an ASSERT failure in CMDIChildWnd::PreCreateWindow. To avoid this, you should specify the style WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW to be backward compatible with MFC 1.0.

MFC 2.0 supports setting different styles for MDI child windows, so you can remove some of the frame window controls, if desired.

Class CFrameWnd has a new data member, BOOL CFrameWnd::m_bAutoMenuEnable. It is set to TRUE by default. This causes menu items that don't have ON_UPDATE_COMMAND_UI or ON_COMMAND handlers to be automatically disabled. Menu items that have ON_COMMAND handlers, but no ON_UPDATE_COMMAND_UI handlers, will be automatically enabled.

This makes it easy to implement optional commands based on the current selection. Also, this greatly reduces the need for applications to write ON_UPDATE_COMMAND_UI handlers for enable/disable of menu items. For instance, an AppWizard-generated application will have Edit Cut/Copy/Paste disabled until the programmer implements handlers for them.

However, if your MFC 1.0 application is not updated to use ON_COMMAND and ON_UPDATE_COMMAND_UI handlers, then it must clear m_bAutoMenuEnable explicitly. Otherwise, menus that you disable will be re-enabled automatically.

Project (Build) Changes

You can continue to build your MFC 1.0 application using a standard makefile. By far the easiest way to migrate your project is to use the Visual C++ project facility to maintain your depedencies and other project options within the Visual C++ environment.

A common link error is unresolved externals to COMDLG32.DLL and SHELL32.DLL APIs. Be sure to link with COMDLG32.LIB and SHELL32.LIB.

You may be able to improve build times by placing #include <afxwin.h> in a precompiled header. By convention, MFC 2.0 applications specify "stdafx.h" as the precompiled header. Then module stdafx.cpp includes stdafx.h. This technique is illustrated by the code created by AppWizard, and by many of the MFC 2.0 samples.

Note   It is important that you neither define nor undefine any of the _AFX_NO_XXX macros in stdafx.h. See the Knowledge Base article "PRB: Problems Occur When Defining _AFX_NO_XXX." You can find Knowledge Base articles on the MSDN Library CD, or at http://www.microsoft.com/kb/.

Visual C++ and ClassWizard Compatibility

Even for minimal migration, we recommend that you follow the steps below so that you can use Visual C++ and ClassWizard to edit your application resources and code.

Full Migration

A full migration of your existing C or MFC 1.0 application to MFC 2.0 will offer you all the advantages of MFC 2.0. For most applications, a full migration is not difficult and is well worth the effort.

A successful full migration of an application to MFC 2.0 requires essentially the same understanding of MFC 2.0 as developing a new application from scratch. You should become familiar with the MFC 2.0 Class Library, Visual C++, AppWizard, and ClassWizard before you begin the full migration. You should understand what portions of your application code can be removed by deriving equivalent or improved functionality from the MFC 2.0 classes. Not only will using more of the library implementation make your source code smaller, but it will make these parts of your application better integrated with the rest of the MFC framework.

By fully migrating your application to MFC 2.0, you will be able to derive additional functionality from MFC at relatively little extra cost. For example, if your application did not have a splitter window user interface, but one would be useful to your users, then you will be able to quickly add this feature, having already ported your code to MFC 2.0's document/view architecture.

Although a full migration to MFC 2.0 may require a couple days effort for large applications, the process itself is fairly straightforward. The following general steps describe the process:

  1. Analyze how your existing application architecture factors into document, views, and frame windows.

    Do this before you start editing any code. Many programmers tend to intertwine document code with view code. Although doing so is not necessarily "bad," separation of document and view functionality is a design philosophy that the MFC framework endorses and supports particularly well. Even though MFC 1.0 did not have CDocument and CView classes, it also endorsed document/view separation. So will all future versions of the library.

    Study the MFC 2.0 samples that use the CDocument and CView classes, particularly the MFC Tutorial Sample SCRIBBLE. Then analyze your application to determine what the document is and what the view is. Determine whether your application has multiple document types or views.

    Even if your application does not lend itself to clear document/view separation, you will still be able to fully migrate to MFC 2.0 and take essentially complete advantage of the framework. You can "fake" document/view separation by implementing CDocument- and CView-derived classes, but your document or view class may delegate most of its work to the other class. Or, your view class might rely on your CFrameWnd- or CMDIChildWnd-derived class to implement the bulk of your application's user interface. In summary, you have almost complete freedom as to how to separate your document, view, and frame window classes.

    Your analysis also should determine whether your application needs multiple view classes and possibly multiple document classes. Even relatively simple applications sometimes need more than one view class. However, multiple views, such as in a splitter window, don't necessarily dictate that you have multiple CView-derived classes. For example, if each pane in the splitter window provides the same user interface as other panes in the splitter window, then they can share the same view class. In that case, each pane is simply a distinct object of the same view class. You'll probably want to design multiple view classes, if your application provides very distinct user interfaces in different windows.

  2. Analyze what AppWizard-supported framework features your application will need.

    AppWizard will create a skeleton MFC 2.0 application that supports various framework features that you select as options in AppWizard dialogs. Before you run AppWizard to create your skeleton application, you should first become familiar with the options AppWizard provides.

    Then, take a little time to decide which of AppWizard's options you'll want to select. Don't attempt to do this in a few minutes the first time you run AppWizard. For example, if your application does not already support OLE, this is a major decision that you'll want to consider. If you didn't chose AppWizard's OLE option to begin with, you'll still be able to modify your application code to use MFC's OLE features. But starting with the OLE option in AppWizard to begin with will save you time.

    Your analysis should determine whether your application is a single document interface (SDI) or multiple document interface (MDI) application. This particular determination should be obvious if you're familiar with these two distinct user interfaces in other Windows applications. AppWizard will create MDI applications by default since the MDI user interface is usually more functional to end users for it lets them open more than one document/file at a time. Fortunately, with the MFC 2.0 document/view architecture, supporting MDI requires no extra coding on your part.

  3. Generate a new application using AppWizard.

    Having done the above analysis, you're now ready to run AppWizard to create the skeleton code for your application.

    Having analyzed how your application separates into document, views, and frame windows, you should have a good idea what names to give to their corresponding classes and modules. You might want to assign somewhat generic names, such as the tutorial sample's CScribDoc and CScribView, and scribdoc.cpp and scribvw.cpp. However, if your application requires multiple view classes, you'll probably want to give the first AppWizard-created view class a more specialized name, such as CDataEntryView and CReportView. See the next step for additional information on creating multiple document and view classes.

    Having anticipated what additional AppWizard options you want, such as SDI or MDI, and OLE, you should now be able to select the AppWizard options and create the skeleton application in just a few minutes.

  4. Optionally, clone second view, document, and frame window classes.

    If your above analysis determines that your application should have multiple view, document, or frame window classes, then it's a good time to create the skeleton code for these classes right after you run AppWizard.

    You can create the skeleton code for your additional view, document, and frame window classes by cloning the ones created by AppWizard. That is, copy the .cpp and .h files, assigning a new module name for the second document or class. Then edit the skeleton code by changing class names. Another alternative is to use the ClassWizard's Add Class functionality to create a new class automatically in the files you specify using the names you specify. You will already be familiar with the ClassWizard's ability to create new classes if you have followed the SCRIBBLE tutorial.

    In either case, in your CWinApp-derived class's InitInstance function, you must register additional document template objects for any associations that you want to make between you multiple document, view, and frame window classes.

    This is also a quick step. You can postpone this step if you're not committed to implementing multiple documents, views, or frame window classes in your application.

  5. Migrate the relevant portions of your MFC 1.0 code into the classes created by AppWizard.

    This step represents the bulk of the work in migrating your MFC 1.0 application to MFC 2.0. You should do this incrementally. Migrate relatively small chunks of your application at a time. As you do this, you'll learn more details about what functionality the framework provides that will allow you to discard some of your old MFC 1.0 application code.

    As you migrate these chunks of code, keep in mind the guidelines presented under "Minimal Migration". Many of those guidelines apply to full migration. AppWizard will have already added the //{{AFX_MSG and //{{AFX_MSG_MAP comments to your command target classes (application, document, view, and frame window). It is not necessary for you manually add these as under the minimal migration approach. Although it is not required, we recommend that you move message-handling functions between the //{{AFX_MSG comments nested in the message maps. Also, move the declarations of these message-handling (afx_msg) functions between the //{{AFX_MSG comments in your header files. Doing so will allow you to use ClassWizard throughout the rest of your project's life cycle(s).

    These recommendations regarding //{{AFX_MSG comments also apply, perhaps to a lesser degree, to dialogs. If you don't anticipate many future changes to a given dialog class, then it might not be worth your effort to make that dialog ClassWizard-aware. That's fine. We recommend, of course, that you create all new dialog classes using ClassWizard's Add Class option.

    As you migrate an MFC 1.0 or Windows application, you may want to maintain compatibility with existing file formats. (The default MFC 2.0 document serialization mechanism may not be appropriate for your application.) To do direct CFile write and read calls, or to implement a non-file based document, you will want to override CDocument::OnOpenDocument and OnSaveDocument. The MFC General sample DIBLOOK provides an example of this technique. If your current application already serializes objects, then this will not be an issue.

Alphabetical API Changes

To understand the reasons for these changes, please refer to "Reason for Changes" below.

API / Variable MFC 2.0 Change (Reason for Change)
CMetaFileDC::Close Return type (2)
CWnd::Create Extra default param added, CWnd* const (1, 3)
CFrameWnd::Create Extra default param added, CWnd* const (1, 3)
CMDIChildWnd::Create Extra default param added, CWnd* const (1, 3)  dwStyle default is now: WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW
CWnd::CreateEx Extra default param added, CWnd* const (1, 3)
CBitmap::CreateBitmap Parameter types (4)
CDC::EnumObjects Callback prototype (2)
CTime::Format Const function (3)
CTimeSpan::Format Const function (3)
CTime::FormatGmt Const function (3)
CFile::GetStatus Nonvirtual (5)
CDC::GrayString Callback prototype and parameter type (2)
CBitmapButton::LoadBitmaps Extra default parameter (1)
CWnd::OnActivateApp Parameter type (2)
CWnd::OnCompareItem Extra parameter (6)
CWnd::OnDeleteItem Extra parameter (6)
CWnd::OnDrawItem Extra parameter (6)
CWnd::OnDropFiles Parameter type (2)
CWnd::OnGetMinMaxInfo Parameter type (6)
CWnd::OnMeasureItem Extra parameter (6)
CWnd::OnMenuChar Return type (2)
CWnd::OnNcCalcSize Extra parameter (6)
CWnd::OnPaintClipboard Parameter type (2)
CWnd::OnParentNotify Parameter type (2)
CWnd::OnSizeClipboard Parameter type (2)
CWnd::OnSysCommand Parameter type (2)
CWnd::OnWinIniChange Parameter type (2)
CDC::PlayMetaFile Parameter type (2)
CEdit::SetSel Extra default parameter (6)
CEdit::SetTabStops Parameter type (5)
CWnd::SetTimer Callback prototype and parameter type (2)
CRuntimeClass::m_pszClassName Renamed m_lpszClassName (5)

Deleted or Obsolete API MFC 2.0 Change (Reason for Change)
CBitmapButton Removed ctor with 3 params - use LoadBitmaps (1)
CMDIFrameWnd:: CreateClient Use OnCreateClient (1)
GetChildFrame Use MDIGetActive (1)
GetDCOrg Use Windows API directly for 3.x (4)
m_pMDIFrameWnd Now call GetParentFrame OR GetMDIFrame (1)

Reasons for Changes:

Compiler Errors

Most changes to the MFC 2.0 APIs will generate one of a few compiler errors, or none at all if standard type conversions satisfy the compiler. The following compiler errors may be generated when compiling existing MFC 1.0 applications under MFC 2.0:

Number Compiler Error Message
Compiler error C2039 'Identifier' : is not a member of ‘class-key.’
This error is caused when a member function or data member has been removed from a class, for example CFrameWnd's m_pMDIFrameWnd.
Compiler error C2501 'Identifier' : missing decl-specifiers.
This error is caused when you use an unknown class name. This is usually the case when the class no longer exists or has been moved to a different header file. For example if you get this error for CMetaFile and CBitmapButton then you must add #include "afxext.h" to the source files using those classes.
Compiler error C2248 'Member' cannot access 'specifier' member declared in class 'class.'
This error occurs if the access of a member has changed from MFC 1.0 to 2. For example, an undocumented API has been moved from public to protected member access. This should only occur in code that is using undocumented and unsupported APIs, which should be changed to use the appropriate MFC 2.0 functionality.
Compiler error C2642 Cast to pointer to member must be from related pointer to member.
This error occurs when the message handler function prototype differs from the one in afxwin.h. For example, a line containing the ON_WM_ACTIVATEAPP macro will emit this error if the parameters and return type of your OnActivateApp message handler match the MFC 1.0 declaration.
Compiler error C2660 'Function' : function does not take 'number' parameters.
The number of parameters has changed from MFC 1.0 to MFC 2.0. For example, calling the CBitmapButton constructor with three parameters causes this error since this particular constructor has been removed and replaced by the LoadBitmaps member function.
Compiler error C2664 'Function' : cannot convert parameter 'number' from 'type1' to 'type2.'
The type of a parameter has changed, and standard conversions do not satisfy the compiler. CDC::EnumObjects is an example of this. In this case, the prototype of the callback function has changed.

Technical Notes by NumberTechnical Notes by Category