Manipulating the Windows Clipboard

This is a discussion on Manipulating the Windows Clipboard within the Windows Programming forums, part of the Platform Specific Boards category; I just started dabbling in pointers, and they fill me with a sense of nearly God-like power. I am also ...

Manipulating the Windows Clipboard

I just started dabbling in pointers, and they fill me with a sense of nearly God-like power. I am also interested in manipulating various aspects of Windows, particularly Windows 98. From thence cometh my question. . . .

How does one manipulate the contents of the Windows Clipboard within his program? Is this possible?

Is the address of the clipboard constant on every machine on every boot?
If so, what's the address?

If not, can I write a loop that will query all available memory locations to check for clipboard data?

Finally, can one set the address for a pointer with hexadecimal numbers? I tried something to the extent of:

Memory Allocation
When your program transfers something to the clipboard, it must allocate a memory block and essentially hand it over to the clipboard. When we've needed to allocate memory in earlier programs in this book, we've simply used the malloc function that is supported by the standard C run-time library. However, because the memory blocks stored by the clipboard must be shared among applications running under Windows, the malloc function is inadequate for this task.

Instead, we must dredge up memory allocation functions that were designed back in the dark ages of Windows, in the days when the operating system ran in a 16-bit real-mode memory architecture. These functions are still supported and you can still use them, but they are not often needed.

To allocate a memory block using the Windows API, you can call

hGlobal = GlobalAlloc (uiFlags, dwSize) ;
The function takes two parameters: a possible series of flags and a size in bytes of the allocated block. The function returns a handle of type HGLOBAL, called a "handle to a global memory block" or a "global handle." A NULL return value indicates that sufficient memory was not available for the allocation.

Although the two parameters to GlobalAlloc are defined a bit differently, they are both 32-bit unsigned integers. If you set the first parameter to zero, you effectively use the flag GMEM_FIXED. In this case, the global handle that GlobalAlloc returns is actually a pointer to the allocated memory block.

You can also use the flag GMEM_ZEROINIT if you'd like every byte in the memory block to be initially set to zero. The succinct GPTR flag combines the GMEM_FIXED and GMEM_ZEROINIT flags as defined in the Windows header files:

#define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
There is also a reallocation function:

hGlobal = GlobalReAlloc (hGlobal, dwSize, uiFlags) ;
You can use the GMEM_ZEROINIT flag to zero out the new bytes if the memory block is being enlarged.

Here's the function to obtain the size of the memory block:

dwSize = GlobalSize (hGlobal) ;
and the function to free it:

GlobalFree (hGlobal) ;
In the early 16-bit versions of Windows, the GMEM_FIXED flag was strongly discouraged because Windows could not move the block in physical memory. In the 32-bit versions of Windows, the GMEM_FIXED flag is normal because it returns a virtual address and the operating system can move the block in physical memory by altering the page table. When programming for the 16-bit versions of Windows, using the flag GMEM_MOVEABLE in GlobalAlloc was instead recommended. (Note that most dictionaries prefer the spelling "movable" over "moveable," so that's how I'll spell the word otherwise.) There's also a shorthand identifier identified in the Windows header files to additionally zero out the movable memory:

#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)
The GMEM_MOVEABLE flag allows Windows to move a memory block in virtual memory. This doesn't necessarily mean that the memory block will be moved in physical memory, but the address that the application uses to read and write to the block can change.

Although GMEM_MOVEABLE was the rule in 16-bit versions of Windows, it is generally less useful now. However, if your application frequently allocates, reallocates, and frees memory blocks of various sizes, the virtual address space of your application can become fragmented. Conceivably, you could run out of virtual memory addresses. If this is a potential problem, then you'll want to use movable memory, and here's how to do it.

First define a pointer (for example, to an int type) and a variable of type GLOBALHANDLE:

hGlobal = GlobalAlloc (GHND, 1024) ;
As with any Windows handle, don't worry too much about what the number really means. Just store it. When you need to access that memory block, call

p = (int *) GlobalLock (hGlobal) ;
This translates the handle into a pointer. During the time that the block is locked, Windows will fix the address in virtual memory. It will not move. When you are finished accessing the block, call

GlobalUnlock (hGlobal) ;
This gives Windows the freedom to move the block in virtual memory. To be really compulsively correct about this process (and to experience the torments of early Windows programmers), you should lock and unlock the memory block in the course of a single message.

When you want to free the memory, call GlobalFree with the handle rather than the pointer. If you don't currently have access to the handle, use the function

hGlobal = GlobalHandle (p) ;
You can lock a memory block multiple times before unlocking it. Windows maintains a lock count, and each lock requires a corresponding unlock before the block is free to be moved. When Windows moves a block in virtual memory, it doesn't need to copy the bytes from one location to another—it needs only manipulate the page tables. In general, in the 32-bit versions of Windows the only real reason for allocating a movable block for your own program's use is to prevent fragmentation of virtual memory. When using the clipboard, you should also use movable memory.

