2009-05-30 82 views
45

在過去的幾個小時中,我一直在跟蹤一個相當具體的錯誤,因爲另一個應用程序打開了剪貼板。本質上作爲剪貼板是一個共享資源(按"Why does my shared clipboard not work?"),並試圖執行如何處理被阻止的剪貼板和其他奇怪

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的默認行爲是嘗試延時100毫秒10次。

有已被最終用戶注意到一個最後一件事,那就是儘管被拋出的異常的複製到剪貼板操作仍然有效,我一直沒能找到有關爲什麼這可以是任何進一步的信息。

我當前解決這個問題的方法就是默默地忽略異常......這真的是最好的方法嗎?

回答

23

由於剪貼板由所有UI應用程序共享,你會遇到這種不時顯然,你不想讓你的應用程序崩潰,如果它無法寫入剪貼板,所以適當地處理ExternalException是合理的,如果setobjectdata調用寫入剪貼板失敗,我會建議向用戶顯示和出錯。

建議使用(通過p/invoke)user32!GetOpenClipboardWindow來查看是否另一個應用程序打開了剪貼板,它將返回剪貼板打開的窗口的HWND,或者如果沒有應用程序打開該窗口,您可以旋轉該值直到IntPtr.Zero達指定的時間。

+0

我已經閱讀了GetOpenClipboardWindow,似乎這是剪貼板訪問問題的最佳解決方案。感謝您的回覆。 – 2009-05-31 07:00:47

+0

如何獲取阻止剪貼板的進程 - 請參閱:http://stackoverflow.com/questions/6583642/determine-which-process-is-locking-the-clipboard – toong 2011-09-13 14:23:39

+1

只需先關閉剪貼板。看到我的答案。 – Triynko 2012-10-10 19:08:49

38

對不起復活一個老問題,但另一個解決方法是使用Clipboard.SetDataObject而不是Clipboard.SetText

根據this msdn article這種方法有兩個參數 - 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 
+0

一點都不好,好點。 – 2011-03-05 17:45:44

+0

我面對的問題是,即使retryTimes和retryDelay(20/500)的數字都很高,打開Firefox仍然會導致失敗。很奇怪... – aaaidan 2011-08-30 04:17:48

0

這是有點蹩腳的..但它解決了我的問題。

延遲後重試clear()。

更多資料@this博客。

10

今天我遇到了這個錯誤。我決定通過告訴用戶有關可能存在不當行爲的應用程序來處理它。要做到這一點,你可以做這樣的事情:

[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」。我搜索了這方面的信息,驚訝地發現它只有一次命中,那是俄文。因此,我添加了這個答案,以便爲該字符串提供另一個命中,並提供進一步的幫助以引發潛在的不正常應用程序亮起。

-3

所以我其實是拿出自己的解決方案。我知道我有點晚了,但是,這似乎是爲我工作。

// 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 Studios。我明顯地創建了一個循環函數,並且試圖用重試的方式強制它進入剪貼板。

從本質上講,這裏的流動。比方說,你要的話剪貼板放置到剪貼板中,在你的代碼的任何地方(假設這個函數的定義)。

  1. 調用函數ClipboardPaste(「Clipboard」);
  2. 然後它會清除剪貼板
  3. 然後它會「嘗試」將您的字符串放入剪貼板。
  4. 首先,它等待半秒(500毫秒)
  5. 清除剪貼板再次
  6. ,然後嘗試使用SetDataObject
  7. SetDataObject如果失敗,將重試5次把字符串到剪貼板,以每次重試之間有250毫秒的延遲。
  8. 如果初始嘗試失敗,它捕獲該異常,死機,那麼它會嘗試它一遍。

是的,如果你知道你的剪貼板將永遠有一個異常,無論是什麼(無限循環),這確實有缺陷。不過,我還沒有遇到這種方法的無限循環。另一個缺陷是,它可能需要幾秒鐘(基本上是放慢您的應用程序)之前,它會工作,而它的嘗試可能凍結您的應用程序,一旦成功應用將仍然繼續。

1

一個Clipboard.Clear()Clipboard.SetDataObject(pasteString, true)這樣做,似乎這樣的伎倆。

設置retryTimesretryDelay的早先提出的建議並沒有爲我在任何情況下,默認的方式retryTimes = 10retryDelay = 100ms

0

的工作只是把這個第一:

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

我注意到,如果你」在粘貼操作(WM_PASTE消息)的中間,包括在TextChanged事件期間,剪貼板仍然由接收事件的窗口(TextBox)鎖定。因此,如果您只是在事件處理程序中調用該「CloseClipboard」方法,則可以調用託管的Clipboard.Clear和Clipboard.SetText方法,而不會有任何問題或延遲。

0

通過利用傑夫·羅的代碼(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

我強調這不能解決問題,但它減少了我的應用程序的發生。