请教:为什么子线程中创建的窗口,调用DestoryWindow时会阻塞?

hyqhero 2021-05-04 04:45:03
我在子线程中创建了一个窗口和消息循环。
当我通过PostMessage发送自定义消息,希望线程中的消息循环退出。然而当我在线程中收到自定义消息并调用DestroyWindow时,DestroyWindow被阻塞并且没有触发WM_DESTORY,因此消息循环线程不能正常退出。
我的问题是:什么情况下DestroyWindow会阻塞?与其它消息冲突?比如MoveWindow?有人遇到过类似的情况吗?
代码如下:
销毁窗口:
 
#define WM_QUIT_MSG_LOOP (WM_USER+8600)
mfxStatus CD3D11Device::DeleteRenderChildWindow()
{
LOG(LS_INFO) << "Intel D3D11Render, Enter DeleteRenderChildWindow, HWND:" << m_hChildHwnd;

//SetParent(m_hChildHwnd, NULL);
if (m_hChildHwnd){
LOG(LS_WARNING) << "Intel D3D11Render, DeleteRenderChildWindow, HWND:" << m_hChildHwnd;
PostMessage(m_hChildHwnd, WM_QUIT_MSG_LOOP, NULL, NULL);
}

if(m_pChildWindowMsgThread)
m_pChildWindowMsgThread->Wait();

MSDK_SAFE_DELETE(m_pChildWindowMsgThread);
MSDK_SAFE_DELETE(m_pCreateFinishEvent);
LOG(LS_INFO) << "Intel D3D11Render, Leave DeleteRenderChildWindow, HWND:" << m_hChildHwnd;

return MFX_ERR_NONE;
}

创建窗口:

LRESULT CALLBACK CD3D11Device::ChildRenderMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LRESULT lResult = 0;
switch (uMsg) {
case WM_DESTROY: {
LOG(LS_WARNING) << "Intel D3D11Render ChildRenderMsgProc, PostQuitMessage, HWND:" << hwnd;
PostQuitMessage(0);
break;
}
case WM_SETCURSOR: {
break;
}
default:
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}

return lResult;
}

HWND CD3D11Device::ThreadCreateChildWindow(){
HMODULE hInstance = nullptr;
BOOL result =
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<char*>(&DefWindowProc), &hInstance);

if (!result) {
LOG(LS_ERROR) << "[ThreadCreateChildWindow]GetModuleHandleExA failed.";
return 0;
}

// Register the host window class. See the MSDN documentation of the
WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = &ChildRenderMsgProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.lpszClassName = _T("Render Window Class");
//wcex.style |= CS_HREDRAW | CS_VREDRAW &~WS_CAPTION &~WS_SYSMENU;

// Ignore the error which may happen when the class is already registered.
RegisterClassExW(&wcex);

RECT rcClient = { 0 };
GetWindowRect(m_HandleWindow, &rcClient);
// Create the host window.
HWND hChildWindow =
CreateWindowW(_T("Render Window Class"), _T("MiniRender"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0,
0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, m_HandleWindow, nullptr, hInstance, nullptr);
if (!hChildWindow) {
LOG(LS_ERROR) << "[ThreadCreateChildWindow]Create child window failed.";
return 0;
}

ShowWindow(hChildWindow, SW_SHOW);
SetRenderChildHwnd(hChildWindow);
m_pCreateFinishEvent->Signal();
LOG(LS_WARNING) << "Intel D3D11Render, ThreadCreateChildWindow, HWND:" << hChildWindow;

return hChildWindow;
}

消息循环线程:

unsigned int CD3D11Device::ChildWindowMsgThread(void* ctx){
CD3D11Device* pD3D11Device = static_cast<CD3D11Device*>(ctx);

HWND hChildWindow = NULL;
if (pD3D11Device) {
hChildWindow = pD3D11Device->ThreadCreateChildWindow();
}

if (hChildWindow == NULL){
return 0;
}

MSG msg;
BOOL result;
while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (result == -1) {
LOG(LS_ERROR) << "Intel D3D11Render, ChildWindowMsgThread, GetMessage failed, HWND:" << hChildWindow;
continue;
}

if (msg.message == WM_QUIT_MSG_LOOP) {
LOG(LS_WARNING) << "Intel D3D11Render, ChildWindowMsgThread, recv WM_QUIT_MSG_LOOP, HWND:" << hChildWindow;
//DestroyWindow阻塞。why??
DestroyWindow(hChildWindow);
}
else {
PostMessage(pD3D11Device->GetParentHwnd(), msg.message, msg.wParam, msg.lParam);
}

LOG(LS_WARNING) << "Intel D3D11Render, ChildWindowMsgThread, GetMessageing, HWND:" << hChildWindow;
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}
...全文
1970 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
hyqhero 2021-05-06
  • 打赏
  • 举报
回复
引用 1 楼 zgl7903 的回复:
给线程投递消息 试试 PostThreadMessage
试了,没有用,不是postmessage的问题,是DestoryWindow的问题
_mervyn 2021-05-06
  • 打赏
  • 举报
回复
If the window being destroyed is a child window that does not have the WS_EX_NOPARENTNOTIFY style, a WM_PARENTNOTIFY message is sent to the parent. 死锁 我猜你应该是: 主线程在等子线程结束: m_pChildWindowMsgThread->Wait(); 不处理其他消息了 子线程在等主线程处理 WM_PARENTNOTIFY 不要让ui线程有任何机会阻塞,不调用任何可能会引起等待的接口。比如要调用诸如m_pChildWindowMsgThread->Wait() 之类的时候就要有条件反射的思考这样行不行。 这种父子窗口在不同线程的尤其要注意,因为你不知道哪个需要HWND的API内部可能就需要父子窗口间通信的。
zgl7903 2021-05-05
  • 打赏
  • 举报
回复
给线程投递消息 试试 PostThreadMessage

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