You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

剪贴板延迟渲染问题:每次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生成时间数据后,剪贴板会存储该静态数据,后续不再发起延迟渲染请求。要实现每次粘贴都获取最新时间,必须在剪贴板数据被消费后,重新将其设置回延迟渲染状态。

正确实现步骤

  1. 完善WM_DESTROYCLIPBOARD消息处理
    WM_DESTROYCLIPBOARD消息会在剪贴板数据被目标应用获取完成后触发(也会在剪贴板被清空/替换时触发),这是重置延迟渲染的最佳时机。在WindowProc中添加以下代码:

    case WM_DESTROYCLIPBOARD:
        // 重新设置延迟渲染状态,等待下一次粘贴请求
        if (OpenClipboard(hwnd)) {
            EmptyClipboard();
            SetClipboardData(CF_TEXT, nullptr);
            CloseClipboard();
        }
        break;
    
  2. 修正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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:50:24