When allocating memory for the clipboard, you should use the GlobalAlloc function with both the GMEM_MOVEABLE and the GMEM_SHARE flags. The GMEM_SHARE flag makes the block available to other Windows applications.

Transferring Text to the Clipboard
Let's assume that you want to transfer an ANSI character string to the clipboard. You have a pointer (called pString) to this string, and you want to transfer iLength characters that might or might not be NULL-terminated.

You must first use GlobalAlloc to allocate a memory block of sufficient size to hold the character string. Include room for a terminating NULL:

hGlobal = GlobalAlloc (GHND | GMEM_SHARE, iLength + 1) ;
The value of hGlobal will be NULL if the block could not be allocated. If the allocation is successful, lock the block to get a pointer to it:

Call OpenClipboard and CloseClipboard while processing a single message. Don't leave the clipboard open any longer than necessary.

Don't give the clipboard a locked memory handle.

After you call SetClipboardData, don't continue to use the memory block. It no longer belongs to your program, and you should treat the handle as invalid. If you need to continue to access the data, make another copy of it or read it from the clipboard (as described in the next section). You can also continue to reference the block between the SetClipboardData call and the CloseClipboard call, but don't use the global handle you passed to the SetClipboardData function. This function also returns a global handle that you can use instead. Lock this handle to access the memory. Unlock the handle before you call CloseClipboard.
Getting Text from the Clipboard
Getting text from the clipboard is only a little more complex than transferring text to the clipboard. You must first determine whether the clipboard does in fact contain data in the CF_TEXT format. One of the easiest methods is to use the call

bAvailable = IsClipboardFormatAvailable (CF_TEXT) ;
This function returns TRUE (nonzero) if the clipboard contains CF_TEXT data. We used this function in the POPPAD2 program in Chapter 10 to determine whether the Paste item on the Edit menu should be enabled or grayed. IsClipboardFormatAvailable is one of the few clipboard functions that you can use without first opening the clipboard. However, if you later open the clipboard to get this text, you should also check again (using the same function or one of the other methods) to determine whether the CF_TEXT data is still in the clipboard.

hGlobal = GetClipboardData (CF_TEXT) ;
This handle will be NULL if the clipboard doesn't contain data in the CF_TEXT format. This is another way to determine whether the clipboard contains text. If GetClipboardData returns NULL, close the clipboard without doing anything else.

The handle you receive from GetClipboardData doesn't belong to your program—it belongs to the clipboard. The handle is valid only between the GetClipboardData and CloseClipboard calls. You can't free that handle or alter the data it references. If you need to have continued access to the data, you should make a copy of the memory block.

Here's one method for copying the data into your program. Just allocate a pointer to a block of the same size as the clipboard data block:

pText = (char *) malloc (GlobalSize (hGlobal)) ;
Recall that hGlobal was the global handle obtained from the GetClipboardData call. Now lock the handle to get a pointer to the clipboard block:

pGlobal = GlobalLock (hGlobal) ;
Now just copy the data:

strcpy (pText, pGlobal) ;
Or you can use some simple C code:

while (*pText++ = *pGlobal++) ;
Unlock the block before closing the clipboard:

GlobalUnlock (hGlobal) ;
CloseClipboard () ;
Now you have a pointer called pText that references the program's own copy of the text.

Opening and Closing the Clipboard
Only one program can have the clipboard open at any time. The purpose of the OpenClipboard call is to prevent the clipboard contents from changing while a program is using the clipboard. OpenClipboard returns a BOOL value indicating whether the clipboard was successfully opened. It will not be opened if another application failed to close it. If every program politely opens and then closes the clipboard as quickly as possible responding to a user command, you'll probably never run into the problem of being unable to open the clipboard.

In the world of impolite programs and preemptive multitasking, some problems could arise. Even if your program hasn't lost input focus between the time it put something into the clipboard and the time the user invokes a Paste option, don't assume that what you've put in there is still there. A background process could have accessed the clipboard during that time.

Watch out for a more subtle problem involving message boxes: If you can't allocate enough memory to copy something to the clipboard, then you might want to display a message box. If this message box isn't system modal, however, the user can switch to another application while the message box is displayed. You should either make the message box system modal or close the clipboard before you display the message box.

You can also run into problems if you leave the clipboard open while you display a dialog box. Edit fields in a dialog box use the clipboard for cutting and pasting text.

Sending and receiving data via clipboard can be complicated in pure Win32 API format. An easier solution would be to use MFC. MFC allows the program to easily add contents and retrieve contents to and from the clipboard.