剪贴板延迟渲染问题:每次Ctrl+V无法粘贴更新后的当前时间
问题:剪贴板延迟渲染后无法更新时间
我开发了一个Windows应用,利用剪贴板延迟渲染(delayed rendering)功能粘贴当前时间,但遇到问题:时间被渲染一次后,后续按下Ctrl+V时始终粘贴同一时间,无法获取最新的系统时间。
我认为需要再次调用SetClipboardData(CF_TEXT, nullptr);来恢复延迟渲染状态,但不清楚应该在何时何地执行该操作。同时想了解:能否检测到目标应用已获取剪贴板数据?如何实现用户每次按下Ctrl+V时都粘贴当前最新的时间?
附上相关C++代码:
#include <windows.h> #include <thread> #include <chrono> #include <ctime> #include <iomanip> #include <sstream> // Get current time with milliseconds (HH:MM:SS.xxx) void GetTime(std::string& time_str) { auto now = std::chrono::system_clock::now(); auto in_time_t = std::chrono::system_clock::to_time_t(now); auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000; std::tm bt{}; localtime_s(&bt, &in_time_t); std::ostringstream oss; oss << std::put_time(&bt, "%H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << milliseconds.count(); time_str = oss.str(); } void RenderClipboardData(HWND hwnd) { std::string time_str; GetTime(time_str); EmptyClipboard(); // Allocate global memory for the clipboard data HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, time_str.size() + 1); if (hGlobal) { // Lock the global memory and copy the string into it void* pGlobal = GlobalLock(hGlobal); if (pGlobal) { memcpy(pGlobal, time_str.c_str(), time_str.size() + 1); GlobalUnlock(hGlobal); SetClipboardData(CF_TEXT, hGlobal); } else { // Free the global memory if it wasn't successfully set GlobalFree(hGlobal); } } } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_RENDERFORMAT: if (wParam == CF_TEXT) { RenderClipboardData(hwnd); } break; case WM_CREATE: CreateWindow( L"STATIC", L"This application pastes the current time on Ctrl+V. ", WS_VISIBLE | WS_CHILD, 10, 10, 600, 100, hwnd, nullptr, reinterpret_cast<LPCREATESTRUCT>(lParam)->hInstance, nullptr); if (OpenClipboard(hwnd)) { EmptyClipboard(); SetClipboardData(CF_TEXT, nullptr); // Delayed rendering to get the current time CloseClipboard(); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { constexpr wchar_t CLASS_NAME[] = L"SampleWindowClass"; WNDCLASS wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); HWND hwnd = CreateWindowEx( 0, // Optional window styles. CLASS_NAME, // Window class L"Delayed Clipboard Rendering", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, 640, 120, nullptr, // Parent window nullptr, // Menu hInstance, // Instance handle nullptr // Additional application data ); if (hwnd == nullptr) { return 0; } ShowWindow(hwnd, nCmdShow); MSG msg = {}; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
已尝试的方案:
- 处理
WM_DESTROYCLIPBOARD消息并在此处重置剪贴板为延迟渲染状态。
解决方案
核心原因
第一次触发WM_RENDERFORMAT生成时间数据后,剪贴板会存储该静态数据,后续不再发起延迟渲染请求。要实现每次粘贴都获取最新时间,必须在剪贴板数据被消费后,重新将其设置回延迟渲染状态。
正确实现步骤
完善
WM_DESTROYCLIPBOARD消息处理WM_DESTROYCLIPBOARD消息会在剪贴板数据被目标应用获取完成后触发(也会在剪贴板被清空/替换时触发),这是重置延迟渲染的最佳时机。在WindowProc中添加以下代码:case WM_DESTROYCLIPBOARD: // 重新设置延迟渲染状态,等待下一次粘贴请求 if (OpenClipboard(hwnd)) { EmptyClipboard(); SetClipboardData(CF_TEXT, nullptr); CloseClipboard(); } break;修正
RenderClipboardData函数
原函数中的EmptyClipboard()是多余的,因为系统在触发WM_RENDERFORMAT时已经为你打开了剪贴板并做好了准备,重复调用会导致异常。修改后的函数如下:void RenderClipboardData(HWND hwnd) { std::string time_str; GetTime(time_str); // 移除多余的EmptyClipboard()调用 HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, time_str.size() + 1); if (hGlobal) { void* pGlobal = GlobalLock(hGlobal); if (pGlobal) { memcpy(pGlobal, time_str.c_str(), time_str.size() + 1); GlobalUnlock(hGlobal); SetClipboardData(CF_TEXT, hGlobal); } else { GlobalFree(hGlobal); } } }
关键说明
- 无需手动检测目标应用是否获取了剪贴板数据,
WM_DESTROYCLIPBOARD就是系统提供的明确信号,告知你当前剪贴板数据已被处理。 - 每次收到
WM_DESTROYCLIPBOARD后重新设置延迟渲染,就能确保下一次Ctrl+V时,系统会再次触发WM_RENDERFORMAT生成最新时间。
内容的提问来源于stack exchange,提问作者Thomas W.
相关产品推荐
相关产品推荐

