Get a site

VC++ 6.0 ebook chapter index
Free counters!

Beyond Simple Clipboard Use

We've seen that transferring text from the clipboard requires four calls after the data has been prepared:

OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (iFormat, hGlobal) ;
CloseClipboard () ;

Getting access to this data requires three calls:

OpenClipboard (hwnd) ;
hGlobal = GetClipboardData (iFormat) ;
[other program lines]
CloseClipboard () ;

You can make a copy of the clipboard data or use it in some other manner between the GetClipboardData and CloseClipboard calls. That approach may be all you'll need for most purposes, but you can also use the clipboard in more sophisticated ways.

Using Multiple Data Items

When you open the clipboard to put data into it, you must call EmptyClipboard to signal Windows to free or delete the contents of the clipboard. You can't add something to the existing contents of the clipboard. So, in this sense, the clipboard holds only one item at a time.

However, between the EmptyClipboard and the CloseClipboard calls, you can call SetClipboardData several times, each time using a different clipboard format. For instance, if you want to store a short string of text in the clipboard, you can write that text to a metafile and to a bitmap. In this way, you make that character string available not only to programs that can read text from the clipboard but also to programs that read bitmaps and metafiles from the clipboard. Of course, these programs won't be able to easily recognize that the metafile or bitmap actually contains a character string.

If you want to write several handles to the clipboard, you call SetClipboardData for each of them:

OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (CF_TEXT, hGlobalText) ;
SetClipboardData (CF_BITMAP, hBitmap) ;
SetClipboardData (CF_METAFILEPICT, hGlobalMFP) ;
CloseClipboard () ;

While these three formats of data are in the clipboard, an IsClipboardFormatAvailable call with the CF_TEXT, CF_BITMAP, or CF_METAFILEPICT argument will return TRUE. A program can get access to these handles by calling

hGlobalText = GetClipboardData (CF_TEXT) ;


hBitmap = GetClipboardData (CF_BITMAP) ;


hGlobalMFP = GetClipboardData (CF_METAFILEPICT) ;

The next time a program calls EmptyClipboard, Windows will free or delete all three of the handles retained by the clipboard.

Don't use this technique to add different text formats, different bitmap formats, or different metafile formats to the clipboard. Use only one text format, one bitmap format, and one metafile format. As I mentioned, Windows will convert among CF_TEXT, CF_ OEMTEXT, and CF_UNICODETEXT. It will also convert between CF_BITMAP and CF_DIB, and between CF_METAFILEPICT and CF_ENHMETAFILE.

A program can determine all the formats stored by the clipboard by first opening the clipboard and then calling EnumClipboardFormats. Start off by setting a variable iFormat to 0:

iFormat = 0 ; OpenClipboard (hwnd) ;

Now make successive EnumClipboardFormats calls starting with the 0 value. The function will return a positive iFormat value for each format currently in the clipboard. When the function returns 0, you're done:

while (iFormat = EnumClipboardFormats (iFormat))
     [logic for each iFormat value]
CloseClipboard () ;

You can obtain the number of different formats currently in the clipboard by calling

iCount = CountClipboardFormats () ;

Delayed Rendering

When you put data into the clipboard, you generally make a copy of the data and give the clipboard a handle to a global memory block that contains the copy. For very large data items, this approach can waste memory. If the user never pastes that data into another program, it will continue to occupy memory space until it is replaced by something else.

You can avoid this problem by using a technique called "delayed rendering," in which your program doesn't actually supply the data until another program needs it. Rather than give Windows a handle to the data, you simply use a NULL in the SetClipboardData call:

OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (iFormat, NULL) ;
CloseClipboard () ;

You can have multiple SetClipboardData calls using different values of iFormat. You can use NULL parameters with some of them and real handles with others.

That's simple enough, but now the process gets a little more complex. When another program calls GetClipboardData, Windows will check to see if the handle for that format is NULL. If it is, Windows will send a message to the "clipboard owner" (your program) asking for a real handle to the data. Your program must then supply this handle.

More specifically, the "clipboard owner" is the last window that put data into the clipboard. When a program calls OpenClipboard, Windows stores the window handle required by this function. This handle identifies the window that has the clipboard open. On receipt of an EmptyClipboard call, Windows establishes this window as the new clipboard owner.

A program that uses delayed rendering must process three messages in its window procedure: WM_RENDERFORMAT, WM_RENDERALLFORMATS, and WM_DESTROYCLIPBOARD. Windows sends your window procedure a WM_RENDERFORMAT message when another program calls GetClipboardData. The value of wParam is the format requested. When you process the WM_RENDERFORMAT message, don't open and empty the clipboard. Simply create a global memory block for the format given by wParam, transfer the data to it, and call SetClipboardData with the correct format and the global handle. Obviously, you'll need to retain information in your program to construct this data properly when processing WM_RENDERFORMAT. When another program calls EmptyClipboard, Windows sends your program a WM_DESTROYCLIPBOARD message. This tells you that the information to construct the clipboard data is no longer needed. You are no longer the clipboard owner.

