fix: 复制按钮加重试逻辑(CLIPBRD_E_CANT_OPEN 是常见 race)

修复 bug:点 [复制] 按钮偶发 "OpenClipboard 失败 0x800401D0 (CLIPBRD_E_CANT_OPEN)"。

根因:Clipboard.SetText 不自带重试,剪贴板被其他进程占着(RDP 同步、杀软扫描、用户刚复制过其他内容)时会抛 COMException 0x800401D0,典型 race condition。

修法:CopyMessageButton_Click 里包一层重试循环,最多 10 次,每次失败后 Thread.Sleep 50ms 再试(总等待上限 ~500ms,对 UI 不可感知)。失败兜底弹 MessageBox 显示重试次数 + HRESULT 0x 代码,方便用户诊断。

只捕获 COMException(剪贴板错误都走 OLE COM 接口),其他异常类型继续向上抛。
This commit is contained in:
2026-09-06 23:04:18 +08:00
parent 021a45465d
commit c036a8dcb8
+28 -7
View File
@@ -292,11 +292,31 @@ public partial class MainWindow : Window
{
if (HistoryList.SelectedItem is not GotifyMessage msg) return;
var text = BuildFullMessageText(msg);
try
// 剪贴板常被其他进程占着(RDP、杀软、其他复制操作),CLIPBRD_E_CANT_OPEN 是常见 race。
// Clipboard.SetText 不自带重试,自己包一层:最多 10 次,每次等 50ms。
const int MaxAttempts = 10;
const int DelayMs = 50;
Exception? lastEx = null;
bool ok = false;
for (int attempt = 1; attempt <= MaxAttempts; attempt++)
{
try
{
Clipboard.SetText(text);
ok = true;
break;
}
catch (System.Runtime.InteropServices.COMException ex)
{
lastEx = ex;
System.Threading.Thread.Sleep(DelayMs);
}
}
if (ok)
{
Clipboard.SetText(text);
_logger?.Info($"Copied message to clipboard. id={msg.Id}");
// 按钮反馈:短暂显示「已复制 ✓」
if (sender is Button btn)
{
var origContent = btn.Content;
@@ -313,11 +333,12 @@ public partial class MainWindow : Window
timer.Start();
}
}
catch (Exception ex)
else
{
_logger?.Error("Copy to clipboard failed.", ex);
MessageBox.Show($"复制失败:{ex.Message}", "Windows Gotify",
MessageBoxButton.OK, MessageBoxImage.Warning);
_logger?.Error("Copy to clipboard failed after retries.", lastEx);
MessageBox.Show(
$"复制失败:剪贴板被其他程序占用,重试 {MaxAttempts} 次仍未成功。\n请关闭正在使用剪贴板的程序后重试。\n\n错误:0x{(lastEx?.HResult ?? 0):X8}",
"Windows Gotify", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}