如何在单独线程中创建POPUP窗口并避免子窗口自动销毁
核心问题原因
- Windows的窗口和创建它的线程强绑定,创建窗口的线程必须运行独立的消息循环来处理窗口消息,你的
task1函数创建完窗口后直接退出,线程结束后绑定的子窗口自然被系统销毁。 - 你在主窗口的
WM_CREATE回调中直接调用t1.join(),会阻塞主线程,导致主线程的消息循环无法正常运行,主窗口也会卡住。 - 子窗口的
WM_DESTROY处理中调用了PostQuitMessage,会直接触发子线程消息循环退出,同时不符合子窗口跟随主窗口销毁的要求。
具体修改方案
1. 给子窗口线程添加消息循环
修改task1函数,在创建完子窗口之后添加消息循环,直到子窗口被销毁才退出线程:
int task1(MyStruct myStruct) { sprintf_s(msgbuf_ansi, ("thread\n")); OutputDebugStringA(msgbuf_ansi); HWND hwnd_child; myStruct.wc.lpfnWndProc = WndProcChild; myStruct.wc.lpszClassName = g_szChildClassName; if (!RegisterClassEx(&myStruct.wc)) { MessageBox(NULL, L"thread - Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } hwnd_child = CreateWindowEx(0, g_szChildClassName, L"Child", WS_POPUP | WS_BORDER, 200, 0, 190, 110, myStruct.hWnd, 0, myStruct.hInst, 0); // 新增:保存子窗口句柄到全局变量 g_hwndChild = hwnd_child; swprintf_s(msgbuf, _T("THREAD - CHILD - hwnd: %02X\n"), (int)hwnd_child); OutputDebugString(msgbuf); if (hwnd_child == NULL) { MessageBox(NULL, L"thread - Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } SetWindowLong(hwnd_child, GWL_EXSTYLE, GetWindowLong(hwnd_child, GWL_EXSTYLE) | WS_EX_LAYERED); SetLayeredWindowAttributes(hwnd_child, 0, 128, LWA_ALPHA); ShowWindow(hwnd_child, myStruct.nCmdShow); UpdateWindow(hwnd_child); // 新增:子线程独立消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
2. 调整主窗口线程逻辑,移除阻塞的join调用
首先新增两个全局变量,用来保存子窗口句柄和子线程对象:
HWND g_hwndChild = NULL; thread t1;
修改主窗口WM_CREATE回调,不要阻塞主线程:
case WM_CREATE: { swprintf_s(msgbuf, _T("WM_CREATE - PARENT \n")); OutputDebugString(msgbuf); t1 = thread(task1, g_myStruct); // 移除t1.join(),避免阻塞主线程消息循环 return 0; }
3. 调整销毁逻辑,实现子窗口跟随主窗口销毁
修改主窗口的WM_DESTROY回调,主动销毁子窗口并等待子线程退出:
case WM_DESTROY: swprintf_s(msgbuf, _T("WM_DESTROY - PARENT \n")); OutputDebugString(msgbuf); // 主动销毁子窗口 if (g_hwndChild != NULL) { DestroyWindow(g_hwndChild); } // 等待子线程正常退出 if (t1.joinable()) { t1.join(); } PostQuitMessage(0); return 0;
子窗口的WM_DESTROY回调保留PostQuitMessage即可,仅用来退出子线程自身的消息循环,不会影响主进程运行。
其他优化建议
- 多线程访问全局变量时建议加锁,避免线程安全问题。
- 窗口操作尽量在创建它的线程中执行,跨线程控制窗口可以用
SendMessage或PostMessage传递消息。 - 全局变量
g_fMouseTracking如果要创建多个子窗口会产生冲突,建议用SetWindowLongPtr将这个状态绑定到对应子窗口的额外存储空间中。
内容的提问来源于stack exchange,提问作者Derek007
相关产品推荐
相关产品推荐