If your program terminates while it is still the clipboard owner, and the clipboard still contains NULL data handles that your program set with SetClipboardData, you'll receive a WM_RENDERALLFORMATS message. You should open the clipboard, empty it, put the data in global memory blocks, and call SetClipboardData for each format. Then close the clipboard. The WM_RENDERALLFORMATS message is one of the last messages your window procedure receives. It is followed by a WM_DESTROYCLIPBOARD message—because you've rendered all the data—and then the normal WM_DESTROY.

If your program can transfer only one format of data to the clipboard (text, for instance), you can combine the WM_RENDERALLFORMATS and WM_RENDERFORMAT processing. The code will look something like this:

     OpenClipboard (hwnd) ;
     EmptyClipboard () ;
                              // fall through
     [put text into global memory block]
     SetClipboardData (CF_TEXT, hGlobal) ;

     if (message == WM_RENDERALLFORMATS)
          CloseClipboard () ;
     return 0 ;

If your program uses several clipboard formats, you'll want to process the WM_ RENDERFORMAT message only for the format requested by wParam. You don't need to process the WM_DESTROYCLIPBOARD message unless it is burdensome for your program to retain the information necessary to construct the data.

Private Data Formats

So far we've dealt with only the standard clipboard formats defined by Windows. However, you may want to use the clipboard to store a "private data format." Many word processors use this technique to store text that contains font and formatting information.

At first, this concept may seem nonsensical. If the purpose of the clipboard is to transfer data between applications, why should the clipboard contain data that only one application understands? The answer is simple: The clipboard also exists to allow the transfer of data to and from itself (or perhaps between different instances of the same program), and these instances obviously understand the same private formats.

There are several ways to use private data formats. The easiest involves data that is ostensibly in one of the standard clipboard formats (that is, text, bitmap, or metafile) but that has meaning only to your program. In this case, you use one of the following wFormat values in your SetClipboardData and GetClipboardData calls: CF_DSPTEXT, CF_DSPBITMAP, CF_DSPMETAFILEPICT, or CF_DSPENHMETAFILE. (The letters DSP stand for "display.") These formats allow the Windows clipboard viewer to display the data as text, a bitmap, or a metafile. However, another program that calls GetClipboardData using the normal CF_TEXT, CF_BITMAP, CF_DIB, CF_METAFILEPICT, or CF_ENHMETAFILE format won't obtain this data.

If you use one of these formats to put data in the clipboard, you must also use the same format to get the data out. But how do you know if the data is from another instance of your program or from another program using one of these formats? Here's one way: You can first obtain the clipboard owner by calling

hwndClipOwner = GetClipboardOwner () ;

You can then get the name of the window class of this window handle:

TCHAR szClassName [32] ;
[other program lines]
GetClassName (hwndClipOwner, szClassName, 32) ;

If the class name is the same as your program's, then the data was put in the clipboard by another instance of your program.

The second way to use private formats involves the CF_OWNERDISPLAY flag. The global memory handle to SetClipboardData is NULL:

SetClipboardData (CF_OWNERDISPLAY, NULL) ;

This is the method that some word processors use to show formatted text in the client area of the clipboard viewer included with Windows. Obviously, the clipboard viewer doesn't know how to display this formatted text. When a word processor specifies the CF_OWNERDISPLAY format, it is also taking responsibility for painting the clipboard viewer's client area.

Because the global memory handle is NULL, a program that calls SetClipboardData with the CF_OWNERDISPLAY format (the clipboard owner) must process the delayed rendering messages sent to the clipboard owner by Windows, as well as five additional messages. The following five messages are sent by the clipboard viewer to the clipboard owner:

Handling these messages may look like more trouble than it's worth. However, the process does provide a benefit to the user: when copying text from a word processsor to the clipboard, the user will find it comforting to see the text still formatted in the clipboard viewer's client area.

The third way to use private clipboard data formats is to register your own clipboard format name. You supply a name for this format to Windows, and Windows gives your program a number to use as the format parameter in SetClipboardData and GetClipboardData. Programs that use this method generally also copy data to the clipboard in one of the standard formats. This approach allows the clipboard viewer to display data in its client area (without the hassles involved with CF_OWNERDISPLAY) and permits other programs to copy data from the clipboard.

As an example, let's assume we've written a vector-drawing program that copies data to the clipboard in a bitmap format, a metafile format, and its own registered clipboard format. The clipboard viewer will display the metafile or bitmap. Other programs that can read bitmaps or metafiles from the clipboard will obtain those formats. However, when the vector-drawing program itself needs to read data from the clipboard, it will copy the data in its own registered format because that format probably contains more information than the bitmap or metafile.

A program registers a new clipboard format by calling

iFormat = RegisterClipboardFormat (szFormatName) ;

The iFormat value is between 0xC000 and 0xFFFF. A clipboard viewer (or a program that obtains all the current clipboard formats by calling EnumClipboardFormats) can obtain the ASCII name of this format by calling

GetClipboardFormatName (iFormat, psBuffer, iMaxCount) ;

Windows copies up to iMaxCount characters into psBuffer.

Programmers who use this method for copying data to the clipboard might want to publicize the format name and the actual format of the data. If the program becomes popular, other programs can then copy data in this format from the clipboard.