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.

Comments (20)
  1. Adam Rosenfield says:

    Of course, GetClipboardSequenceNumber() doesn't work if the clipboard provider is using delayed rendering — the clipboard has no way of knowing if and when the underlying data the provider uses to render the actual clipboard data changes

    I suppose it could ask the provider to render the data on each call and do a memory comparison, but that would be rather ludicrous, since it completely defeats the purpose of delayed rendering (performance), and it turns a function that looks like it should just be a simple get of an integer stored in memory somewhere into a potentially complex and expensive operation.

    [It is naturally the delayed-render provider's responsibility to do a new SetClipboardData if the underlying contents change. This was necessary even under the old model. -Raymond]
  2. Joshua says:

    [It is naturally the delayed-render provider's responsibility to do a new SetClipboardData if the underlying contents change. This was necessary even under the old model. -Raymond]

    And I'll bet that at least one program has used it for region capture on a constantly changing area with the obvious consequences.

  3. James Johnston says:

    Too bad AddClipboardFormatListener only works on Windows Vista & up…  Not an option for the rest of us who still have to support XP…  (Maybe in a few years time we can refactor to use this new API after the rest of the world moves on?)

  4. Brian says:

    I wouldn't use GetClipboardSequenceNumber if I were you, it has a design flaw in that it will break if the user copies exactly 4,294,967,296 times before pasting!

  5. Anonymous Coward says:

    Raymond, it would have been polite to mention in the article that AddClipboardFormatListener isn't available in Windows XP, and hence cannot be used in most software.

    [The question was "Why is there still no way to X?" The use of "still" means that this was a "It's already 2010 and you idiots haven't fixed X"-type question. Answer: "This was fixed in 2007." If the question were "Why is there still no way to X with Windows XP?" then the answer is "Because time travel has not been perfected." -Raymond]
  6. AC says:

    Raymond:

    Before you throw a fit at the comments again (j/k): They're teasing/trolling, we do appreciate your articles.

  7. Klimax says:

    Don't see problem with new API – simply if(ver>) else…

    Or at least #if and then making two different binaries…

  8. J says:

    @TC:

    There isn't really a race condition there. The user issued a paste command, and we're pasting on data based on the contents of the clipboard at the time the paste command was issued.

  9. TC says:

    Is there a timing issue there? Couldn't you get interrupted just before:

      if (m_CachedClipboardData) Paste(m_CachedClipboardData);

    by someone who changes the clipboard data – *after* you've decided that it *hasn't* changed?

    [This problem isn't new to GetClipboardSequenceNumber. Solving it is left as an exercise. -Raymond]
  10. Worf says:

    @TC: Or, put another way, what happens? The user gets the old data. Which if it's changing so quickly that the interruption matters, the user probably won't notice.

    Realistically, it will never happen since users are slow – they copy, switch, they paste. The only way otherwise is if some stupid thing (*cough* popular_cpu_hogging_browser_plugin *cough*) is spamming the clipboard with copy operations.

  11. xpclient says:

    For XP, there is the older SetClipboardViewer API.

  12. Klimax says:

    @JamesJohnston: Where possible avoiding trouble by using new API? One less of collison between your programm and bad programm? (those bugs caused by externals can be quite problematic, so why not to avoid them on new systems and let those on old deal with that, if they don't want/can't upgrade)

  13. A says:

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

    Seems that some people took that *too* seriously.

    -Hey, we have this old app using the pre-Vista clipboard API

    -We shouldn't use that anymore

    clipbrd.exe is no longer available in Windows Vista

  14. @Klimax: what's the point?  If I have to do it the old way on Win XP, then why would I bother to then code a different way on Vista if the code for XP still works on newer Windows?

    @TC:  you could always just check the sequence number *again* after pasting to see if it changed, if it makes you feel better.  If it changed, then go back and read the clipboard again.  But what if it changed *again* after you checked the sequence number a second time?  Worf probably has the right idea; it doesn't matter.

    [Or solve the problem the same way you did under the old model: Open the clipboard before checking the sequence number. -Raymond]
  15. Kevin says:

    For the sake of completeness, the Ole clipboard API's also have an alternative to GetClipboardSequenceNumber, for the limited case where you want to know if your own application is still the clipboard source.  After your app calls OleSetClipboard to place a data object, OleIsCurrentClipboard on that same pointer will tell you if your object is still there.

  16. Does not work says:

    Unfortunatly, at least Windows 7 is not sending the WM_CLIPBOARDUPDATE message correctly.

    I have updated my small application to use both the old or the new method to monitor the clipboard (depending on a switch), only to find out, that WM_CLIPBOARDUPDATE is not reliable.

    My application has a debug window that displays the current Clipboard owner, the current Clipboard sequence number and all available Clipboard formats after each WM_CLIPBOARDUPDATE or WM_DRAWCLIPBOARD. When I start three instances of this program, and change the clipbaord by doing Ctrl+C in various application (especially in the Explorer), with the new method, all three programs see different states of the clipboard. Most notably, the list of available formats is different for all three instances, for example: 0, 2 and 7 formats. Even the sequence number can be different.

    Conclusion: It need to be fixed.

  17. Does not work #2 says:

    Further experimenting shows that one program using the new method is breaking all other programs in the same session that are also monitoring the clipbard, even if this other programs are using the old method. As soon as I terminate the new-method program, the others (still running) return to correct operation (once again, they are seeing the same state of the clipboard in terms of available formats and the sequence number).

    Site note: The HWNDs returned by GetClipboardOwner are different in each process that processes WM_CLIPBOARDUPDATE or WM_DRAWCLIPBOARD. Strange.

  18. yuhong2 says:

    [The question was "Why is there still no way to X?" The use of "still" means that this was a "It's already 2010 and you idiots haven't fixed X"-type question. Answer: "This was fixed in 2007." If the question were "Why is there still no way to X with Windows XP?" then the answer is "Because time travel has not been perfected." -Raymond]

    Yea, I remember reading the "I swear the xxx bug is fixed; it was fixed 4.5 years ago!" blog articles from michkap about exactly this.

  19. Marc says:

    If the question were "Why is there still no way to X with Windows XP?" then the answer is "Because time travel has not been perfected."

    No, the answer is "Because we don't backport things."  No time machine is needed to release an update for Windows XP.  New API functions were added via service backs for NT4 without creating any paradoxes.

  20. yuhong2 says:

    Marc: Yes, and they still even recently sometimes add functions in hotfixes and even security updates, but they did not require major changes to the OS's architecture or anything like that.

Comments are closed.