Now for the details. This is the general syntax of the GlobalAlloc call:
hGlobalMemory = GlobalAlloc (wFlags, dwBytes) ;
The dwBytes parameter is a double word (unsigned long). This value can be greater than 65,536, but there are special considerations in using global memory blocks larger than 64 KB. (These will be discussed later.)
The wFlags parameter can be a combination of several identifiers that are combined with the C bitwise OR operator. You first have a choice of three identifiers to define the attribute of the allocated memory block:
GMEM_FIXED—Memory is fixed. (This is the default if wFlags is 0.)
GMEM_MOVEABLE—Memory is moveable.
GMEM_DISCARDABLE—Memory is discardable. This option should be used only with GMEM_MOVEABLE. I'll discuss later how you can manage discardable global memory in your programs.
With any of the above three flags, you can use the GMEM_ZEROINIT flag for convenience; this flag tells Windows to initialize memory contents to 0.
You can use two more flags to tell Windows what to do if not enough free memory exists in the global heap. When Windows attempts to allocate the block requested by GlobalAlloc, it first searches to see if a large enough free block exists already. If not, Windows begins moving blocks of memory that are moveable and not currently locked. If that still doesn't generate enough space, Windows begins discarding blocks that are marked as discardable and not currently locked, again moving moveable unlocked segments. You can inhibit this action by using one of two flags:
GMEM_NOCOMPACT—Windows will neither compact memory nor discard memory when attempting to allocate the block.
GMEM_NODISCARD—Windows will not discard discardable global memory when attempting to allocate the block. Windows may still compact memory by moving moveable blocks.
If your program implements Dynamic Data Exchange (DDE), you'll need to use the GMEM_DDESHARE flag to allocate blocks of memory that are shareable among multiple programs. I'll discuss this in Chapter 17.
WINDOWS.H includes two shorthand flags for the most common global memory allocations. The flag GHND (which stands for ”global handle“) is defined as:
GMEM_MOVEABLE | GMEM_ZEROINIT
The flag GPTR (”global pointer“) is defined as:
GMEM_FIXED | GMEM_ZEROINIT
The name of this flag seems odd. Why is a fixed global block referred to as a ”global pointer“? The answer is given later in this chapter, in the section entitled ”Memory Allocation Shortcuts.“
The hGlobalMemory value returned from GlobalAlloc is a handle to the global memory block. It is NULL if GlobalAlloc could not allocate the requested memory. You should definitely check the return value from GlobalAlloc when allocating global memory.
The function GlobalLock locks the segment in memory by incrementing the lock count and returns a far pointer to type char. You should have a variable declared for this pointer:
LPSTR lpGlobalMemory ;
[other program lines]
lpGlobalMemory = GlobalLock (hGlobalMemory) ;
If hGlobalMemory is valid, GlobalLock can return NULL only if you flagged the memory block with GMEM_DISCARDABLE. The NULL return value indicates that the block has been discarded.
Because GlobalLock is declared as returning a far pointer to type char, you should use casting if you need something different:
DWORD FAR *lpdwGlobalMemory ;
[other program lines]
lpdwGlobalMemory = (DWORD FAR *) GlobalLock (hGlobalMemory) ;
The far pointer returned from GlobalLock points to the beginning of a segment. The offset address is 0. If you need to save pointers to areas within a moveable block, do not save them as far pointers. These far pointers may be invalid the next time you lock the segment. Instead, store an offset from the beginning of the block. For a global block less than 64 KB, for instance, you need save only the offset address (the lower 16 bits) of the pointer.
The GlobalUnlock function decrements the lock count for the hGlobalMemory handle:
GlobalUnlock (hGlobalMemory) ;
Calling GlobalUnlock invalidates the lpGlobalMemory pointer returned from GlobalLock. When the lock count is 0, Windows can move the block in memory.
GlobalLock and GlobalUnlock are fairly fast, so you don't suffer a real performance penalty if you use the two functions liberally. You should definitely not keep a block locked from one message to another. Remember that Windows performs best when memory is moveable. When you make a call to a Windows function, Windows may need to load code into memory. If you have a locked memory block sitting around, Windows may have to discard other segments to make room.
When you are entirely finished with the memory block, you can call:
GlobalFree (hGlobalMemory) ;
Following this call, the hGlobalMemory handle is no longer valid, and the block is freed.