過去数時間にわたって、別のアプリケーションがクリップボードを開いているために発生するかなり具体的なバグを追跡しています。基本的に、クリップボードは共有リソースであるため( 「共有クリップボードが機能しないのはなぜですか?」 )、実行しようとします
Clipboard.SetText(string)
または
Clipboard.Clear().
次の例外がスローされます。
System.Runtime.InteropServices.ExternalException:要求されたクリップボード操作は成功しませんでした。 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回試行することです。
最後に、エンドユーザーが注目していることが1つあります。つまり、例外がスローされても、クリップボードへのコピー操作は引き続き機能します。これがなぜなのかについて、これ以上の情報を見つけることができませんでした。
私の現在の問題の解決策は、例外を静かに無視することです...これが本当に最善の方法ですか?
クリップボードはすべてのUIアプリケーションで共有されるため、時々これに遭遇します。明らかに、クリップボードへの書き込みに失敗した場合にアプリケーションをクラッシュさせたくないので、ExternalExceptionを適切に処理することは合理的です。クリップボードに書き込むSetObjectData呼び出しが失敗した場合、ユーザーにエラーを提示することをお勧めします。
( P/Invoke を介して) user32!GetOpenClipboardWindow
を使用して、別のアプリケーションがクリップボードを開いているかどうかを確認することをお勧めします。クリップボードを開いているウィンドウのHWNDを返します。アプリケーションが開いていない場合はIntPtr.Zero
を返します。指定された時間、IntPtr.Zero
まで値をスピンできます。
別の回避策は、Clipboard.SetDataObject
の代わりにClipboard.SetText
を使用することです。
このMSDN記事 によると、このメソッドには2つのパラメーターがあります-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
今日、このエラーに遭遇しました。誤動作する可能性のあるアプリケーションについてユーザーに伝えることで、この問題を処理することにしました。そのためには、次のようなことができます。
[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」でした。私はそれに関する情報を検索しましたが、ヒットが1つしかなかったことに驚き、それはロシア語でした。したがって、この答えを追加します。その文字列に別のヒットを与え、誤動作する可能性のあるアプリを明らかにするためのさらなる支援を提供するためです。
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
を使用することで、エラーの頻度を減らすことができました。
これで問題が解決しないことを強調しますが、アプリケーションの発生を減らしました。
最初にこれを呼び出すだけです:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr CloseClipboard();
TextChangedイベント中を含め、貼り付け操作(WM_PASTEメッセージ)の途中にいる場合、クリップボードはイベントを受け取るウィンドウ(TextBox)によってロックされたままになることに気付きました。そのため、イベントハンドラー内でその「CloseClipboard」メソッドを呼び出すだけで、問題や遅延なしにマネージClipboard.ClearおよびClipboard.SetTextメソッドを呼び出すことができます。
Clipboard.Clear()
の前にClipboard.SetDataObject(pasteString, true)
を実行すると、うまくいくようです。
retryTimes
とretryDelay
を設定するという以前の提案は、私にとってはうまくいきませんでした。いずれの場合もデフォルトはretryTimes = 10
およびretryDelay = 100ms
これは少し安っぽいです...しかし、それは私の問題を解決しました。
しばらくしてからclear()を再試行してください。
詳細については、ブログ投稿ブロックされたクリップボードの処理方法-Clipboard.Clear()エラーを参照してください。