Tenho um aplicativo que usa renderização atrasada da área de transferência para colar a hora atual. Mas: quando a hora for renderizada uma vez, sempre irá colar a mesma hora, e não a hora atualizada.
Acho que precisaria ligar SetClipboardData(CF_TEXT, nullptr);
novamente, para restaurar outro caso de renderização atrasada, mas não sei quando ou onde devo fazer isso. Posso detectar quando o aplicativo de destino retirou os dados da área de transferência?
Como posso colar a hora atual cada vez que o usuário pressiona 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;
}
Eu tentei:
- processar a
WM_DESTROYCLIPBOARD
mensagem e redefinir a área de transferência para renderização atrasada.
Em primeiro lugar, você NÃO deve ligar
EmptyClipboard()
ao renderizar o texto. Outro aplicativo que deseja o texto já abriu a área de transferência, então basta colocar o texto na área de transferência e não fazer mais nada.Agora, para realizar o que deseja, você pode fazer com que seu
WM_RENDERFORMAT
manipulador poste uma mensagem personalizada em sua janela após renderizar os dados e, em seguida, emitir uma nova renderização de atraso a partir desse manipulador de mensagens, por exemplo:Dito isso, lembre-se de que depois de solicitar a renderização atrasada de seus dados, outro aplicativo poderá aparecer e substituir o conteúdo da área de transferência por seus próprios dados, portanto, sua renderização atrasada irá parar de funcionar. Você pode detectar essa condição manipulando
WM_DESTROYCLIPBOARD
e/ou usandoGetClipboardOwner()
para ter certeza de que vocêHWND
ainda é o proprietário. Talvez seja necessário usar um cronômetro ou outro mecanismo para reiniciar a renderização atrasada se isso acontecer.