Code Cleaning MFC/ATL Applications for 64-Bit Intel® Architecture

Submit New Article

Last Modified On :   October 2, 2008 5:38 PM PDT
Rate
 


 

Introduction

Microsoft Foundations Classes (MFC*) and Active Template Libraries (ATL*) allow programmers to easily build user interface objects for their programs. These classes and libraries, which have been around since the release of Microsoft Visual C++* 4.0, exist in many applications today. They are a set of object-oriented interfaces that encapsulate most of the Windows* API. They also add an application framework for Windows that provides most of the common user interfaces expected from a Windows program.

A large portion of the existing Windows applications today that need to be ported to the 64-bit Intel® operating environment under Windows will contain some MFC/ATL components. Microsoft has endeavored to port both MFC and ATL, thus hiding most of the work associated with porting these classes and libraries, but developers must still address some porting issues associated with them manually.

In many cases, it requires no effort beyond a simple recompile to run applications that are code-clean for the Itanium® Processor Family on processors that support Intel® Extended Memory 64 Technology (Intel®64). Some limitations and caveats apply to this general rule, notably with regard to assembly, intrinsics, and macros. Details are available from the Industry Developer's Guide.


API Changes
 
The growth of pointers is one of the major issues that developers face when migrating from the 32-bit environment to the 64-bit environment running Windows on Intel® architecture, including the Itanium Processor Family and Intel®64. To address this issue, Microsoft has changed some of the standard MFC/ATL APIs where pointers were used as either function returns or function parameters, creating polymorphic pointer types that allow the return or parameter to grow with the operating system:

DWORD_PTR unsigned long type having pointer precision
INT_PTR signed integral type having pointer precision
LONG_PTR signed long type having pointer precision
UINT_PTR unsigned INT_PTR

ULONG_PTR unsigned LONG_PTR
SIZE_T the maximum number of bytes to which a pointer can refer – or can span; unsigned
SSIZE_T signedSIZE_T


Table 1: Microsoft Polymorphic types
Function Returns

Many of the MFC/ATL functions return a pointer to an object, which causes a problem when porting software to the 64-bit environment where the return type is a non-polymorphic type. The following set of examples illustrates the changes that Microsoft has made to MFC/ATL in order to make it compatible with Win64*.

Example 1: Return type in the MFC API DoModal return. The API originally returned an int that pointed to an instance of the dialog box.

Non-clean code that will create problems on 64-bit operating environments because of pointer truncation:
int COptionSheet::DoModal()

 

Cleaned code uses the INT_PTR type, which will scale to 64 bits:
INT_PTR COptionSheet::DoModal()

 

Example 2: Fix of a function that returns a BOOL but the body of the function returns a pointer.Here the programmer has to decide whether the return from the body can be cast down to an int or if some other method needs to be used. Since SendMessage is a Windows API, changing its return type would be very unlikely. Fortunately, the return is not a pointer, so we can cast to an int.

Non-clean code return of a pointer will cause a pointer truncation warning:

BOOL CMainFrame::OnQueryNewPalette()
{CView* pView = GetActiveView();
if (pView != NULL)
return ( pView->SendMessage(WM_QUERYNEWPALETTE) );
return FALSE;
}

 

Cleaned code casts the return of the function to an int:
return (int)( pView->SendMessage(WM_QUERYNEWPALETTE) );

 

Function Parameters

Microsoft has also changed some of the MFC/ATL API's function definitions where it was passing a pointer parameter that was of a non-polymorphic type. Depending on how old the application is, it may have bad typing of the wparam and lparam parameters. These parameters were changed to different types in the Windows API header files about two years ago. These typing errors were hidden because all the types for Win32* were the same size. The following is a set of simple examples that show these changes.

Example 3: The following code sample fails to build on 64-bit Intel architecture with Win64 because the wparam and lparam types are now defined as polymorphic and will grow with the OS.

