2008 November 19th Wednesday (十一月 十九日 水曜日)

   The below prolog program had puzzled me for days.  This morning I got a inspiration.  I made a mistake.

%% exercise

assign(X, Y) -->
    left(X), [:=], right(Y), [;].

left(X, Start, End) :-
    [ X | End ] = Start,
    atomic(X).

right(Y, Start, End) :-
    [ Y | End ] = Start,
    number(Y).

  The variable X, Y, is not used mainly to pass arguments into the "assign".  They are used to get the analysed the lexical item.

| ?- assign(X, Y, [x, :=, 1, ;], R).

R = []
X = x
Y = 1

yes

  OK.  You used it as follow.

| ?- assign(x, 2, L, []).

L = [x,:=,2,;]

yes

OR

| ?- assign(x, 2, L, R).

L = [x,:=,2,;|R]

yes

  It means that you can combind an assign statement as its syntax.  That is followed as a powerful feature in prolog -- unification.
So, the source before many days is wrong, I changed it.

Differences Between Modal and Modeless Dialog Boxes

  Working with modeless dialog boxes is similar to working with modal dialog boxes, but there are several important differences.

  First, modeless dialog boxes usually include a caption bar and a system menu box. These are actually the default options when you create
a dialog box in Developer Studio. The STYLE statement in the dialog box template for a modeless dialog box will look something like this:


     STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

  The caption bar and system menu allow the user to move the modeless dialog box to another area of the display using either the mouse or
the keyboard. You don't normally provide a caption bar and system menu with a modal dialog box, because the user can't do anything in the
underlying window anyway.

  The second big difference: Notice that the WS_VISIBLE style is included in our sample STYLE statement. In Developer Studio, select this
option from the More Styles tab of the Dialog Properties dialog. If you omit WS_VISIBLE, you must call ShowWindow after the CreateDialog call:

     hDlgModeless = CreateDialog (  . . .  ) ;
     ShowWindow (hDlgModeless, SW_SHOW) ;

  If you neither include WS_VISIBLE nor call ShowWindow, the modeless dialog box will not be displayed. Programmers who have mastered modal
dialog boxes often overlook this peculiarity and thus experience difficulties when first trying to create a modeless dialog box.

  The third difference: Unlike messages to modal dialog boxes and message boxes, messages to modeless dialog boxes come through your program's
message queue. The message queue must be altered to pass these messages to the dialog box window procedure. Here's how you do it: When you use
CreateDialog to create a modeless dialog box, you should save the dialog box handle returned from the call in a global variable (for instance,
hDlgModeless). Change your message loop to look like

while (GetMessage (&msg, NULL, 0, 0))
{
     if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
     {
          TranslateMessage (&msg) ;
          DispatchMessage  (&msg) ;
     }
}

  If the message is intended for the modeless dialog box, then IsDialogMessage sends it to the dialog box window procedure and returns TRUE
(nonzero); otherwise, it returns FALSE (0). The TranslateMessage and DispatchMessage functions should be called only if hDlgModeless is 0 or
if the message is not for the dialog box. If you use keyboard accelerators for your program's window, the message loop looks like this:

while (GetMessage (&msg, NULL, 0, 0))
{
     if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
     {
          if (!TranslateAccelerator (hwnd, hAccel, &msg))
          {
               TranslateMessage (&msg) ;
               DispatchMessage  (&msg) ;
          }
     }
}

  Because global variables are initialized to 0, hDlgModeless will be 0 until the dialog box is created, thus ensuring that IsDialogMessage
is not called with an invalid window handle. You must take the same precaution when you destroy the modeless dialog box, as explained below.

  The hDlgModeless variable can also be used by other parts of the program as a test of the existence of the modeless dialog box. For example,
other windows in the program can send messages to the dialog box while hDlgModeless is not equal to 0.

  The final big difference: Use DestroyWindow rather than EndDialog to end a modeless dialog box. When you call DestroyWindow, set the hDlgModeless
