16.2.2 Managing Local Dynamic-Data Objects

In Windows, a local heap can be set up in any data segment. The application's automatic data segment, however, is by far the most common place a local heap is used.

The LocalInit function establishes a specified area within any data segment as a local heap. Calls to LocalAlloc and other local memory functions operate on the data segment currently referenced by the DS register. As long as this data segment has been initialized by LocalInit, the local memory functions will work.

If you are developing a dynamic-link library that requires a local heap, you should call LocalInit during the initialization of the library. If you are developing a Windows application, as opposed to a dynamic-link library, you should not call LocalInit for the application's automatic data segment. Based on the location of other data in the automatic data segment (the task header, static data, and stack) and the heap size specified in the application's .DEF file, Windows itself calls LocalInit with the correct values for the location and size of the local heap.

The organization of a local heap is similar to that of a global heap:

Fixed objects are located at the bottom of the local heap.

Nondiscardable, movable objects are allocated above the fixed objects.

Discardable objects are allocated from the top of the local heap.

The following figure illustrates this organization:

As Windows adds new objects to an application's local heap, movable objects may move as Windows compacts the heap. Also, Windows may discard some objects to make room for new ones. Windows never moves fixed objects when they are allocated in a local heap.

16.2.2.1 Allocating Memory in the Local Heap

By using the LocalAlloc function, you can allocate a specified size object in a local heap and can specify certain characteristics of the object. The most important characteristic is whether the object is fixed or movable, and if movable, whether it is discardable.

When you allocate an object in a local heap, other objects may be moved or discarded. In certain cases, you may not want the local heap to be reorganized as the new object is added. You may want to guarantee that pointers previously set to movable objects remain unchanged. To guarantee that no objects will be discarded from the local heap when you call LocalAlloc, set the LMEM_NODISCARD flag in the wFlags parameter. To guarantee that no objects in the local heap will be moved or discarded, specify the LMEM_NOCOMPACT flag.

LocalAlloc returns a handle to the allocated local memory object. If memory in the local heap is not available, LocalAlloc returns NULL. In managing an object using all other Windows memory functions described below, you should use the handle returned by LocalAlloc.

16.2.2.2 Locking and Unlocking Local Memory Objects

To many C programmers who are used to using the C run-time library function malloc, using memory handles may seem foreign at first. Because allocated objects in the local heap may move around as new objects are added, you cannot always expect a pointer to an allocated object to remain valid. The purpose of a local memory handle is to provide a constant reference to a movable object.

Since a memory handle is an indirect reference, you must dereference the handle to obtain the near address of the local object. You do this by calling the LocalLock function. This function temporarily fixes the object at a constant location in the local heap. This means that the near address returned by LocalLock will remain valid until you subsequently call LocalUnlock. The following example shows how to use LocalLock to dereference the handle of a movable object.

HLOCAL hLocalObject;
char NEAR * pcLocalObject;

/* NEAR is not necessary in small and medium models. */

if (hLocalObject = LocalAlloc(LMEM_MOVEABLE, 32)) {
    if (pcLocalObject = LocalLock(hLocalObject)) {

        /*
         * Use pcLocalObject as the near address of the locally
         * allocated object.
         */
          .
          .
          .

        LocalUnlock(hLocalObject);
    }

    else {

        /* The lock failed. React accordingly. */

    }
}

else {

    /* The 32 bytes cannot be allocated. React accordingly. */

}

If you allocate a local memory object and specify the LMEM_FIXED attribute, the object is already guaranteed not to move in memory. Consequently, you need not call LocalLock to lock the object temporarily at a fixed address. Also, you need not dereference the handle, as you normally would by using LocalLock, because the 16-bit handle is simply the 16-bit near address of the local memory object. The following example illustrates this:

char NEAR * pcLocalObject;

/* NEAR is not necessary in small or medium models. */

if (pcLocalObject = LocalAlloc(LMEM_FIXED,32)) {

    /*
     * Use pcLocalObject as the near address of the locally
     * allocated object. It is not necessary to lock and unlock
     * the fixed local object.
     */
      .
      .
      .

}

else {

    /* The 32 bytes cannot be allocated. React accordingly. */

}

You should avoid leaving a movable object locked if your application needs to allocate other objects in the local heap. Otherwise, memory management in Windows is less efficient, since Windows has to work around the locked object while attempting to make room for another object in the movable area of the local heap.

16.2.2.3 Changing a Local Memory Object

You call the LocalReAlloc function to change the size of a local memory object but still preserve its contents. If you specify a smaller size, Windows truncates the object. If you specify a larger size, Windows fills the new area of the object with zeros if you specify LMEM_ZEROINIT; otherwise, the contents of the new area are undefined. Calling LocalReAlloc may cause objects in the local heap to be discarded or moved, just as when you call the LocalAlloc function. To prevent Windows from discarding objects, specify LMEM_NODISCARD; to prevent Windows from moving objects, specify LMEM_NOCOMPACT.

You can also call LocalReAlloc to change the object's attribute from LMEM_MOVEABLE to LMEM_DISCARDABLE or vice versa. To do so, you must also specify LMEM_MODIFY, as follows:

hLocalObject = LocalAlloc (32, LMEM_MOVEABLE);
  .
  .
  .

hLocalObject = LocalReAlloc(hLocalObject,
    32, LMEM_MODIFY | LMEM_DISCARDABLE);

You cannot use LMEM_MODIFY with LocalReAlloc to change the attribute of the local memory object to or from LMEM_FIXED.

16.2.2.4 Freeing and Discarding Local Memory Objects

The Windows functions LocalDiscard and LocalFree discard and free local objects, respectively.

There is a difference between freeing a local object and discarding it. When you discard a local object, its contents are removed from the local heap, but its handle remains valid. When you free a local object, not only are its contents removed from the local heap, but its handle is removed from the table of valid local memory handles. A local object can be discarded or freed only if there are no outstanding locks on it.

You may want to discard an object rather than free it, if you want to reuse its handle. To reuse the handle, call the LocalReAlloc function, specifying the handle and a nonzero size value. By reusing the handle in this way, you save Windows the time required to free an old handle and create a new one. Reusing a handle also allows you to determine how much local memory is available before attempting to allocate a local memory object.

16.2.2.5 Retrieving Information About a Local Memory Object

The LocalSize and LocalFlags functions provide you with information about a local memory object. LocalSize returns the size of the object. LocalFlags indicates whether the memory object is discardable and, if so, whether it has been discarded. LocalFlags also reports the lock count for the memory object.