Over the course of the last couple of hours I have been tracking down a fairly specific bug with that occurs because another application has the clipboard open. Essentially
I ran into this error today. I decided to handle it by telling the user about the potentially misbehaving application. To do so, you can do something like this:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetOpenClipboardWindow();
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int GetWindowText(int hwnd, StringBuilder text, int count);
private void btnCopy_Click(object sender, EventArgs e)
{
try
{
Clipboard.Clear();
Clipboard.SetText(textBox1.Text);
}
catch (Exception ex)
{
string msg = ex.Message;
msg += Environment.NewLine;
msg += Environment.NewLine;
msg += "The problem:";
msg += Environment.NewLine;
msg += getOpenClipboardWindowText();
MessageBox.Show(msg);
}
}
private string getOpenClipboardWindowText()
{
IntPtr hwnd = GetOpenClipboardWindow();
StringBuilder sb = new StringBuilder(501);
GetWindowText(hwnd.ToInt32(), sb, 500);
return sb.ToString();
// example:
// skype_plugin_core_proxy_window: 02490E80
}
For me, the problem window title was "skype_plugin_core_proxy_window". I searched for info on that, and was surprised that it yielded only one hit, and that was in Russian. So I'm adding this answer, both to give another hit for that string, and to provide further help to bring potentially-misbehaving apps to light.
Another workaround would be to use Clipboard.SetDataObject
instead of Clipboard.SetText
.
According to this MSDN article this method has two parameters - retryTimes and retryDelay - that you can use like this:
System.Windows.Forms.Clipboard.SetDataObject(
"some text", // Text to store in clipboard
false, // Do not keep after our application exits
5, // Retry 5 times
200); // 200 ms delay between retries
By making use of Jeff Roe's code (Jeff's Code)
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetOpenClipboardWindow();
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int GetWindowText(int hwnd, StringBuilder text, int count);
private void btnCopy_Click(object sender, EventArgs e)
{
try
{
Clipboard.Clear();
Clipboard.SetText(textBox1.Text);
}
catch (Exception ex)
{
string msg = ex.Message;
msg += Environment.NewLine;
msg += Environment.NewLine;
msg += "The problem:";
msg += Environment.NewLine;
msg += getOpenClipboardWindowText();
MessageBox.Show(msg);
}
}
private string getOpenClipboardWindowText()
{
IntPtr hwnd = GetOpenClipboardWindow();
StringBuilder sb = new StringBuilder(501);
GetWindowText(hwnd.ToInt32(), sb, 500);
return sb.ToString();
// example:
// skype_plugin_core_proxy_window: 02490E80
}
you are able to handle the error in a pretty handy way.
I have managed to reduce the frequency of the error by making use of System.Windows.Forms.Clipboard
Instead of System.Windows.Clipboard
.
I stress that this doesn't fix the problem but it has reduced the occurrence for my application.
This is bit crappy... But it solved my problem.
Retry the clear() after a delay.
More information is in the blog post How to handle a blocked clipboard - Clipboard.Clear() error.
As the clipboard is shared by all UI applications, you will run into this from time to time. Obviously, you don't want your application to crash if it failed to write to the clipboard, so gracefully handling ExternalException is reasonable. I would suggest presenting an error to the user if the SetObjectData call to write to the clipboard fails.
A suggestion would be to use (via P/Invoke) user32!GetOpenClipboardWindow to see if another application has the clipboard open. It will return the HWND of the window which has the clipboard open, or IntPtr.Zero
if no application had it open. You could spin on the value until its IntPtr.Zero
for a specified amount of time.
Doing a Clipboard.Clear()
before Clipboard.SetDataObject(pasteString, true)
seems to do the trick.
The earlier suggestion of setting retryTimes
and retryDelay
didn't work for me and in any case the defaults are retryTimes = 10
and retryDelay = 100ms