Thursday, November 12, 2009

Legacy Code Migration









Legacy Code Migration


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 <setjmp.h> ANSI C header rather than assuming anything about jmp_buf, which must contain a pointer.









    No comments:

    Post a Comment