Program 5-5)
sortMM (Program 5-5) uses pointers extensively and, in particular, performs pointer arithmetic. Migrating this program so that it will build and run under both Win32 and Win64 illustrates the normal techniques used and also demonstrates how easy it is to make assumptions about pointer size.
Using Compiler Warnings
Code inspection is important to detect and remove Win64 problems, but it is always advisable to use the compiler, or some other tool, to scan the code and issue warnings.Microsoft's C++ compiler included with Microsoft Visual Studio 7.0 (.NET) can be configured to issue these warnings. Simply set the -Wp64 and -W3 options on the compiler command line. Within Visual Studio, set these options as follows.
- Select the Project Properties page.
- Open the C++ folder.
- Click on General.
- Select Detect 64-bit Portability Issues and select Yes (/Wp64). Leave the warning level at 3.
Then, when you build the project, the output window will display relevant warning messages. All the Microsoft Visual Studio 7.0 projects from the book's Web site have this warning set.
Premigration Code
Most of sortMM.c is free of warnings but one segment at Step 6 (see Program 5-5) produces several typical warnings. Program 16-1 shows the code fragment along with line numbers. Note that line numbers may change in later versions of this program.
Program 16-1. sortMM.c: Before Win64 Migration, Part 1
The compiler warnings are listed next, but, before looking at them, you might want to scan the code for potential warnings. Bear in mind that the objective is for the program to be buildable and to operate correctly in both Win32 and Win64 modes.
. . .
54 LPBYTE pXFile = NULL, pX;
55 TCHAR _based (pInFile) *pIn;
. . .
130
131 if (!NoPrint)
132 for (iKey = 0; iKey < FsX / RSize; iKey++) {
133 WriteFile (hStdOut, &ChNewLine, TSIZE,
&nWrite, NULL);
134
135 /* The cast on pX is important, as it is a pointer to a
136 * byte and we need the four bytes of a based pointer. */
137 pIn =
(TCHAR _based (pInFile)*) *(LPDWORD) pX;
138
139 while ((*pIn != CR || *(pIn + 1) != LF)
&& (DWORD) pIn < FsIn) {
140 WriteFile (hStdOut, pIn, TSIZE,
&nWrite, NULL);
141 pIn++;
142 }
143 pX += RSize;
144 }
Compiler Warnings
The compiler warnings for this code segment clearly show an assumption that a pointer is 4 bytes.
SORTMM.C(137) : warning C4312: 'type cast' : conversion from
'DWORD' to 'TCHAR __based(pInFile) *' of greater size
SORTMM.C(139) : warning C4311: 'type cast' : pointer truncation
from 'TCHAR __based(pInFile) *' to 'DWORD'
The first warning (line 137) is appropriate. Dereferencing pX, after it is cast to a LPDWORD, produces a 32-bit quantity that is then assigned to pIn, a pointer. Dereferencing pIn will almost certainly cause an exception or some other serious error. The correct solution for line 137 is to replace the LPDWORD cast to a LPTSTR pointer, as follows:
pIn = (TCHAR _based (pInFile)*) *(DWORD_PTR) pX;
The warning for line 139 is interesting because we are comparing a based pointer to the file size. Assuming that the file is not huge, the warning can be ignored. Having said that, the warning for line 137 could also be ignored. However, let's take the long view and prepare for huge files, even though FsSize is currently a DWORD. Allowing for the full pointer range, line 139 becomes:
while ((*pIn != CR || *(pIn + 1) != LF) &&
(SIZE_T)pIn < (SIZE_T)FsIn) {
A second segment, in Step 2b, produces additional truncation warnings. Program 16-2 shows the code fragment.
Program 16-2. sortMM.c: Before Win64 Migration, Part 2
The compiler warnings are as follows:
...
40 DWORD KStart, KSize;
174 /* Step 2b: Get first key; determine key size & start. */
175
176 KStart = (DWORD) pInScan;
177 /* Computed start of key field. */
178 while (*pInScan != ' ' && *pInScan != '\t')
pInScan++;
179 /* Computed end of key field */
180
181 KSize = ((DWORD) pInScan - KStart) / TSIZE;
SORTMM.C(176) : warning C4311: 'type cast' : pointer
truncation from 'TCHAR __based(pInFile) *' to 'DWORD'
SORTMM.C(181) : warning C4311: 'type cast' : pointer truncation
from 'TCHAR __based(pInFile) *' to 'DWORD'
The correction is to use DWORD_PTR as the date type in line 40 and in the casts on lines 176 and 181.Additional warnings of the same nature occur in Step 2c at the end of the CreateIndexFile function. The book's Web site contains the modified file, sortMM64.c, which can be used for both Win32 and Win64, and it eliminates all the warnings.
Warnings and Changes in Other Programs
All the book's example projects on the Web site are set to give 64-bit warnings. Most programs compiled without warnings, and no changes were necessary.atouEX (Program 14-2), however, required several changes to use DWORD_PTR for the integer stored in the hEvent field of an overlapped structure. This is because the HANDLE date type is 64 bits in Win64. The changes are noted in the listing on the book's Web site.Some warnings can be ignored. For example, functions such as strlen() return a size_t value. Frequently, a string length will be assigned to a DWORD, causing a "loss of precision" warning. This warning can be ignored in all practical situations.