The Windows Uniform Data Model is designed to minimize source code changes, but it is impossible to avoid modification altogether. For example, functions that deal directly with memory allocation and memory block sizes, such as HeapCreate and HeapAlloc (Chapter 5), must use either a 32-bit or 64-bit size field, depending on the model. Similarly, you need to examine code carefully to ensure that there are no hidden assumptions about the sizes of pointers and size fields.
API changes, primarily to the memory management functions, are described first.
API Changes
The most significant API changes are in the memory management functions introduced in Chapter 5. The new definitions use the SIZE_T data type (see Table 16-2) in the count field. For example, the definition of HeapAlloc is:
LPVOID HeapAlloc (
HANDLE hHeap,
DWORD dwFlags,
SIZE_T dwBytes);
The third field, the number of bytes requested, is of type SIZE_T and is therefore either a 64-bit or 32-bit unsigned integer. Previously, this field was defined to be a DWORD (always 32 bits).
SIZE_T is used as required in Chapter 5.
Changes to Remove Assumptions about Data Item Size
There are numerous potential problems based on assumptions about data size. Here are a few examples.
-
A DWORD is no longer appropriate for a memory block size. Use SIZE_T or DWORD64 instead.
-
Communicating processes, whether on the same system or on different systems, must be careful about field lengths. For instance, the socket messages in Chapter 12 were defined with LONG32 length fields to ensure that a port to UNIX or Win64 would not result in a 64-bit field. Memory block sizes should be limited to 2GB during communication between Windows processes that use different models.
-
Use sizeof to compute data structure and data type lengths; these sizes will differ between Win32 and Win64 if the data structure contains pointers or SIZE_T data items). Literal size constants should be removed (this, of course, is always good advice).
-
Unions that mix pointers with arithmetic data types should be examined for any assumptions about data item size.
-
Any cast or other conversion between a pointer and an arithmetic type should be examined carefully. For instance, see the code fragments in the Example: Using Pointer Precision Data Types section.
-
In particular, be wary of implicit casts of 32-bit integers to 64-bit integers in function calls. There is no assurance that the high-order 32 bits will be cleared, and the function may receive a very large 64-bit integer value.
-
Pointers are aligned on 8-byte boundaries, and additional structure padding caused by alignment can increase data structure size more than necessary and even impact performance. Moving pointers to the beginning of a structure will minimize this bloat.
-
Use the format specifier %p rather than %x to print a pointer, and use a specifier such as %ld when printing a platform scaled type such as SIZE_T.
-
setjmp and longjmp should use the ANSI C header rather than assuming anything about jmp_buf, which must contain a pointer.
Example: Migrating sortMM (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
. . .
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 }
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.
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
...
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;
The compiler warnings are as follows:
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.
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.
Share with your friends: |