如何无丢失地将GetClipboardData的HANDLE数据存文件并回载?
剪贴板HANDLE数据的保存与恢复方案
针对你提出的需求,以下是Windows平台下无丢失保存/加载剪贴板数据的实现方案,优先覆盖CF_LOCALE、CF_TEXT、CF_OEMTEXT、CF_UNICODETEXT格式,同时兼容绝大多数标准剪贴板格式。
核心原理
剪贴板返回的HANDLE是全局内存对象,直接保存HANDLE本身没有意义(因为HANDLE是进程内的内存引用,重启或跨进程后失效)。正确的做法是:
- 从HANDLE中读取实际数据内容
- 记录剪贴板格式ID、数据大小
- 将上述信息写入文件
- 恢复时,根据保存的格式和数据重新分配符合剪贴板要求的全局内存,再将数据写入并设置到剪贴板
实现代码(C++)
保存剪贴板数据到文件
#include <windows.h> #include <wchar.h> bool SaveClipboardDataToFile(const wchar_t* filePath) { if (!OpenClipboard(nullptr)) return false; bool success = false; HANDLE hData = nullptr; UINT format = 0; // 优先检查目标格式 UINT targetFormats[] = {CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT, CF_LOCALE, 0}; for (int i = 0; targetFormats[i] != 0; ++i) { if (IsClipboardFormatAvailable(targetFormats[i])) { format = targetFormats[i]; hData = GetClipboardData(format); break; } } // 如果目标格式都不存在,取第一个可用格式 if (!hData) { format = EnumClipboardFormats(0); if (format) hData = GetClipboardData(format); } if (hData) { DWORD dataSize = 0; LPVOID pLockedData = GlobalLock(hData); switch (format) { case CF_TEXT: case CF_OEMTEXT: dataSize = lstrlenA((LPSTR)pLockedData) + 1; // 包含结尾空字符 break; case CF_UNICODETEXT: dataSize = lstrlenW((LPWSTR)pLockedData) * sizeof(WCHAR) + sizeof(WCHAR); break; case CF_LOCALE: dataSize = sizeof(LCID); break; default: dataSize = GlobalSize(hData); // 其他格式直接取全局内存大小 break; } GlobalUnlock(hData); if (dataSize > 0) { HANDLE hFile = CreateFileW(filePath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != INVALID_HANDLE_VALUE) { DWORD written; // 写入格式ID WriteFile(hFile, &format, sizeof(UINT), &written, nullptr); // 写入数据大小 WriteFile(hFile, &dataSize, sizeof(DWORD), &written, nullptr); // 写入数据内容 pLockedData = GlobalLock(hData); if (pLockedData) { WriteFile(hFile, pLockedData, dataSize, &written, nullptr); GlobalUnlock(hData); success = true; } CloseHandle(hFile); } } } CloseClipboard(); return success; }
从文件加载剪贴板数据
bool LoadClipboardDataFromFile(const wchar_t* filePath) { HANDLE hFile = CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) return false; bool success = false; UINT format = 0; DWORD dataSize = 0; DWORD read; // 读取格式ID if (!ReadFile(hFile, &format, sizeof(UINT), &read, nullptr) || read != sizeof(UINT)) { CloseHandle(hFile); return false; } // 读取数据大小 if (!ReadFile(hFile, &dataSize, sizeof(DWORD), &read, nullptr) || read != sizeof(DWORD)) { CloseHandle(hFile); return false; } // 分配剪贴板要求的全局内存(必须用GMEM_MOVEABLE) HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dataSize); if (hData) { LPVOID pData = GlobalLock(hData); if (pData) { // 读取文件中的数据 if (ReadFile(hFile, pData, dataSize, &read, nullptr) && read == dataSize) { GlobalUnlock(hData); // 写入剪贴板 if (OpenClipboard(nullptr)) { EmptyClipboard(); if (SetClipboardData(format, hData)) { success = true; // 注意:SetClipboardData后,内存所有权移交剪贴板,不能再释放 } else { GlobalFree(hData); } CloseClipboard(); } else { GlobalFree(hData); } } else { GlobalUnlock(hData); GlobalFree(hData); } } else { GlobalFree(hData); } } CloseHandle(hFile); return success; }
关键注意事项
- 内存要求:剪贴板接受的内存必须是用
GlobalAlloc分配的GMEM_MOVEABLE类型内存,不能用堆内存或栈内存 - 字符串格式:必须保存结尾的空字符(
\0或\0\0),否则恢复后的字符串会出现截断或乱码 - CF_LOCALE处理:该格式存储的是
LCID(区域设置ID),直接保存数值即可,恢复后系统会自动识别 - 剪贴板所有权:调用
SetClipboardData成功后,不能再调用GlobalFree释放内存,所有权已移交系统 - 兼容性:对于自定义剪贴板格式,只要是基于全局内存的,该方案都能兼容
内容的提问来源于stack exchange,提问作者A_72_
相关产品推荐
相关产品推荐