global variable to NULL.

  The user customarily terminates a modeless dialog box by choosing Close from the system menu. Although the Close option is enabled, the dialog
box window procedure within Windows does not process the WM_CLOSE message. You must do this yourself in the dialog box procedure:

case WM_CLOSE :
     DestroyWindow (hDlg) ;
     hDlgModeless = NULL ;
     break ;

  Note the difference between these two window handles: the hDlg parameter to DestroyWindow is the parameter passed to the dialog box procedure;
hDlgModeless is the global variable returned from CreateDialog that you test within the message loop.

  You can also allow a user to close a modeless dialog box using push buttons. Use the same logic as for the WM_CLOSE message. Any information that
the dialog box must "return" to the window that created it can be stored in global variables. If you'd prefer not using global variables, you can
create the modeless dialog box by using CreateDialogParam and pass to it a structure pointer, as described earlier.

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:

     int * p ;
     GLOBALHANDLE hGlobal ;
Then allocate the memory. For example:

     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:

pGlobal = GlobalLock (hGlobal) ;

Copy the character string into the global memory block:

for (i = 0 ; i < wLength ; i++)
     *pGlobal++ = *pString++ ;

You don't need to add the terminating NULL because the GHND flag for GlobalAlloc zeroes out the entire memory block during allocation. Unlock the block:

GlobalUnlock (hGlobal) ;

Now you have a global memory handle that references a memory block containing the NULL-terminated text. To get this into the clipboard, open the clipboard and empty it:

OpenClipboard (hwnd) ;
EmptyClipboard () ;

Give the clipboard the memory handle by using the CF_TEXT identifier, and close the clipboard:

SetClipboardData (CF_TEXT, hGlobal) ;
CloseClipboard () ;
You're done.

Here are some rules concerning this process:


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.

To transfer the text out, first open the clipboard:

OpenClipboard (hwnd) ;

Obtain the handle to the global memory block referencing the text:

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.

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 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:

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

  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 that wParam 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. The wParam
  parameter is a handle to the clipboard viewer's window, the low word of lParam 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.

Clipboard Viewer Functions and Messages

A program can become part of the clipboard viewer chain by calling the SetClipboardViewer function. If the primary purpose of the program is to serve as a clipboard viewer,
the program can call this function during processing of the WM_CREATE message. The function returns the window handle of the previous current clipboard viewer. The program
should save that handle in a static variable:

static HWND hwndNextViewer ;
[other program lines]
case WM_CREATE :
     [other program lines]
     hwndNextViewer = SetClipboardViewer (hwnd) ;
    
If your program is the first program to become a clipboard viewer during the Windows session, then hwndNextViewer will be NULL.

Windows sends a WM_DRAWCLIPBOARD message to the current clipboard viewer (the most recent window to register itself as a clipboard viewer) whenever the contents of the clipboard
change. Each program in the clipboard viewer chain should use SendMessage to pass this message to the next clipboard viewer. The last program in the clipboard viewer chain (the
first window to register itself as a clipboard viewer) will have stored a NULL hwndNextViewer value. If hwndNextViewer is NULL, the program simply returns without sending the message
to another program. (Don't confuse the WM_DRAWCLIPBOARD and WM_PAINTCLIPBOARD messages. The WM_PAINTCLIPBOARD message is sent by a clipboard viewer to programs that use the CF_OWNERDISPLAY
clipboard format. The WM_ DRAWCLIPBOARD message is sent by Windows to the current clipboard viewer.)

The easiest way to process the WM_DRAWCLIPBOARD message is to send the message to the next clipboard viewer (unless hwndNextViewer is NULL) and invalidate the client area of your window:

case WM_DRAWCLIPBOARD :
     if (hwndNextViewer)
          SendMessage (hwndNextViewer, message, wParam, lParam) ;

     InvalidateRect (hwnd, NULL, TRUE) ;
     return 0 ;
    
During processing of the WM_PAINT message, you can read the contents of the clipboard by using the normal OpenClipboard, GetClipboardData, and CloseClipboard calls.

