问题描述
我在子线程中创建了一个窗口和消息循环。
当我通过PostMessage
发送自定义消息时,当我在线程中调用DestroyWindow
时,DestroyWindow
阻塞了,没有触发WM_DESTORY,所以消息循环线程无法正常退出。
我试过PostThreadMessage
,另外我试过用closewindow。但问题是在调用 DestroyWindow
后不会触发 WM_DESTORY,而不是 PostMessage
。
消息循环线程仍在运行并且窗口句柄有效,为什么?折腾了几天也没找到原因,非常感谢。
销毁窗口:
#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,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,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,recv WM_QUIT_MSG_LOOP,HWND:" << hChildWindow;
//这里堵了
DestroyWindow(hChildWindow);
}
else {
PostMessage(pD3D11Device->GetParentHwnd(),msg.message,msg.wParam,msg.lParam);
}
LOG(LS_WARNING) << "Intel D3D11Render,GetMessageing,HWND:" << hChildWindow;
TranslateMessage(&msg);
dispatchMessage(&msg);
}
return 0;
}
解决方法
窗口具有线程关联性。只有创建窗口的线程才能销毁窗口(并为窗口接收和发送消息)。这在 documentation 中有明确说明:
线程不能使用 DestroyWindow 来销毁由不同线程创建的窗口。