Non-clean code pointer type will cause a pointer truncation warning:
LONG CCSDialog::OnHelp(LONG, UINT lParam)

 

By adding the new Windows lparam and wparam types, the code will compile for both 32-bit and 64-bit operating systems.
LONG CCSDialog::OnHelp(WPARAM, LPARAM lParam)

 

Example 4: In the case of SetItemData, Microsoft changed the parameter type of pDesc to a DWORD_PTR, so it would grow with the OS. Therefore, to code-clean instances of the following code, the programmer either needs to have the variable definition changed, or if that is too costly, to cast it to a DWORD_PTR.

Non-clean code pointer type will cause a pointer truncation warning:
SetItemData(nIndex, (pDesc);

 

By casting this pointer to a polymorphic type, we get code that will compile for both 32-bit and 64-bit operating systems:
SetItemData(nIndex, (DWORD_PTR)pDesc);

 

There are many other instances where Microsoft has changed function definitions. In most of these cases, the compiler will catch the mismatch and throw out a 'pointer truncation warning'. The best place to find the new definitions is in the SDK.

WIN32* Macros Redefined

Some of the Win32* macros have been redefined to handle the larger pointer types in Win64. Some of these are LOWORD, HIWORD, LOBYTE and HIBYTE, which are defined in windef.h. The following is another simple example of these changes.

Example 5: Since these macros are already code cleaned, in most cases, all the programmer needs to do is remove the cast. The following code doesn't need the cast to compile cleanly for either 32-bit or 64-bit architectures:

VERIFY(m_strText.LoadString(LOWORD((DWORD)lpszText)));
VERIFY(m_strText.LoadString(LOWORD(lpszText)));

 


MFC Data Serialization

The MFC data serialization class CArchive provides a simple way to save user data to disk or other medium. In fact, the programmer could create their own data class or structure and with the MFC Serialize member function, write or read the whole class to disk or other media. This should all work well when serializing across Win32/Win64 operating systems, and Microsoft has set up the serialization classes to seamlessly handle non-polymorphic types without special coding or callback functions. There is a problem, however, with serializing classes or structures that contain polymorphic types. In this case, any unmodified polymorphic types that a Win32 operating system tries to read will cause file I/O errors.

If the application designer must use polymorphic types in his/her classes, then they will need to create a callback function like SerializeElements, which is used with the CMap array class that would type the elements to a supported type for the OS. An example of this technique follows.

Example 6: The following code sample shows a simple class that contains a polymorphic type as one of its data members.

class CMyPoint
{
public:
// CMyPoint();
public:
// Attributes
int x;
INT_PTR y;
//
 Operations
public:>
// ~CMyPoint();
};

 

In order to allow this class to be serialized cleanly on both Win32 and Win64, the designer will need to cast the specific data member to the appropriate operating system. SerializeElements is one example of a built-in callback function for the CMap class. The following shows the usage of this class to fix the file I/O problem.
void SerializeElements(CArchive& ar, CMyPoint* ppElements, int nCount) {
if (ar.IsStoring())
{
}
else
{
for (int i = 0; i < nCount; i++)
{
CMyPoint* pMyPoint = new CMyPoint;
*(ppElements + i) = pMyPoint;
ar >> (INT_PTR)y;
}
}
...

 


Conclusion

Microsoft has made many changes to the base Windows API that have affected many of the base MFC and ATL APIs. These changes were hidden in the Win32 operating system because of the IPL32 coding standard, but with the growth of pointers to 64 bits on 64-bit Windows, these changes need to be addressed. Microsoft has fully ported both the MFC and the ATL classes for the developer, so porting individual applications should be much easier than porting a Windows application that doesn't use MFC/ATL.

Microsoft has ported these classes with data compatibility in mind, so that an application running on a Win32 operating system should be able to read/write to the same application on a Win64 operating system. Microsoft has created a new set of polymorphic pointer and size types that will help the developer code-clean his or her application.


Additional Resources