WinForms 中 Clipboard 操作必须在 STA 线程执行,控制台需加 [STAThread],WPF 需 Dispatcher.Invoke;读取应先用 GetDataObject 判断格式再获取,监听需通过 Win32 API 注册 WM_CLIPBOARDUPDATE。

Windows Forms 应用中直接调用 Clipboard 类就能读写剪贴板,但必须确保当前线程是 STA(Single-Threaded Apartment),否则会抛出 InvalidOperationException: "Current thread must be set to single thread apartment (STA) mode" —— 这是 90% 的初学者卡住的第一步。
为什么 WinForms 里 Clipboard.SetText 失败?
因为 .NET 的 Clipboard 类底层依赖 Windows COM 剪贴板 API,强制要求调用线程标记为 STA。控制台程序默认是 MTA,WPF 主线程虽是 STA,但 WinForms 的 Application.Run() 启动方式才真正保障了上下文完整性。
- WinForms 窗体中直接用
Clipboard.SetText("hello")是安全的(窗体线程天然 STA) - 控制台项目想用?必须在
Main方法上加[STAThread]特性,且不能用Task.Run或后台线程调用 - WPF 中推荐用
Clipboard.SetText,但需确保在 UI 线程执行(可用Dispatcher.Invoke) - 跨进程或长时间运行的后台任务里写剪贴板?不可靠——系统可能清空它,也不建议轮询监听
如何安全读取多种格式(文本 / 图片 / 文件路径)?
Clipboard.GetDataObject() 返回 IDataObject,它支持多格式共存。别直接用 Clipboard.GetText(),它只处理 Unicode 文本,遇到图片或拖入的文件列表就返回空。
- 先检查是否包含目标格式:
dataObj.GetDataPresent(DataFormats.Bitmap) - 读文本优先用
DataFormats.Text或DataFormats.UnicodeText,避免乱码 - 读文件路径用
dataObj.GetData(DataFormats.FileDrop),返回string[],注意它只在“拖放复制”时存在(比如从资源管理器 Ctrl+C 一个文件) - 读图片后务必判空:
var img = dataObj.GetData(DataFormats.Bitmap) as Image,COM 对象可能已释放
如何监听剪贴板内容变化?
Windows 没有原生的剪贴板变更事件,.NET 也没有内置监听机制。常见做法是用 Win32 API 的 AddClipboardFormatListener,但必须满足:窗口句柄有效、消息循环正常、且仅限前台窗口。
- WinForms 中重写
WndProc,捕获WM_CLIPBOARDUPDATE(0x031D) - 注册前必须调用
Clipboard.Clear()触发一次初始状态,否则首次变化可能漏掉 - 不要在回调里做耗时操作(如保存大图到磁盘),会阻塞 UI 消息队列
- .NET 6+ 可用
PInvoke.User32.AddClipboardFormatListener,但需 NuGet 安装Microsoft.Windows.CsWin32
真正麻烦的是格式兼容性:同一个剪贴板内容,Chrome 复制的富文本可能带 HTML 和 RTF,而记事本只留纯文本;截图工具往里写的是 Bitmap,但有些旧软件只认 DIB 格式。实际项目里,别指望一次读取覆盖所有场景,得按需 fallback 到不同格式分支判断。










