如何处理被阻止的剪贴板和其他古怪
在过去几个小时中,我一直在跟踪一个相当具体的错误,因为另一个应用程序打开了剪贴板。 本质上,由于剪贴板是一个共享的资源(按照“为什么我的共享剪贴板不起作用?” ),你试图执行
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文章,这个方法有两个参数 – retryTimes和retryDelay – 你可以这样使用:
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议设置retryTimes
和retryDelay
不适用于我,在任何情况下默认值是retryTimes = 10
和retryDelay = 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程。 比方说,你想把剪贴板放在剪贴板,在你的代码中的任何地方(假设这个函数被定义)。
- 调用函数ClipboardPaste(“Clipboard”);
- 它将清除剪贴板
- 然后它会“尝试”把你的string放入剪贴板。
- 首先等待半秒(500毫秒)
- 再次清除剪贴板
- 然后它尝试使用SetDataObject将string放入剪贴板
- 如果失败,SetDataObject将重试最多5次,每次重试之间有250毫秒的延迟。
- 如果最初的尝试失败,它捕获exception,崩溃,然后再试一遍。
是的,如果你知道你的剪贴板将永远有一个exception(无限循环),那么这是有缺陷的。 不过,我还没有遇到这种方法的无限循环。 另一个缺点是它可能需要几秒钟的时间(实质上会减慢你的应用程序的运行速度),而它正在尝试它可能会冻结你的应用程序,一旦成功,应用程序将继续运行。