Last time, we saw that the system will wait up to 30 seconds for a clipboard owner to produce delay-rendered data. But what if you want to extend this timeout?
The timeout itself is embedded in the clipboard manager and is not configurable. Thirty seconds is the longest the clipboard manager will wait for the clipboard data to be rendered.
But that doesn’t mean that’s the longest you will wait.
Recall that the WM_
message handler will call SetÂClipboardÂData
when it’s done, hoping that the clipboard is still open by the original code that requested the data.
So you just need to keep the clipboard open long enough for the clipboard owner to finish rendering the data format and calling SetÂClipboardÂData
.
But how do you know that the clipboard owner has finished rendering the data format?
One idea is to poll by calling GetÂClipboardÂSequenceÂNumber
until the sequence number changes, indicating that somebody (presumably the clipboard owner) called SetÂClipboardÂData
. This is unsatisfying because (1) it’s polling, and (2) if an error occurs which prevents the clipboard owner from producing the data, you’re just going to be polling forever, unless you create your own timeout.
I have another idea: Call GetÂClipboardÂOwner
to obtain the handle of the clipboard owner. This is the window that is busy trying to generate the clipboard data. Send this window a harmless message like WM_
. The purpose is not to get a meaningful reply to the message; the purpose is to know “Hey, this window is responding to messages again!”
The theory here is that if the window is not responding to messages, then that means that it is still busy producing the data to be put onto the clipboard. Once it starts responding, then the delay-rendering has completed.
This theory holds up if the work is being done by the thread that owns the clipboard owner window. However, if the clipboard owner window is delegating the work to another thread, then this mechanism will report that the clipboard owner processed the WM_NULL
message, because threads can process inbound sent messages while waiting for an outbound sent message to return.
But hey, it’s better than nothing.
if (OpenClipboard(hwnd)) { if (IsClipboardFormatAvailable(format)) { auto data = GetClipboardData(format); if (!data) { auto owner = GetClipboardOwner(); SendMessage(owner, WM_NULL, 0, 0); data = GetClipboardData(format); } if (data) { // yay, we have clipboard data } } CloseClipboard(); }
After opening the clipboard, we check whether our desired format is available. If so, we ask for it. If that request failed, then we send the clipboard owner a WM_NULL
message to wait for it to finish generating the format, and then try a second time. If that second try also fails, then we just give up.
Of course, instead of sending the WM_
message with SendÂMessage
, you can use SendÂMessageÂTimeout
to apply your own custom timeout. If the SendÂMessageÂTimeout
of WM_
fails, then you can probably skip calling GetÂClipboardÂData
a second time. The thread is probably still busy processing the original request. There’s an off chance that the thread completed the original request but became hung on something else in the meantime. I don’t consider this off chance worth the effort to work around. The whole thing is just a workaround anyway.
It turns out that if you go back to the customer’s original problem, none of these workarounds are needed anyway. We’ll look at a better solution next time. </P.
I have a WIFIDirect question. But I don't know where to ask. Can I ask here?
I am building a WIFIDirect APP with WinRT in C++. I can start an Auto GO(not legacy softAP mode) without problem and I can see the SSID by doing WIFI Scan with my phone. I want send this Auto GO's SSID to another device through BT or BLE(not just by eyes), but I can't find an API to query...
I get that the clipboard API is old, but damn these are some creaky, unsatisfying workarounds.
I wonder if it ever occurred to Microsoft to update the clipboard API so that it had some actual, reliable, event-driven mechanism for dealing with the deferred-rendering scenario.
Realistically you have two options:
Generally speaking (1) is the preferred outcome 😅 in my experience. The WinRT wrappers aren't bad and deal with most of the 'event driven' for you (it provides a Clipboard.ContentChanged event, not linking the docs as that requires Raymond's direct approval).
Well having a format take 30+ seconds to render would make your end user wait 30+ seconds after pressing paste. Not sure how an event driven API would fix that. Show a progress bar? Most people expect a paste to be instantaneous. I hope Raymond will take the 30 seconds out of the equation tomorrow
Asynchronous event-driven APIs are a thing. Indeed, Microsoft has been pushing hard with the whole async thing, especially on WinRT/UWP/etc.
Some will complain about it being a whole new API that doesn't get used as much as the old one. But when an API is broken enough, that kind of complaint is weak. There are numbers of examples, even in Windows, where a new API has supplanted and older API that was deprecated, and where the...
Guessing backwards compatibility concerns make doing too much with the clipboard APIs difficult.