如何处理被阻止的剪贴板和其他古怪

在过去几个小时中,我一直在跟踪一个相当具体的错误,因为另一个应用程序打开了剪贴板。 本质上,由于剪贴板是一个共享的资源(按照“为什么我的共享剪贴板不起作用?” ),你试图执行

Clipboard.SetText(string) 

要么

 Clipboard.Clear(). 

引发以下exception:

 System.Runtime.InteropServices.ExternalException:请求的剪贴板操作没有成功。 
    在System.Windows.Forms.Clipboard.ThrowIfFailed(Int32 hr)
    在System.Windows.Forms.Clipboard.SetDataObject(对象数据,布尔副本,Int32重试时间,Int32重试延迟)
    在System.Windows.Forms.Clipboard.SetText(string文本,TextDataFormat格式)
    在System.Windows.Forms.Clipboard.SetText(string文本)

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

terminal用户已经注意到最后一件事情,即使拷贝到剪贴板的操作仍然有效,但我仍然无法find任何进一步的信息。

我目前解决这个问题的方法就是默默无视例外……这真的是最好的方法吗?

由于剪贴板是由所有的UI应用程序共享的,所以你会偶尔遇到这个问题,显然你不希望你的应用程序崩溃,如果它没有写入剪贴板,所以优雅地处理ExternalException是合理的,我会build议提交如果setobjectdata调用写入到剪贴板失败,则用户发生错误。

build议使用(通过p / invoke) user32!GetOpenClipboardWindow来查看是否另一个应用程序打开了剪贴板,它将返回打开剪贴板的窗口的HWND,或者如果没有应用程序打开,则返回IntPtr.Zero ,你可以旋转的值,直到它的IntPtr.Zero一段指定的时间。

我很抱歉复活一个旧的问题,但另一个解决方法是使用Clipboard.SetDataObject而不是Clipboard.SetText

根据这个MSDN文章,这个方法有两个参数 – retryTimesretryDelay – 你可以这样使用:

 Clipboard.SetDataObject( "some text", //text to store in clipboard false, //do not keep after our app exits 5, //retry 5 times 200); //200ms delay between retries 

我今天遇到这个错误。 我决定通过告诉用户有关可能行为不当的应用程序来处理这个问题。 要做到这一点,你可以做这样的事情:

 [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”。 我search了这方面的信息,惊讶地发现只有一个命中,那是俄文。 因此,我添加了这个答案,这是为了给这个string另一个命中,并提供进一步的帮助,以潜在的行为不端的应用程序。

Clipboard.SetDataObject(pasteString, true) Clipboard.Clear()之前做一个Clipboard.Clear()似乎有诀窍。

早先的build议设置retryTimesretryDelay不适用于我,在任何情况下默认值是retryTimes = 10retryDelay = 100ms

这有点蹩脚..但它解决了我的问题。

延迟后重试clear()。

更多信息@ 这个博客。

先打电话给这个:

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

我注意到,如果您正在进行粘贴操作(WM_PASTE消息),包括TextChanged事件期间,剪贴板仍然被接收事件的窗口(TextBox)locking。 因此,如果您只是在事件处理程序中调用“CloseClipboard”方法,则可以调用托pipe的Clipboard.Clear和Clipboard.SetText方法,而不会有任何问题或延迟。

通过使用Jeff Roe的代码( 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 } 

你可以用非常方便的方式处理错误。

我设法通过使用System.Windows.Forms.Clipboard而不是System.Windows.Clipboard来减less错误的频率。

我强调这并不能解决问题,但却减less了我的申请事件。

所以我实际上已经提出了自己的解决scheme。 我知道我有点晚了,但是,这似乎是为我工作。

 // 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#,但是这些方法暴露给所有视觉工作室。 我明显创build了一个循环函数,并试图强制它进入剪贴板重试。

基本上这是stream程。 比方说,你想把剪贴板放在剪贴板,在你的代码中的任何地方(假设这个函数被定义)。

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

是的,如果你知道你的剪贴板将永远有一个exception(无限循环),那么这是有缺陷的。 不过,我还没有遇到这种方法的无限循环。 另一个缺点是它可能需要几秒钟的时间(实质上会减慢你的应用程序的运行速度),而它正在尝试它可能会冻结你的应用程序,一旦成功,应用程序将继续运行。