Повторный рендеринг буфера обмена

У меня есть приложение, которое использует отложенный рендеринг буфера обмена для вставки текущего времени. Но: когда время было визуализировано один раз, оно всегда будет вставлять одно и то же время, а не обновленное время.

Думаю, мне придется снова вызвать SetClipboardData(CF_TEXT, nullptr);, чтобы восстановить еще один случай отложенного рендеринга, но я не знаю, когда и где мне это сделать. Могу ли я определить, когда целевое приложение взяло данные из буфера обмена?

Как я могу вставлять текущее время каждый раз, когда пользователь нажимает Ctrl+V?

#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 и сброс настроек буфера обмена для отложенного рендеринга.
Learn.microsoft.com/en-us/windows/win32/dataxchg/… должен объяснить, что вам нужно сделать.
BitTickler 15.07.2024 16:20

@BitTickler: возможно, это должно объяснять это, но, ИМХО, нет. Я пробовал реализовать WM_DESTROYCLIPBOARD, но это не имеет значения.

Thomas Weller 15.07.2024 16:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, НЕ следует вызывать EmptyClipboard() при рендеринге текста. Другое приложение, которому нужен текст, уже открыло буфер обмена, поэтому вам нужно просто поместить текст в буфер обмена и больше ничего не делать.

Теперь, чтобы выполнить то, что вы хотите, вы можете сделать так, чтобы ваш обработчик WM_RENDERFORMAT отправлял специальное сообщение в ваше окно после рендеринга данных, а затем запускал новую задержку рендеринга из этого обработчика сообщения, например:

bool SetClipboardDataDelayRendered(HWND hwnd) {
    if (!OpenClipboard(hwnd)) return false;
    EmptyClipboard();
    SetClipboardData(CF_TEXT, nullptr); // Delayed rendering to get the current time
    CloseClipboard();
    return true;
}

void RenderClipboardData(HWND hwnd) {
    ...
    // DO NOT DO THIS HERE!
    // EmptyClipboard();
    ...
}

static const UINT WM_SET_CB_DELAY_RENDERED = WM_USER + 100;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

    switch (uMsg) {
    case WM_CREATE:
        ...
        SendMessage(hwnd, WM_SET_CB_DELAY_RENDERED, 0, 0);
        break;
    case WM_SET_CB_DELAY_RENDERED:
        // keep trying until the clipboard can be opened...
        if (!SetClipboardDataDelayRendered(hwnd))
            PostMessage(hwnd, WM_SET_CB_DELAY_RENDERED, 0, 0);
        break;
    case WM_RENDERFORMAT:
        if (wParam == CF_TEXT) {
            RenderClipboardData(hwnd);
            // the other app has the clipboard open,
            // wait for it to close before resetting it...
            PostMessage(hwnd, WM_SET_CB_DELAY_RENDERED, 0, 0);
        }
        break;
    ...
    }
    return 0;
}

При этом имейте в виду, что после того, как вы запросите отложенный рендеринг ваших данных, другое приложение может прийти и заменить содержимое буфера обмена своими собственными данными, поэтому ваш отложенный рендеринг перестанет работать. Вы можете обнаружить это состояние, обработав WM_DESTROYCLIPBOARD и/или используя GetClipboardOwner(), чтобы убедиться, что ваш HWND по-прежнему является владельцем. В этом случае вам может потребоваться использовать таймер или другой механизм для перезапуска отложенного рендеринга.

Потрясающий. Большое спасибо. Работает так, как я хотел.

Thomas Weller 15.07.2024 21:10

Скорее грубая сила. Я не уверен, должно ли это быть решением по умолчанию. Голосовать не буду, но сомнения полны ;)

BitTickler 24.07.2024 04:34

@BitTickler, очевидно, это не исчерпывающее решение. Возможно, потребуется дополнительная работа. Это необычное использование буфера обмена. Я не рассмотрел все возможные сценарии.

Remy Lebeau 24.07.2024 05:43

Другие вопросы по теме