如何处理剪贴板阻塞和其他异常情况

发布于 2024-07-21 12:21:15 字数 1008 浏览 5 评论 0原文

在过去的几个小时中,我一直在追踪一个相当具体的错误,该错误是由于另一个应用程序打开了剪贴板而发生的。 本质上,因为剪贴板是共享资源(根据 "为什么我的共享剪贴板不起作用?"),并且您尝试执行

Clipboard.SetText(string)

Clipboard.Clear().

抛出以下异常:

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)

我最初的解决方案是短暂暂停后重试,直到我意识到 Clipboard.SetDataObject 有用于次数和延迟长度的字段。 .NET 的默认行为是尝试 10 次,延迟 100 毫秒。

最终用户注意到了最后一件事。 也就是说,尽管抛出异常,复制到剪贴板操作仍然有效。 我无法找到有关原因的任何进一步信息。

我目前对问题的解决方案只是默默地忽略异常......这真的是最好的方法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

携君以终年 2024-07-28 12:21:15

Clipboard.SetDataObject(pasteString, true) 之前执行 Clipboard.Clear() 似乎可以解决问题。

之前设置 retryTimesretryDelay 的建议对我来说不起作用,无论如何,默认值是 retryTimes = 10retryDelay = 100 毫秒

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

雨的味道风的声音 2024-07-28 12:21:15

首先调用它:

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

我注意到,如果您正在进行粘贴操作(WM_PASTE 消息),包括在 TextChanged 事件期间,剪贴板将保持由接收该事件的窗口(TextBox)锁定。 因此,如果您只是在事件处理程序中调用“CloseClipboard”方法,那么您可以调用托管的 Clipboard.Clear 和 Clipboard.SetText 方法,而不会出现任何问题或延迟。

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.

流星番茄 2024-07-28 12:21:15

我今天遇到了这个错误。 我决定通过告诉用户潜在的行为不当的应用程序来处理它。 为此,您可以执行以下操作:

[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
}

对我来说,问题窗口标题是“skype_plugin_core_proxy_window”。 我搜索了相关信息,令我惊讶的是它只产生了一个点击,而且是俄语的。 因此,我添加这个答案,既是为了再次打击该字符串,也是为了提供进一步的帮助,以揭露潜在的行为不当的应用程序。

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.

他是夢罘是命 2024-07-28 12:21:15

由于剪贴板是由所有 UI 应用程序共享的,因此您有时会遇到这种情况。 显然,您不希望应用程序在无法写入剪贴板时崩溃,因此优雅地处理ExternalException是合理的。 我建议如果写入剪贴板的 SetObjectData 调用失败,则向用户显示错误。

建议使用(通过P/Invokeuser32!GetOpenClipboardWindow 查看其他应用程序是否具有剪贴板打开。 它将返回打开剪贴板的窗口的 HWND,如果没有应用程序打开它,则返回 IntPtr.Zero。 您可以旋转该值,直到达到 IntPtr.Zero 指定的时间。

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.

再浓的妆也掩不了殇 2024-07-28 12:21:15

另一种解决方法是使用 Clipboard.SetDataObject 而不是 Clipboard.SetText

根据 这篇 MSDN 文章,此方法有两个参数 - retryTimes retryDelay - 您可以像这样使用:

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

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
混吃等死 2024-07-28 12:21:15

通过使用 Jeff Roe 的代码(Jeff 的代码),

[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
}

您能够以非常方便的方式处理错误。

我通过使用 System.Windows.Forms.Clipboard 而不是 System.Windows.Clipboard 成功地降低了错误发生的频率。

我强调,这并不能解决问题,但它减少了我的应用程序的发生率。

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.

荒岛晴空 2024-07-28 12:21:15

我实际上已经提出了自己的解决方案,它似乎对我有用。

// 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);
        }
    }

这显然是 C#,但是这些方法向所有 Visual Studio 公开。 显然,我创建了一个循环函数,并尝试通过重试将其强制放入剪贴板。

基本上这就是流程。 假设您要将单词剪贴板放入剪贴板中的代码中的任何位置(假设已定义此函数)。

  1. 调用函数 ClipboardPaste("Clipboard");
  2. 然后它会清除剪贴板
  3. 然后它会“尝试”将您的字符串放入剪贴板。
  4. 首先,它等待半秒(500 毫秒),
  5. 再次清除剪贴板。
  6. 然后,它尝试使用 SetDataObject 将字符串放入剪贴板。
  7. 如果失败,SetDataObject 将重试最多五次,每次重试之间有 250 毫秒的延迟。
  8. 如果初始尝试失败,它会捕获异常(崩溃),然后重试。

是的,如果您知道无论如何您的剪贴板都会出现异常(无限循环),那么这确实有一个缺陷。 但是,我还没有使用此方法遇到无限循环。 另一个缺陷是,它可能需要几秒钟的时间(基本上会减慢您的应用程序的速度)才能工作,在尝试时可能会冻结您的应用程序,一旦成功,应用程序无论如何都会继续。

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.

随风而去 2024-07-28 12:21:15

这有点糟糕......但它解决了我的问题。

延迟后重试clear()。

更多信息请参阅博客文章如何处理阻止的剪贴板 - Clipboard.Clear() 错误

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文