16.5 Traps to Avoid When Managing Program Data

The previous sections in this chapter explained the basics of how Windows manages memory. They provided guidelines for choosing between methods for allocating program data and for effectively using a particular method.

This section focuses on common Windows programming errors that you should avoid when managing program data. Once you understand how Windows manages memory, the following guidelines will be quite clear.

Do not assume the privilege level in which your application is running.

Future versions of Windows may change the privilege-level ring in which applications will run.

Do not use DOS protected-mode interface (DPMI) services in a Windows application.

You can use DPMI services only in a dynamic-link library, and only the DPMI services not provided by Windows. Do not use DPMI services for hooking interrupts or faults. The DPMI specification does not provide for unhooking chained interrupts.

Avoid far pointers to static data in small and medium models.

Suppose a module contains the following declaration:

/* DO NOT FOLLOW THIS EXAMPLE. */

static LPSTR lpstrDlgName = "MyDlg";
      .
      .
      .

    hDlg = CreateDialog(hinst,
        lpstrDlgName,
        hWndParent,
        (DLGPROC) lpDialogProc);

The LPSTR (char FAR *) pointer initially set by the Windows loader will be made invalid if the automatic data segment that contains the literal MyDlg moves in memory (unless the automatic data segment is a fixed segment).

The proper way to write the preceding code is to declare the string with a near pointer, PSTR (char NEAR *), and cast it to the LPSTR data type required by the CreateDialog function, as shown in the following example:

/* FOLLOW THIS EXAMPLE. */

static PSTR pstrDlgName = "MyDlg";
   .
   .
   .

   hDlg = CreateDialog(hinst,
        (LPSTR) pstrDlgName,
        hWndParent,
        (DLGPROC) lpDialogProc);

The cast to LPSTR dynamically pushes the current value of the DS register instead of the value of DS at the time the module was loaded.

Do not pass data to other applications by means of a global handle.

You should not use a global handle to share data with another application, because you should assume that your application and other Windows applications have disjoint address spaces.

In future versions of Windows, the address spaces of applications may be disjoint.

The only methods supported by Windows to pass data between applications are the clipboard and the dynamic data exchange (DDE) protocol. If you pass a global handle through DDE to another application, the global object must have been allocated with the GMEM_DDESHARE flag. To share memory, you should always use DDE.

Do not assume any relationship between a handle and a far pointer in any mode.

When using global memory objects, you must always call the GlobalLock function to dereference a handle to a far pointer, regardless of the mode in which Windows is running.

Do not load a segment register with a value other than one provided by Windows or MS-DOS.

In Windows, segment registers are interpreted as selectors, not physical paragraph addresses. Therefore, you should not read the interrupt table by setting ES or DS to zero, for example. Use only the appropriate MS-DOS function to hook an interrupt vector.

Do not perform segment arithmetic.

Do not increment the segment address of a far pointer in an attempt to increment the pointer. This technique is not supported in Windows. For more information, see Section 16.2.3.2, “Locking and Unlocking a Global Memory Object.”

Do not compare segment addresses.

Do not compare the selector values that Windows assigns to memory objects to determine which object is lower in memory. This technique is not supported in Windows.

Do not read or write past the ends of memory objects.

Do not read or write past the ends of memory objects under any circumstances. Although this may go undetected in other memory configurations, Windows will report this error as a GP fault.