Windows平台C++中如何复制GetClipboardData返回的剪贴板句柄内容?
解决剪贴板内容保存与恢复的问题
核心问题在于你保存的是剪贴板返回的原句柄,而EmptyClipboard()会让剪贴板回收这些句柄,导致后续无法使用。正确的做法是提前复制句柄指向的实际内容到自己管理的内存中,而不是保存原句柄。
实现步骤
- 打开剪贴板并枚举所有存在的格式:不要硬编码格式列表,用
EnumClipboardFormats遍历当前剪贴板支持的所有格式,更可靠。 - 复制每个格式的内容到自有内存:对每个格式,通过
GlobalLock获取内容指针,GlobalSize获取内容长度,然后分配符合剪贴板要求的内存(需带GMEM_MOVEABLE标记),将内容复制进去。 - 设置新剪贴板内容并执行操作:清空剪贴板,设置新内容,完成操作后再恢复原内容。
- 恢复原剪贴板内容:再次清空剪贴板,将之前保存的每个格式和对应内存句柄交还给剪贴板。
示例代码
#include <vector> #include <windows.h> // 保存剪贴板格式与对应内容的结构体 typedef struct { UINT format; HANDLE data; } ClipboardItem; int main() { std::vector<ClipboardItem> savedClipboard; HANDLE newClipData = WhatIWantOnClipboard(); // 生成新剪贴板内容的自定义函数 UINT newFormat = CF_TEXT; // 根据实际需求替换格式 // 1. 打开剪贴板并保存原内容 if (OpenClipboard(NULL)) { UINT format = EnumClipboardFormats(0); while (format != 0) { HANDLE hClipData = GetClipboardData(format); if (hClipData != NULL) { LPVOID pSrc = GlobalLock(hClipData); SIZE_T dataSize = GlobalSize(hClipData); // 分配可被剪贴板接管的内存 HANDLE hDest = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dataSize); if (hDest != NULL) { LPVOID pDest = GlobalLock(hDest); memcpy(pDest, pSrc, dataSize); GlobalUnlock(hDest); savedClipboard.push_back({format, hDest}); } GlobalUnlock(hClipData); } format = EnumClipboardFormats(format); } // 2. 设置新剪贴板内容 EmptyClipboard(); SetClipboardData(newFormat, newClipData); CloseClipboard(); // 释放剪贴板,允许其他程序访问 // 执行需要使用新剪贴板的操作 DoSomethingWithClipboard(); // 3. 恢复原剪贴板内容 if (OpenClipboard(NULL)) { EmptyClipboard(); for (auto& item : savedClipboard) { SetClipboardData(item.format, item.data); // 注意:SetClipboardData后,剪贴板接管内存所有权,无需手动释放 } CloseClipboard(); } } return 0; }
关键说明
- 原句柄失效原因:
GetClipboardData返回的句柄由剪贴板管理,调用EmptyClipboard()时,剪贴板会立即释放这些句柄,后续再使用会触发"访问拒绝"错误。 - 内存分配要求:必须用
GlobalAlloc并指定GMEM_MOVEABLE,因为剪贴板需要可移动的内存块来管理。SetClipboardData后,内存所有权转移给剪贴板,不要自行释放。 - 特殊格式处理:对于私有格式或
CF_OWNERDISPLAY这类特殊格式,可能需要额外处理,但绝大多数标准格式(如文本、图片)都适用此方法。
内容的提问来源于stack exchange,提问作者mRedgate
相关产品推荐
相关产品推荐

