Win32 Series - Beyond Simple Clipboard Use

方永贞
2023-12-01

http://www-user.tu-chemnitz.de/~heha/petzold/

 

 

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 theGetClipboardData 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) ;

or

hBitmap = GetClipboardData (CF_BITMAP) ;

or

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 positiveiFormat 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 callsGetClipboardData, 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 anEmptyClipboard 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:

case WM_RENDERALLFORMATS :
     OpenClipboard (hwnd) ;
     EmptyClipboard () ;
                              // fall through
case WM_RENDERFORMAT :
     [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:

  • WM_ASKCBFORMATNAME The clipboard viewer sends this message to  the clipboard owner to get a name for the format of the data. The lParam parameter is a pointer to a buffer, and  wParam is the maximum number of characters for this buffer. The clipboard owner must copy the name of the clipboard  format into this buffer.

  • WM_SIZECLIPBOARD This message tells the clipboard owner that the  size of the clipboard viewer's client area has changed. The wParam parameter is a handle to the clipboard viewer, and  lParam is a pointer to a RECT structure containing the new size. If the RECT structure contains all zeros,  the clipboard viewer is being destroyed or minimized. And, although the  Windows clipboard viewer allows only one instance of itself to be running, other  clipboard viewers can also send this message to the clipboard owner.  Handling these multiple clipboard viewers isn't impossible for the clipboard  owner (given thatwParam identifies the particular viewer), but it isn't easy, either.

  • WM_PAINTCLIPBOARD This message tells the clipboard owner to update  the clipboard viewer's client area. Again, wParam is a handle to the clipboard viewer's window. The  lParam parameter is a global handle to a  PAINTSTRUCT structure. The clipboard owner can lock the handle and obtain a handle to  the clipboard viewer's device context from the hdc field of this structure.

  • WM_HSCROLLCLIPBOARD and  WM_VSCROLLCLIPBOARDThese messages inform the clipboard owner that a user has scrolled the clipboard viewer's  scroll bars. ThewParam parameter is a handle to the clipboard viewer's window,  the low word oflParam is the scrolling request, and the high word of  lParam is the thumb position if the low word is SB_THUMBPOSITION.

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.

 

 类似资料:

相关阅读

相关文章

相关问答