windows平台用setclipboardviewer监听剪贴板变化最直接:需创建隐藏窗口并运行消息循环,注册后系统主动发送wm_drawclipboard通知,须转发给nextclipboardviewer,并枚举所有可用格式(如cf_unicodetext、cf_html、cf_dib)读取内容。

Windows 平台用 SetClipboardViewer 监听剪贴板变化最直接
在 Windows 上,SetClipboardViewer 是最轻量、最符合系统机制的监听方式——它不轮询、不 hook、不依赖 UI 线程消息循环,而是让系统主动通知你剪贴板内容变更。关键在于:你必须注册一个窗口句柄(HWND),该窗口需能接收 WM_DRAWCLIPBOARD 消息。
常见错误是直接在控制台程序里调用 SetClipboardViewer 却没提供有效窗口。控制台进程默认无消息泵,也不会收到该消息。必须创建一个隐藏窗口(哪怕只用于接收消息),并运行自己的 GetMessage + DispatchMessage 循环。
- 使用
CreateWindowEx创建一个无样式(WS_POPUP)、无可见区域的窗口,类名自定义即可 - 注册后,每次剪贴板内容被修改(包括 Ctrl+C、右键复制、程序写入等),系统会向你的窗口发送
WM_DRAWCLIPBOARD - 收到消息后,立刻调用
OpenClipboard→EnumClipboardFormats或GetClipboardData读取内容,再调用CloseClipboard - 务必在窗口过程(
WndProc)中将WM_DRAWCLIPBOARD转发给NextClipboardViewer(通过ChangeClipboardChain链维护),否则其他监听程序会失效
读取剪贴板时要处理多种格式,别只认 CF_TEXT
用户复制的可能是纯文本、HTML、富文本(RTF)、图片(CF_DIB)、甚至文件列表(CF_HDROP)。如果只尝试获取 CF_TEXT,遇到微信复制的带样式的文字或截图就会返回空。
正确做法是先枚举当前剪贴板中的所有可用格式:
UINT fmt = EnumClipboardFormats(0);
while (fmt) {
// 尝试获取该格式数据,如 CF_UNICODETEXT、CF_HTML、CF_DIB 等
HANDLE hData = GetClipboardData(fmt);
if (hData) { /* 解析逻辑 */ }
fmt = EnumClipboardFormats(fmt);
}
重点注意:
-
CF_UNICODETEXT是现代 Windows 默认文本格式,优先于CF_TEXT;用GlobalLock获取宽字符指针,别当char*处理 -
CF_HTML格式有固定前缀("Version:..."后接 HTML 片段),需跳过头部再解析 -
CF_DIB返回的是位图原始数据(BITMAPINFO + 像素),不是HBITMAP,不能直接GetObject - 读取前必须确保
IsClipboardFormatAvailable(fmt)为真,否则GetClipboardData可能返回NULL
Linux(X11)用 xclip 或 XConvertSelection 都不实时,得靠 property change 监听
X11 没有类似 Windows 的“剪贴板变更广播”机制。所谓“监听”本质是轮询或监听 PropertyNotify 事件——因为 X11 把剪贴板(PRIMARY 和 CLIPBOARD)实现为窗口属性(XA_CLIPBOARD 等 atom)。
可靠方案是:创建一个不可见窗口,用 XSelectInput 监听自身窗口的 PropertyChangeMask,再用 XSetSelectionOwner 临时“抢占” CLIPBOARD 所有权。当其他程序修改剪贴板时,X Server 会向当前 owner 发送 SelectionNotify 或触发 property 变更。
- 不要依赖
xclip -o定时执行——延迟高、易丢变更、频繁 fork 开销大 - 真正低延迟需用
XLib手动管理连接、事件循环和 selection 请求流程 - 注意区分
PRIMARY(鼠标中键粘贴)和CLIPBOARD(Ctrl+C/V),多数应用只改后者 - Wayland 下更复杂:原生不支持全局剪贴板监听,需走
xdg-desktop-portal的 D-Bus 接口,且仅限 sandboxed 应用
macOS 用 NSPasteboard 的 addObserver:forTypes: 是唯一正解
macOS 提供了 Objective-C API 层的原生监听:NSPasteboard 支持添加观察者,指定关注的 pasteboard 类型(如 NSPasteboardTypeString、NSPasteboardTypePNG),系统会在内容变更时异步回调。
但 C++ 项目需桥接 Objective-C++(.mm 文件),且必须运行在 Cocoa 应用上下文中——即不能是纯命令行程序。最小可行结构是:
- 创建一个隐藏的
NSApplication实例(调用[NSApplication sharedApplication]) - 获取共享 pasteboard:
[NSPasteboard generalPasteboard] - 用
addObserver:forTypes:注册 observer 对象,并实现pasteboard:provideDataForType:回调 - 保持 run loop 运行(
[NSApp run]或用NSTimer驱动)
容易忽略的一点:NSPasteboard 的变更通知不包含“谁改的”,也不保证每次复制都触发(例如某些沙盒应用受限时可能静默失败)。若需捕获复制动作本身(而非结果),必须 hook NSPasteboard 的 writeObjects: 方法——这属于私有 API,上架 App Store 会被拒。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











