Windows平台用OpenClipboard+GetClipboardData读纯文本最稳:需严格按OpenClipboard→GetClipboardData→GlobalLock→复制→GlobalUnlock→CloseClipboard顺序操作,优先用CF_UNICODETEXT,强制转LPCWSTR后拷贝,读完立即CloseClipboard防卡死。

Windows 平台用 OpenClipboard + GetClipboardData 读纯文本最稳
Windows 剪贴板 API 是原生、低层但可靠的方案,不依赖第三方库。读纯文本(CF_TEXT 或更推荐的 CF_UNICODETEXT)必须按顺序调用 OpenClipboard → GetClipboardData → GlobalLock → 复制内容 → GlobalUnlock → CloseClipboard。漏掉任意一步都可能失败或卡死剪贴板。
常见错误现象:GetClipboardData 返回 NULL(没开权限、其他进程占着、格式不匹配);GlobalLock 返回 nullptr(数据已释放或格式非全局内存块)。
实操建议:
- 始终先调用
OpenClipboard(nullptr),传nullptr表示不关联特定窗口句柄,避免线程/窗口上下文问题 - 优先请求
CF_UNICODETEXT,而非CF_TEXT,后者只支持 ANSI,Win10+ 默认用 UTF-16,容易乱码 -
GlobalLock返回的是LPVOID,需强制转为LPCWSTR(宽字符)再用wcscpy_s拷出,别直接 reinterpret_cast 后当std::string用 - 读完立刻
CloseClipboard(),否则其他程序可能无法访问剪贴板——这是用户最常忽略的“卡剪贴板”根源
写入富文本(RTF)要用 SetClipboardData 配合 GlobalAlloc 手动管理内存
富文本不是简单塞字符串。Windows 要求你把 RTF 内容(如 {\rtf1\ansi\ansicpg932\deff0\deflang1041{\fonttbl{\f0\fnil\fcharset0 MS Gothic;}}\viewkind4\uc1\pard\f0\fs20 hello\par})写进一块由 GlobalAlloc(GMEM_MOVEABLE, size) 分配的全局内存,并用 GlobalLock 写入后解锁,再传给 SetClipboardData(CF_RTF, hMem)。
立即学习“C++免费学习笔记(深入)”;
关键点在于:这块内存所有权移交给了系统,你不能 GlobalFree 它,也不能在 SetClipboardData 后继续读写——系统会在需要时自动锁定/解锁。
实操建议:
- RTF 字符串末尾必须有
\par或\line,否则部分编辑器(如 WordPad)可能拒绝渲染 - 分配内存大小要包含结尾的空字符(
wcslen(rtf_str) + 1),且用GlobalSize(hMem)校验是否匹配 - 不要尝试用
CF_HTML替代 RTF——它不是标准剪贴板格式,很多程序(包括记事本、VS Code)根本不识别,兼容性极差 - 若写入后粘贴出来仍是纯文本,大概率是目标程序不支持 RTF,或你误用了
CF_TEXT格式 ID
Linux(X11)下用 xcb 读写剪贴板得手动处理 UTF8_STRING 和 text/plain 两个 Atom
X11 没有统一“剪贴板”概念,实际有三套选择(selection):PRIMARY(鼠标中键粘贴)、CLIPBOARD(Ctrl+C/V)、SECONDARY(极少用)。C++ 程序通常只管 CLIPBOARD,但必须显式声明支持哪些 MIME 类型。
读取时,你要先 xcb_convert_selection 请求 UTF8_STRING,等事件回来再解析;写入时,得响应 xcb_selection_request_event_t,并在回调里用 xcb_change_property 把数据以 UTF8_STRING 格式塞进去。现代应用还应同时提供 text/plain;charset=utf-8(对应 Atom text/plain),否则 Firefox、GNOME Terminal 可能 fallback 到乱码。
实操建议:
- 别用老旧的
Xlib封装(如XFetchBuffer),它不支持多格式协商,且线程不安全;xcb是唯一可控方案 - 写入前必须调用
xcb_set_selection_owner抢占CLIPBOARD,否则你的数据永远不会被发出去 - 读取返回的数据是 raw bytes,需按 UTF-8 解码成
std::u8string或std::string(C++20 前就用std::string存 UTF-8 字节流) - Wayland 下这套完全失效——得走
wp_clipboard_grabD-Bus 协议,和 X11 不兼容,必须运行时检测显示协议
macOS 用 NSPasteboard 最省事,但必须在主线程且链接 AppKit
macOS 的 NSPasteboard 是 Objective-C API,C++ 项目需混编(.mm 文件),且所有调用必须在主线程(AppKit 非线程安全)。读纯文本直接 [pb stringForType:NSPasteboardTypeString];写入则 [pb clearContents]; [pb writeObjects:@[str]]; 即可,自动适配 Unicode。
富文本支持靠 NSPasteboardTypeRTF 或 NSPasteboardTypeHTML:前者传 NSData *(含 RTF 字节),后者传 HTML 字符串(注意 meta charset 必须是 UTF-8)。系统会自动做格式降级(比如目标程序只认纯文本,就 fallback 到 NSPasteboardTypeString)。
实操建议:
- 工程里必须 link
-framework AppKit,且 C++ 文件后缀改为.mm,否则链接报_OBJC_CLASS_$_NSPasteboard未定义 - 不要在子线程调用,哪怕加了
@autoreleasepool也没用——会 crash 或静默失败 - 写入 HTML 时,确保字符串以
<meta charset="utf-8">开头,否则 Safari、Notes 可能当 Latin-1 解析 - macOS 13+ 对沙盒应用限制变严:需在
entitlements中开启com.apple.security.network.client(虽不走网络,但 pasteboard 服务底层用 XPC)
跨平台剪贴板看似只是读写字符串,实则每个系统都在用完全不同机制和约束。最易被忽略的是生命周期管理:Windows 的 CloseClipboard、X11 的 selection owner 权限、macOS 的主线程绑定——它们不报错,但会让功能间歇性失灵,排查时往往绕半天才想到这一步。


















