Почему DestroyWindow заблокирован?

Я создаю окно и цикл сообщений в дочернем потоке. Когда я отправил пользовательское сообщение через PostMessage, когда я вызвал DestroyWindow в потоке, DestroyWindow заблокировался и не вызвал WM_DESTORY, поэтому поток цикла сообщений не может нормально завершиться. Пробовал PostThreadMessage, Кроме того, пытался закрыть окно. Но проблема в том, что WM_DESTORY не срабатывает после вызова DestroyWindow, а не 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, 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(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;
    }

person yoe    schedule 06.05.2021    source источник
comment
Когда приложение блокируется. Нажмите Break All в отладчике. Проверьте поток, который пытается вызвать DestroyWindow, и посмотрите на стек вызовов.   -  person xMRi    schedule 07.05.2021


Ответы (1)


Окно имеет сходство с потоком. Только поток, создавший окно, может уничтожить окно (а также получать и отправлять сообщения для окна). Это четко указано в документации:

Поток не может использовать DestroyWindow для уничтожения окна, созданного другим потоком.

person Remy Lebeau    schedule 06.05.2021
comment
Заранее спасибо. DestroyWindow вызывается в потоке, который создает окно. Вы можете видеть, что DestroyWindow находится в CD3D11Device::ChildWindowMsgThread при получении пользовательского сообщения WM_QUIT_MSG_LOOP. Когда мне нужно удалить это окно, я отправляю сообщение WM_QUIT_MSG_LOOP в цикл сообщений. - person yoe; 06.05.2021
comment
Согласно документации DestroyWindow(): Если функция завершается успешно, возвращаемое значение не равно нулю. Если функция завершается ошибкой, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError. Что на самом деле возвращают DestroyWindow() и GetLastError()? Вы УВЕРЕНЫ, что DestroyWindow() действительно звонят? Код отправляет WM_QUIT_MSG_LOOP в m_hChildHwnd, но где назначается m_hChildWnd? В SetRenderChildHwnd()? Показанный код вряд ли является минимально воспроизводимым примером - person Remy Lebeau; 06.05.2021