September 19th, 2011

The clipboard viewer linked list is no longer the responsibility of applications to maintain, unless they want to

Commenter Nice Clipboard Manager (with drop->clipboard) wonders why Windows still uses a linked list to inform programs about clipboard modifications. If any clipboard viewer fails to maintain the chain, then some windows won’t get informed of the change, and if a clipboard viewer creates a loop in the chain, an infinite loop results.

Well, sure, that’s what happens if you use the old clipboard viewer chain. So don’t use it. The old clipboard viewer chain remains for backward compatibility, but it’s hardly the best way to monitor the clipboard. (This is another example of people asking for a feature that already exists.)

Instead of using the clipboard viewer chain, just add yourself as a clipboard format listener via AddClipboardFormatListener. Once you’ve done that, the system will post you a WM_CLIPBOARDUPDATE message when the contents of the clipboard have changed, and you can respond accordingly. When you’re done, call RemoveClipboardFormatListener.

By using the clipboard format listener model, you let Windows worry about keeping track of all the people who are monitoring the clipboard, as Clipboarder Gadget suggested. (Mind you, Windows doesn’t go so far as making each clipboard viewer think that it’s the only viewer in the chain, because there may be applications which break the chain on purpose. Changing the chain behavior will break compatibility with those applications.)

Let’s turn our scratch program into a clipboard format listener.

void
SniffClipboardContents(HWND hwnd)
{
 SetWindowText(hwnd, IsClipboardFormatAvailable(CF_TEXT)
             ? TEXT("Has text") : TEXT("No text"));
}
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
 SniffClipboardContents(hwnd); // set initial title
 return AddClipboardFormatListener(hwnd);
}
void
OnDestroy(HWND hwnd)
{
 RemoveClipboardFormatListener(hwnd);
 PostQuitMessage(0);
}
... add to window procedure ...
 case WM_CLIPBOARDUPDATE: SniffClipboardContents(hwnd); break;

And that’s it. Much, much simpler than writing a clipboard viewer, and much more robust since you aren’t dependent on other applications not screwing up.

There’s another alternative to registering a clipboard listener and that’s using the clipboard sequence number. The window manager increments the clipboard sequence number each time the contents of the clipboard change. You can compare the sequence number from two points in time to determine whether the contents of the clipboard have changed while you weren’t looking.

Now you have a choice. Do you use the notification method (clipboard format listener) or the polling method (clipboard sequence number)? The notification method is recommended if you want to do something as soon as the clipboard contents change. On the other hand, the polling method is more suitable if you perform calculations based on the clipboard contents and cache the results, and then later you want to verify that your cached results are still valid.

For example, suppose you have a program with a Paste function, and pasting from the clipboard involves creating a complex data structure based on the clipboard contents. The user clicks Paste, you create your complex data structure, and insert it into the document. Your research discovers that a common operation is pasting the same contents several times. To optimize this, you want to cache the complex data structure so that if the user clicks Paste five times in a row, you only have to build the complex data structure the first time and you can just re-use it the other four times.

void DocumentWindow::OnPaste()
{
 if (m_CachedClipboardData == NULL ||
     GetClipboardSequenceNumber() != m_SequenceNumberInCache) {
  delete m_CachedClipboardData;
  m_SequenceNumberInCache = GetClipboardSequenceNumber();
  m_CachedClipboardData = CreateComplexDataFromClipboard();
 }
 if (m_CachedClipboardData) Paste(m_CachedClipboardData);
}

When the OnPaste method is called, we see if we have clipboard data cached from last time. If not, then clearly we need to create our complex data structure from the clipboard. If we do have clipboard data in our cache, we see if the clipboard sequence number has changed. If so, then the cached data is no longer valid and we have to throw it away and create it from scratch. But if we have cached data and the sequence number hasn’t changed, then the cache is still valid and we can avoid calling CreateComplexDataFromClipboard.

The old clipboard viewer is like DDE: please feel free to stop using it.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.