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, iMessage, 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 wishes 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_CHANGECLIPBOARD message to the current clipboard viewer. The wParam parameter is the handle of the window removing itself from the chain (the first parameter to ChangeClipboardChain), and the low word of lParam is the window handle of the next clipboard viewer after the one removing itself from the chain (the second parameter to ChangeClipboardChain).
When your program receives a WM_CHANGECLIPBOARD 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 the low word of 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 hwndNext- Viewer, and hwndNextViewer isn't NULL, send the message to the next clipboard viewer:
case WM_CHANGECBCHAIN :
if (wParam == hwndNextViewer)
hwndNextViewer = LOWORD (lParam) ;
else if (hwndNextViewer)
SendMessage (hwndNextViewer, iMessage, wParam, lParam) ;
return 0 ;
You shouldn't 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 your program 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 the low word of 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 the low word of 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 |