When a program wants to remove itself from the clipboard viewer chain, it must call ChangeClipboardChain. This function requires the window handle of the program leaving the viewer chain
and the window handle of the next clipboard viewer:

ChangeClipboardChain (hwnd, hwndNextViewer) ;

When a program calls ChangeClipboardChain, Windows sends a WM_CHANGECBCHAIN message to the current clipboard viewer. The wParam parameter is the handle of the window removing itself from
the chain (that is, the first parameter to ChangeClipboardChain), and lParam is the window handle of the next clipboard viewer after the one removing itself from the chain (the second argument
to ChangeClipboardChain).

When your program receives a WM_CHANGECBCHAIN message, you must therefore check to see if wParam is equal to the value of hwndNextViewer that you've saved. If it is, your program must set
hwndNextViewer to lParam. This action ensures that any future WM_DRAWCLIPBOARD messages you get won't be sent to the window removing itself from the clipboard viewer chain. If wParam isn't equal
to hwndNextViewer and hwndNextViewer isn't NULL, send the message to the next clipboard viewer:

case WM_CHANGECBCHAIN :
     if ((HWND) wParam == hwndNextViewer)
          hwndNextViewer = (HWND) lParam ;

     else if (hwndNextViewer)
          SendMessage (hwndNextViewer, message, wParam, lParam) ;
     return 0 ;

You shouldn't really need to include the else if statement, which checks hwndNextViewer for a non-NULL value. A NULL hwndNextViewer value would indicate that the program executing this code
is the last viewer on the chain, in which case the message should never have gotten this far.

If your program is still in the clipboard viewer chain when it is about to terminate, you must remove it from the chain. You can do this during processing of the WM_DESTROY message by calling
ChangeClipboardChain:

case WM_DESTROY :
     ChangeClipboardChain (hwnd, hwndNextViewer) ;
     PostQuitMessage (0) ;
     return 0 ;

Windows also has a function that allows a program to obtain the window handle of the first clipboard viewer:

hwndViewer = GetClipboardViewer () ;

This function isn't normally needed. The return value can be NULL if there is no current clipboard viewer.

Here's an example to illustrate how the clipboard viewer chain works. When Windows first starts up, the current clipboard viewer is NULL:

Current clipboard viewer: NULL

A program with a window handle of hwnd1 calls SetClipboardViewer. The function returns NULL, which becomes the hwndNextViewer value in this program:

Current clipboard viewer: hwnd1
hwnd1's next viewer: NULL

A second program, with a window handle of hwnd2, now calls SetClipboardViewer and gets back hwnd1:

Current clipboard viewer: hwnd2
hwnd2's next viewer: hwnd1
hwnd1's next viewer: NULL

A third program (hwnd3) and then a fourth (hwnd4) also call SetClipboardViewer and get back hwnd2 and hwnd3:

Current clipboard viewer: hwnd4
hwnd4's next viewer: hwnd3
hwnd3's next viewer: hwnd2
hwnd2's next viewer: hwnd1
hwnd1's next viewer: NULL

When the contents of the clipboard change, Windows sends a WM_DRAWCLIPBOARD message to hwnd4, hwnd4 sends the message to hwnd3, hwnd3 sends it to hwnd2,
hwnd2 sends it to hwnd1, and hwnd1 returns.

Now hwnd2 decides to remove itself from the chain by calling

ChangeClipboardChain (hwnd2, hwnd1) ;

Windows sends hwnd4 a WM_CHANGECBCHAIN message with wParam equal to hwnd2 and lParam equal to hwnd1. Because hwnd4's next viewer is hwnd3, hwnd4 sends
this message to hwnd3. Now hwnd3 notes that wParam is equal to its next viewer (hwnd2), so it sets its next viewer equal to lParam (hwnd1) and returns.
The mission is accomplished. The clipboard viewer chain now looks like this:

Current clipboard viewer: hwnd4
hwnd4's next viewer: hwnd3
hwnd3's next viewer: hwnd1
hwnd1's next viewer: NULL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值