ATL and MFC Changes: ATL 7.0 and MFC 7.0

Note   Some features mentioned in this topic may not still exist in the current version of Visual C++..

Many improvements have been made to the ATL and MFC libraries since Visual C++ 6.0. Some of these changes may break existing code.

The ATL and MFC DLL files shipping as part of Visual C++ .NET 2002 have been renamed to ATL70.dll and MFC70.dll, respectively.

The Visual C++ .NET ATL and MFC classes are not binary compatible with the same classes from previous releases, and therefore any source code built using mfc42.dll must be rebuilt with Visual Studio .NET. Any DLL or LIB files used by your application must also be rebuilt with Visual Studio .NET.

For example, a library containing an exported function taking CString as a parameter that has been built using Visual C++ 6.0 will give an unresolved external during linking with a Visual C++ .NET project.

ATL 3.0 provided the class CComModule. In ATL 7.0, the functionality previously provided by CComModule is handled by several new classes. See ATL Module Classes for more information.

In versions of ATL up to and including ATL 3.0 in Visual C++ 6.0, string conversions using the macros in atlconv.h were always performed using the ANSI code page of the system (CP_ACP). Starting with ATL 7.0 in Visual C++ .NET, string conversions are performed using the default ANSI code page of the current thread, unless _CONVERSION_DONT_USE_THREAD_LOCALE is defined, in which case the ANSI code page of the system is used as before.

Note that the string conversion classes, such as CW2AEX, allow you to pass a code page to use for the conversion to their constructors. If a code page is not specified, the classes use the same code page as the macros.

For more information, see ATL and MFC String Conversion Macros.

CException is the base class for all exceptions in the Microsoft Foundation Class Library. Because CException is now an abstract base class, you cannot create CException objects directly; you must create objects of derived classes. If you do create an object directly, you will receive an error. For more information, see CException.

In Visual C++ 6.0, it was acceptable to use the following code:

BSTR bstr = SysAllocString(L"Hello");
CString str = bstr;

With new projects under Visual C++ .NET, this will cause the following error under ANSI builds:

error C2440: 'initializing' : cannot convert from 'BSTR' to 

There are now UNICODE and ANSI versions of CString (CStringW and CStringA). To flag any unnecessary overhead incurred by implicit conversions, constructors that take the inverse type (for example, CStringA taking a UNICODE argument, or CStringW taking an ANSI argument) are now tagged as explicit using the following entry in stdafx.h:


To work around this error, do one of the following:

  • Use CStringW to avoid conversion:

    BSTR bstr = SysAllocString(L"Hello");
    CStringW str = bstr;
  • Explicitly call the constructor:

    BSTR bstr = SysAllocString(L"Hello");
    CString str = CString(bstr);
  • Remove the line #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS from stdafx.h.

CTime Class uses the underlying __time64_t data type. In MFC 6.0, CTime used the time_t data type which was then a 32-bit type. The reason for this change is to support times beyond 3:14:07 on January 19, 2038.

The CComEnumImpl::Skip method in versions before ATL 7.0 would not return the correct error code for an input value of 0. It would also handle large input values in an inconsistent manner. These behaviors were fixed in ATL 7.0.

When a tooltip was displayed in CWnd::DestroyWindow, an assertion error would occur. As a result, in MFC 7.0, the following member variables were moved from AFX_THREAD_STATE to AFX_MODULE_THREAD_STATE:

  • CToolTipCtrl* m_pToolTip

  • CWnd* m_pLastHit

  • int m_nLastHit

  • TOOLINFO m_lastInfo

  • int m_nLastStatus

  • CControlBar* m_pLastStatus

When calling a function in a static library or DLL that takes a wchar_t type (note that BSTR and LPWSTR resolve to wchar_t*), you may get an LNK2001 unresolved external symbol error.

This error is caused by the /Zc:wchar_t compiler option, which is set to on by default in new MFC projects. This option causes the compiler to treat wchar_t as a native type. Before Visual C++ .NET, wchar_t was treated as an unsigned short.

