Сообщение Windows Путаница в цикле

У меня есть окно GUI в WTL, которое запускается внутри потока внутри экземпляра CMessageLoop, который был добавлен в экземпляр приложения и запускается. Теперь внутри обработчика кнопок в основном графическом интерфейсе я создаю новое окно. Как только я нажимаю эту кнопку, создаю окно и пытаюсь опубликовать сообщение о выходе в основной цикл графического интерфейса. Код:

Главное окно имеет собственный поток:

    CMessageLoop theLoop;
    _MyppModule.AddMessageLoop(&theLoop);
    if(m_pMyDlg == NULL) {
        m_pMyDlg = new CMyDlg();
        if(!IsWindow(*m_pMyDlg))
        {
            m_pMyDlg->Create(NULL);
            m_pMyDlg->ShowWindow(SW_SHOW);
            nRet = theLoop.Run();
            _MyppModule.RemoveMessageLoop();
        }
    }

Обработчик кнопки и создание дочернего окна:

LRESULT CMyDlg::OnButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{

     ChildWindowDlg childDlg;
     childDlg.Create(m_hWnd);

     childDlg.ShowWindow(SW_SHOW);

     CMessageLoop _loop;
     );

     _loop.Run();
     ::DestroyWindow(childDlg);

     return S_OK;
}

Теперь, если я нажму кнопку «Закрыть» в моем окне MyDlg, будет вызван обработчик кнопки, внутри которого я делаю ::PostQuitMessage, но это никогда не достигает цикла сообщений theLoop из первого фрагмента кода. Это происходит после выхода из второго цикла, поэтому _loop уничтожается, а дочерний диалог уничтожается. Что здесь происходит?


person AlexandruC    schedule 19.09.2013    source источник


Ответы (2)


Второй фрагмент кода совершенно неестественен. Если ваша цель состоит в том, чтобы открыть всплывающее окно, затем закрыть его, а затем вы хотите завершить выполнение обработчика нажатия кнопки, тогда модальный диалог — это то, что вам нужно:

LRESULT CMyDlg::OnButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, 
                                BOOL& bHandled)
{
     ChildWindowDlg childDlg; // Add constructor parameters if needed
     // Additional initlaization calls might go here
     const INT_PTR nResult = childDlg.DoModal(m_hWnd); // DoModal handles it all
     if(nResult == IDOK) { ... } // Hey's we even have result coming from `EndDialog`
     return 0; // No S_OK here
}

Никаких циклов сообщений, никаких PostQuitMessage, никаких отдельных вызовов создания/уничтожения окна не требуется. Для этого и нужны модальные диалоги.


Если вы не хотите блокировать «вызывающее» окно, и идея состоит в том, чтобы и главное, и подчиненное окна работали бок о бок (или одно является частью другого, в любом случае, оба должны реагировать одновременно), тогда вы не хотите блокировать обработчик сообщений. Обработчик создаст окно и настроит его (.Create, .ShowWindow), а затем выйдет из функции OnButtonClicked.

Оба окна созданы, оба живы, и их сообщение отправляется им циклом сообщений верхнего уровня. Это правильный подход, обычно вам не нужно более одного цикла сообщений на поток. Иногда это может иметь смысл для конкретных операций, но это действительно редко. Windows — это пассивные экземпляры. Они отвечают на сообщения своими обработчиками сообщений. Цикл сообщений потока обслуживает все окна потока, потому что он выполняет DispatchMessage вызов API, который, в свою очередь, ищет целевое окно, берет его WndProc и вызывает его, передавая детали сообщения.

person Roman R.    schedule 19.09.2013
comment
Я хочу, чтобы это второе окно было похоже на рамку в моем основном dlg. - person AlexandruC; 19.09.2013
comment
Поэтому я хотел бы также иметь возможность вызывать другие элементы управления из основного графического интерфейса. Я не хочу, чтобы ребенок блокировал родительский - person AlexandruC; 19.09.2013
comment
и теперь у меня все еще есть одна проблема.. что, если сообщение WM_QUIT приходит от родителя, когда ребенок активен? это завершит дочерний цикл, уничтожит окна ... но цикл основного окна все еще будет активен. - person AlexandruC; 19.09.2013
comment
Представленная проблема применима так, как я это сделал, у вас есть идеи получше? - person AlexandruC; 19.09.2013
comment
Я мог бы обойти эту проблему, если бы запустил дочерний диалог внутри основного цикла, но это означало бы динамическое уничтожение и воссоздание дочернего окна в этом обработчике кнопок, мне нужно сделать это, потому что мне это может понадобиться на другом языке. , Это хороший подход? - person AlexandruC; 19.09.2013
comment
Без блокировки... вам не нужен второй цикл сообщений. В любом случае вторая петля неверна. Вам нужно Create + ShowWindow и оставить обработчик (функцию). Окно будет создано и будет обслуживаться из цикла сохранения сообщений в качестве окна-владельца. - person Roman R.; 19.09.2013
comment
есть идеи по этому поводу? stackoverflow.com/questions/18803783 / - person AlexandruC; 20.09.2013
comment
Я не думаю, что вы разместили там соответствующий код, поэтому у вас нет соответствующей обратной связи. - person Roman R.; 20.09.2013

Здесь у вас есть два цикла сообщений, один из которых является вложенным. С другой стороны, очередь сообщений — одна на поток и заполняется самым внутренним циклом сообщений (с GetMessage). Итак, сообщение WM_QUIT извлекается внутренним циклом сообщений внутри CMyDlg::OnButtonClicked.

person noseratio    schedule 19.09.2013
comment
Извините .. Я забыл упомянуть: после того, как я закрою дочерний диалог и выйду из функции обработчика, я сразу же отредактирую. - person AlexandruC; 19.09.2013
comment
Как выйти из второго цикла, дважды вызвать PostQuitMessage? Показать больше кода. - person noseratio; 19.09.2013
comment
Чувак... Я не вызывал PostQuitMessage из childDialog... я просто скрывал это... мой плохой! - person AlexandruC; 19.09.2013