How to handle a blocked clipboard and other oddities

C#.NetClipboard

C# Problem Overview


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 as the clipboard is a shared resource (as per "Why does my shared clipboard not work?") and you attempt to execute

Clipboard.SetText(string)

or

Clipboard.Clear().

The following exception is thrown:

System.Runtime.InteropServices.ExternalException: Requested Clipboard operation did not succeed.
at System.Windows.Forms.Clipboard.ThrowIfFailed(Int32 hr)
at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay)
at System.Windows.Forms.Clipboard.SetText(String text, TextDataFormat format)
at System.Windows.Forms.Clipboard.SetText(String text)

My initial solution was to retry after a short pause, until I realised that Clipboard.SetDataObject has fields for the number of times and the length of the delay. .NET's default behaviour is to try 10 times with a 100 msec delay.

There is one final thing that has been noted by the end user. That is, despite the exception being thrown, the copy to clipboard operation still works. I haven't been able to find any further information about why this may be.

My current solution to the issue is just to silently ignore the exception... is this really the best way?

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

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.

Solution 3 - C#

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.

Solution 4 - C#

Just call this first:

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr CloseClipboard();

I noticed that if you're in the middle of a paste operation (WM_PASTE message), including during the TextChanged event, the clipboard remains locked by the window (the TextBox) receiving the event. So if you just call that "CloseClipboard" method inside the event handler, then you can call the managed Clipboard.Clear and Clipboard.SetText methods without any issues or delays.

Solution 5 - C#

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

Solution 6 - C#

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.

Solution 7 - C#

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.

Solution 8 - C#

I've actually come up with my own solution and it seems to be working for me.

// This allows the clipboard to have something copied to it.
    public static void ClipboardPaste(String pasteString)
    {
        // This clears the clipboard
        Clipboard.Clear();

        // This is a "Try" of the statement inside {}, if it fails it goes to "Catch"
        // If it "Catches" an exception. Then the function will retry itself.
        try
        {
            // This, per some suggestions I found is a half second second wait before another
            // clipboard clear and before setting the clipboard
            System.Threading.Thread.Sleep(500);
            Clipboard.Clear();
            // This is, instead of using SetText another method to put something into
            // the clipboard, it includes a retry/fail set up with a delay
            // It retries 5 times with 250 milliseconds (0.25 second) between each retry.
            Clipboard.SetDataObject(pasteString, false, 5, 250);
        }
        catch (Exception)
        {
            ClipboardPaste(pasteString);
        }
    }

This is obviously C#, however these methods are exposed to all Visual Studios. I have obviously created a looping function, as well as attempted to force it into the clipboard with retries.

Essentially here's the flow. Let's say you want to place the word clipboard into the clipboard, anywhere in your code (assuming this function is defined).

  1. Call function ClipboardPaste("Clipboard");
  2. It will then clear the clipboard
  3. Then it will "try" to put your string into the clipboard.
  4. First it waits half a second (500 milliseconds)
  5. Clears the clipboard again
  6. Then it tries to put the string into the clipboard using SetDataObject
  7. SetDataObject if it fails will retry up to five times, with a 250 millisecond delay in between each retry.
  8. If the initial attempt fails, it catches the exception, the crash, then it tries it all over again.

Yes, this does have a flaw if you know your clipboard will always have an exception no matter what (infinite loop). However I have not run into an infinite loop with this method yet. The other flaw is that it can take a couple of seconds (essentially slowing down your applications) before it will work, while it's attempting it may freeze your application, once it succeeds the application will continue anyway.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRichard SlaterView Question on Stackoverflow
Solution 1 - C#Alex from JitbitView Answer on Stackoverflow
Solution 2 - C#Phil PriceView Answer on Stackoverflow
Solution 3 - C#Jeff RoeView Answer on Stackoverflow
Solution 4 - C#TriynkoView Answer on Stackoverflow
Solution 5 - C#Tony BennettView Answer on Stackoverflow
Solution 6 - C#Marten2009View Answer on Stackoverflow
Solution 7 - C#Patrick SameeraView Answer on Stackoverflow
Solution 8 - C#FlyingMongooseView Answer on Stackoverflow