为什么 DestroyWindow 被阻止?

问题描述

我在子线程中创建了一个窗口和消息循环。 当我通过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 来销毁由不同线程创建的窗口。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...