Skip to content

Instantly share code, notes, and snippets.

@ihewro
Created January 13, 2026 13:33
Show Gist options
  • Select an option

  • Save ihewro/0bddd0f9b2f56bb4aadcf5a746f616f0 to your computer and use it in GitHub Desktop.

Select an option

Save ihewro/0bddd0f9b2f56bb4aadcf5a746f616f0 to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <cstdio>
#include <string>
#include <chrono>
// ----------------- Common logging utilities -----------------
std::string NowTimeString() {
SYSTEMTIME st;
GetLocalTime(&st);
char buf[64];
std::snprintf(buf, sizeof(buf), "%02d:%02d:%02d.%03d",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
return buf;
}
void Log(const char* threadName, int seq, const char* msg,
BOOL ret = -1, DWORD err = (DWORD)-1) {
std::string time = NowTimeString();
DWORD tid = GetCurrentThreadId();
if (ret == (BOOL)-1) {
std::printf("[%s] [TID=%lu] [%s] #%d %s\n",
time.c_str(), tid, threadName, seq, msg);
} else {
std::printf("[%s] [TID=%lu] [%s] #%d %s ret=%d err=%lu\n",
time.c_str(), tid, threadName, seq, msg, (int)ret, err);
}
std::fflush(stdout);
}
// ----------------- Global: message window handle -----------------
HWND g_hMsgWnd = nullptr;
// ----------------- Simple message window -----------------
LRESULT CALLBACK MsgWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
HWND CreateMessageWindow(HINSTANCE hInst) {
const wchar_t CLASS_NAME[] = L"ClipboardTestMsgWindowClass";
WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MsgWndProc;
wcex.hInstance = hInst;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = CLASS_NAME;
if (!RegisterClassExW(&wcex)) {
std::printf("RegisterClassExW failed, GetLastError=%lu\n", GetLastError());
return nullptr;
}
HWND hWnd = CreateWindowExW(
0,
CLASS_NAME,
L"ClipboardTestMsgWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr,
nullptr,
hInst,
nullptr
);
if (!hWnd) {
std::printf("CreateWindowExW failed, GetLastError=%lu\n", GetLastError());
return nullptr;
}
// The window does not need to be shown; you can comment out ShowWindow
// ShowWindow(hWnd, SW_HIDE);
return hWnd;
}
// ----------------- Thread A: hold the clipboard for 20 seconds -----------------
DWORD WINAPI ThreadA(LPVOID) {
const char* name = "ThreadA";
int seq = 0;
Log(name, seq++, "Start");
HWND hWnd = g_hMsgWnd;
if (!hWnd) {
Log(name, seq++, "g_hMsgWnd is NULL, exiting");
return 0;
}
BOOL ok = ::OpenClipboard(hWnd);
DWORD err = ok ? 0 : ::GetLastError();
Log(name, seq++, "OpenClipboard (first, with hWnd)", ok, err);
if (!ok) {
Log(name, seq++, "OpenClipboard failed, exiting");
return 0;
}
Log(name, seq++, "Holding clipboard for 20 seconds...");
::Sleep(20000);
::CloseClipboard();
Log(name, seq++, "CloseClipboard");
Log(name, seq++, "End");
return 0;
}
// ----------------- Thread B: repeatedly try to open and close the clipboard -----------------
DWORD WINAPI ThreadB(LPVOID) {
const char* name = "ThreadB";
int seq = 0;
Log(name, seq++, "Start");
HWND hWnd = g_hMsgWnd;
if (!hWnd) {
Log(name, seq++, "g_hMsgWnd is NULL, exiting");
return 0;
}
auto start = std::chrono::steady_clock::now();
auto endTime = start + std::chrono::seconds(30);
int attempt = 0;
while (std::chrono::steady_clock::now() < endTime) {
BOOL ok = ::OpenClipboard(hWnd);
DWORD err = ok ? 0 : ::GetLastError();
char buf[128];
std::snprintf(buf, sizeof(buf), "OpenClipboard attempt=%d (with hWnd)", attempt);
Log(name, seq++, buf, ok, err);
if (ok) {
::CloseClipboard();
Log(name, seq++, "CloseClipboard after successful OpenClipboard");
}
++attempt;
::Sleep(200); // Try once every 200ms
}
Log(name, seq++, "End");
return 0;
}
// ----------------- main -----------------
int main() {
std::printf("=== Clipboard multi-thread OpenClipboard(hWnd) test ===\n");
std::fflush(stdout);
HINSTANCE hInst = GetModuleHandle(nullptr);
// 1. Create the message window
g_hMsgWnd = CreateMessageWindow(hInst);
if (!g_hMsgWnd) {
std::printf("Failed to create message window, exit.\n");
return 1;
}
std::printf("Message window created: HWND=0x%p\n", g_hMsgWnd);
// 2. Create thread A and B
HANDLE hThreadA = ::CreateThread(nullptr, 0, ThreadA, nullptr, 0, nullptr);
HANDLE hThreadB = ::CreateThread(nullptr, 0, ThreadB, nullptr, 0, nullptr);
if (!hThreadA || !hThreadB) {
std::printf("Failed to create threads. GetLastError=%lu\n", GetLastError());
return 1;
}
HANDLE handles[2] = { hThreadA, hThreadB };
::WaitForMultipleObjects(2, handles, TRUE, INFINITE);
::CloseHandle(hThreadA);
::CloseHandle(hThreadB);
std::printf("=== Test finished ===\n");
return 0;
}
@ihewro
Copy link
Author

ihewro commented Jan 13, 2026

ThreadA:OpenClipboard and Holding clipboard for 20 seconds...
ThreadB:Also OpenClipboard success!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment