Модальное диалоговое окно MFC не отображается из-за неактивных проверок в CWnd::RunModalLoop
Ниже я поставил источник CWnd::RunModal
, который является циклом сообщений, запускаемым при вызове CDialog::DoModal
- он вступает во владение как вложенный цикл сообщений, пока диалог не закончится.
Обратите внимание, что за исключением пары особых случаев ShowWindow
вызывается только когда очередь сообщений свободна.
Это приводит к тому, что в некоторых случаях в нашем приложении диалог не появляется в течение многих секунд, когда DoModal
называется. Если я отлаживаю код и ставлю точки останова, я вижу, что цикл фазы 1 не достигнут до этого времени. Однако, если я создаю тот же диалог немодально (вызов Create
затем ShowWindow
это появляется мгновенно) - но это было бы неловким изменением, чтобы просто исправить ошибку, не понимая ее хорошо.
Есть ли способ избежать этой проблемы? Возможно я могу позвонить ShowWindow
явно в какой-то момент, например, или опубликовать сообщение для запуска бездействия? Я прочитал "Старая новая вещь - модальность", которая была очень информативной, но не ответила на этот вопрос, и я могу только найти ее редко упоминаемой в Интернете, без успешного разрешения.
wincore.cpp: CWnd:: RunModalLoop
int CWnd::RunModalLoop(DWORD dwFlags)
{
ASSERT(::IsWindow(m_hWnd)); // window must be created
ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
HWND hWndParent = ::GetParent(m_hWnd);
m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
MSG *pMsg = AfxGetCurrentMessage();
// acquire and dispatch messages until the modal state is done
for (;;)
{
ASSERT(ContinueModal());
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
{
ASSERT(ContinueModal());
// show the dialog when the message queue goes idle
if (bShowIdle)
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
bShowIdle = FALSE;
}
// call OnIdle while in bIdle state
if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
{
// send WM_ENTERIDLE to the parent
::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
}
if ((dwFlags & MLF_NOKICKIDLE) ||
!SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
{
// stop idle processing next time
bIdle = FALSE;
}
}
// phase2: pump messages while available
do
{
ASSERT(ContinueModal());
// pump message, but quit on WM_QUIT
if (!AfxPumpMessage())
{
AfxPostQuitMessage(0);
return -1;
}
// show the window when certain special messages rec'd
if (bShowIdle &&
(pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
bShowIdle = FALSE;
}
if (!ContinueModal())
goto ExitModal;
// reset "no idle" state after pumping "normal" message
if (AfxIsIdleMessage(pMsg))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
}
ExitModal:
m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
return m_nModalResult;
}
3 ответа
Поэтому, чтобы ответить на мой собственный вопрос, решение, которое я нашел, состояло в том, чтобы явно вызвать следующие два метода:
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
CWnd::RunModalLoop
должен вызывать их, но только когда он обнаруживает, что очередь сообщений пуста / неактивна. Если этого не происходит, то диалоговое окно существует и блокирует ввод в другие окна, но не отображается.
Из сообщений отслеживания я нашел WM_ACTIVATE
было последнее сообщение, отправленное до того, как что-то застряло, поэтому я добавил OnActivate()
обработчик моего класса Dialog.
Я обнаружил очень похожую проблему в своем приложении. Это произошло только тогда, когда приложение было под большой нагрузкой для рисования (пользователь может открывать множество представлений, представления показывают данные в реальном времени, поэтому они постоянно обновляются, и каждое представление должно быть нарисовано независимо, а процесс рисования занимает много времени. времени). Итак, если в этом сценарии пользователь пытается открыть какой-либо модальный диалог (скажем, диалоговое окно «О программе», или если приложению необходимо отобразить какой-либо модальный диалог, например
BOOL CMyDialog::ContinueModal()
{
// Begin extra code by Me
if (m_nFlags & WF_CONTINUEMODAL)
{
if (!m_bShown && !(GetStyle() & WS_VISIBLE))
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
m_bShown = TRUE;
}
}
// End extra code
return m_nFlags & WF_CONTINUEMODAL;
}
Это вызывает
Недавно у меня была такая же проблема. Я решил, отправив недокументированное сообщение 0x118 перед вызовом DoModal(), который обрабатывается на этапе 2.
...
PostMessage(0x118, 0, 0);
return CDialog::DoModal();