If the main project and library do not use the same setting for /Zc:wchar_t, this will cause a mismatch of function signatures. To avoid this problem, rebuild the library with the /Zc:wchar_t compiler option, or turn it off in the main project using the Treat wchar_t as Built-in Type setting on the Language property page in the Property Pages dialog box.

Consider the following class:

class CMyClass : public CObject
   BOOL bFlag;

   void Serialize (CArchive& ar))
      if (ar.IsStoring())
         ar << (bFlag != FALSE); // breaking change
         ar >> bFlag;

Before Visual C++ .NET, the expression bFlag != FALSE evaluated as a BOOL and four bytes were written; in Visual C++ .NET, it evaluates as a bool and one byte is written. This means that programs compiled with different versions of the compiler may produce mutually incompatible data files.

To avoid the problem, cast the expression to BOOL:

ar << (BOOL)(bFlag != FALSE);

In previous versions of MFC, an ActiveX control displayed property pages for color or font properties by specifying the GUID CLSID_CColorPropPage or CLSID_CFontPropPage, respectively. These GUIDs pointed to the classes CColorPropPage and CFontPropPage, which are no longer implemented. Instead, use the GUIDs CLSID_StockColorPage and CLSID_StockFontPage. These are implemented by msstkprp.dll, so you must redistribute that DLL with your application.

The function parameter in the ON_MESSAGE macro must match the type afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM).

You can find more information on changes in the OLE DB Templates as described in the Knowledge Base article "INFO: Porting Issues with Visual Studio .NET OLE DB Provider Template Classes" (Q321743). You can find Knowledge Base articles on the MSDN Library CD-ROM or at

OLE DB Consumer Classes and Templates

As a general note, the accessor class must implement additional members. This is only necessary if you implement your own accessor class manually. If your accessor class derives from CAccessor, you need not do this.

Old behavior

New behavior

CRowset is a class.

CRowset is a class template and takes one parameter, TAccessor, an accessor class.

CBulkRowset is a class.

CBulkRowset is a class template.

The base class for CArrayRowset was a template parameter (default value was CRowset).

CArrayRowset always derives from CBulkRowset.

CDynamicAccessor::GetColumnInfo took three parameters.

CDynamicAccessor::GetColumnInfo has a new form that takes an additional parameter, ppStringsBuffer. Using this parameter eliminates a memory leak. The old method is deprecated.

Rowset, second parameter of the CAccessorRowset template, is a rowset class.

TRowset, second parameter of the CAccessorRowset template, is a rowset class template.

Rowset, second parameter of the CTable template, is a rowset class.

TRowset, second parameter of the CTable template, is a rowset class template.

Rowset, second parameter of the CCommand template, is a rowset class.

TRowset, second parameter of the CCommand template, is a rowset class template.


DEFINE_COMMAND macro is deprecated. Use DEFINE_COMMAND_EX instead.

OLE DB Provider Classes and Templates:

The internal implementation of many interfaces and methods has changed since Visual C++ 6.0. This may cause compatibility issues depending on whether your application overrides these methods.

Old behavior

New behavior

The rowset/accessor implementation used CSimpleMap/CSimpleArray classes. User-provided collection classes had to be CSimpleMap/CSimpleArray compatible.

The rowset/accessor implementation uses CAtlMap/CAtlArray classes. User-provided collection classes have to be CAtlMap/CAtlArray compatible. In addition, code that calls methods of these collection classes should be reviewed as there are significant differences between the CAtl* and CSimple* classes (parameters, return values, and so on) that can result in run-time errors.

ICommandImpl derived from ICommand.

ICommandImpl is a template that derives from the template's CommandBase argument (the default is ICommand).

ICommandTextImpl derived from ICommandImpl<ICommandImpl<T>.

ICommandTextImpl derives from ICommandImpl<ICommandImpl<T, ICommandText>. Note that here ICommandImpl derives from ICommandText (not the default ICommand).

Community Additions