mfc 程序DoModal 后调用MessageBox 问题

hjjdebug 2009-11-24 12:05:10
看论坛,问MessageBox 的问题也不少,就是没有我问的。
问题提出: mfc 程序DoModal 后调用MessageBox 问题
建一个mfc 向导程序,基于对话框的那种。添加两句MessageBox函数,如下:

int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
int ret=MessageBox(NULL,"OK pressed","info",MB_OK); //这句被执行到,ret为1,但消息框不显示
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MessageBox(NULL,"Cancel pressed","info",MB_OK); //当这句被执行到时,消息框不显示。
}

问: 为什么消息框不能显示呢?
把消息框函数移到DoModal()之前, 是可以显示消息框的。

请高人指点! 谢谢!

...全文
552 19 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
M_S_D_N 2009-11-25
  • 打赏
  • 举报
回复
非常遗憾,你的本质理解还是错了。。。

如果是因为
    if (!ContinueModal())
      goto ExitModal;
导致第二个窗口退出,那么去掉m_pMainWnd = &dlg ;,对话框退出时也是会调用EndDialog,也必定导致m_nFlags标记的变化。
导致第二个窗口立即退出,是因为这两句代码的上一段:
// pump message, but quit on WM_QUIT
if (!AfxGetThread()->PumpMessage())
{
AfxPostQuitMessage(0);
return -1;
}
根本原因还是因为线程消息队列中有WM_QUIT的存在。

[Quote=引用 15 楼 hejinjing_tom_com 的回复:]
引用 14 楼 m_s_d_n 的回复:
呵呵,想得心应手,需要了解现象,更要懂得本质。
这段理解的确有问题。


本质我在13楼已经申明了,欢迎老兄评价!
[/Quote]
hjjdebug 2009-11-25
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 m_s_d_n 的回复:]
呵呵,想得心应手,需要了解现象,更要懂得本质。
这段理解的确有问题。
[/Quote]

本质我在13楼已经申明了,欢迎老兄评价!
hjjdebug 2009-11-25
  • 打赏
  • 举报
回复
看来我们最终的意见是一致的,交流上的误会请见谅,谢谢!
M_S_D_N 2009-11-25
  • 打赏
  • 举报
回复
这些代码我已经跟过N遍了。

总结一下:
正常MFC对话框的退出是由下面代码导致的:
if (!ContinueModal())
goto ExitModal;

在主窗口(m_pMainWnd)已销毁的情况下,后续对话框的退出是由下面代码直接导致的:
// pump message, but quit on WM_QUIT
if (!AfxGetThread()->PumpMessage())
{
AfxPostQuitMessage(0);
return -1;
}
但根本原因是进程的主线程队列中被放入了WM_QUIT消息,则是由下面代码导致的:
// WM_NCDESTROY is the absolute LAST message sent.
void CWnd::OnNcDestroy()
{
// cleanup main and active windows
CWinThread* pThread = AfxGetThread();
if (pThread != NULL)
{
if (pThread->m_pMainWnd == this) // 注意这里
{
if (!afxContextIsDLL)
{
// shut down current thread if possible
if (pThread != AfxGetApp() || AfxOleCanExitApp())
AfxPostQuitMessage(0);
}
pThread->m_pMainWnd = NULL;
}
if (pThread->m_pActiveWnd == this)
pThread->m_pActiveWnd = NULL;
}
...
}

[Quote=引用 17 楼 hejinjing_tom_com 的回复:]
m_s_d_n 兄弟还是要调试一下代码再回答比较准确。
针对于我提到的这个简单的对话框程序。
这里我把我的调试结论再重申一下:
1. 不管有无 m_pMainWnd = &dlg 语句, 对话框的退出均通过:
    if (!ContinueModal())
      goto ExitModal;    // 该语句退出。
2. 如果无m_pMainWnd = &dlg 语句, 退出流程在哪里出现了分支 ?
  在这里,你也提到过了。
  if (pThread != NULL)
{
if (pThread->m_pMainWnd == this)  // 当m_pMainWnd == this
{
if (!afxContextIsDLL)      // 该模块又不是DLL
{
// shut down current thread if possible
if (pThread != AfxGetApp() || AfxOleCanExitApp())
AfxPostQuitMessage(0);  // 发送WM_QUIT 消息
}
pThread->m_pMainWnd = NULL;
}
if (pThread->m_pActiveWnd == this)
pThread->m_pActiveWnd = NULL;
}
所以,若不为m_pMainWnd 赋值,则它保持为空,将不会执行AfxPostQuitMessage(0) 语句。
所以MessageBox 可以正常弹出。

至于你提到的
C/C++ code
导致第二个窗口立即退出,是因为这两句代码的上一段:// pump message, but quit on WM_QUITif (!AfxGetThread()->PumpMessage())
{
AfxPostQuitMessage(0);return-1;
}
该观点是我在12楼猜测提出,在13楼纠正,你在14楼批判,怎么到16楼你又回到我12楼的猜测状态了。跟一下就明白了。
谢谢你的帮助, 正是你的一句参见MFC代码, 才使我想到跟踪一下MFC代码。
[/Quote]
hjjdebug 2009-11-25
  • 打赏
  • 举报
回复
m_s_d_n 兄弟还是要调试一下代码再回答比较准确。
针对于我提到的这个简单的对话框程序。
这里我把我的调试结论再重申一下:
1. 不管有无 m_pMainWnd = &dlg 语句, 对话框的退出均通过:
    if (!ContinueModal())
      goto ExitModal; // 该语句退出。
2. 如果无m_pMainWnd = &dlg 语句, 退出流程在哪里出现了分支 ?
在这里,你也提到过了。
if (pThread != NULL)
{
if (pThread->m_pMainWnd == this) // 当m_pMainWnd == this
{
if (!afxContextIsDLL) // 该模块又不是DLL
{
// shut down current thread if possible
if (pThread != AfxGetApp() || AfxOleCanExitApp())
AfxPostQuitMessage(0); // 发送WM_QUIT 消息
}
pThread->m_pMainWnd = NULL;
}
if (pThread->m_pActiveWnd == this)
pThread->m_pActiveWnd = NULL;
}
所以,若不为m_pMainWnd 赋值,则它保持为空,将不会执行AfxPostQuitMessage(0) 语句。
所以MessageBox 可以正常弹出。

至于你提到的

导致第二个窗口立即退出,是因为这两句代码的上一段:
// pump message, but quit on WM_QUIT
if (!AfxGetThread()->PumpMessage())
{
AfxPostQuitMessage(0);
return -1;
}

该观点是我在12楼猜测提出,在13楼纠正,你在14楼批判,怎么到16楼你又回到我12楼的猜测状态了。跟一下就明白了。
谢谢你的帮助, 正是你的一句参见MFC代码, 才使我想到跟踪一下MFC代码。
hw_henry2008 2009-11-24
  • 打赏
  • 举报
回复
此时窗体对象已经推出了,这是OK键的默认动作。
它里边调用了CDialog::OnOK();而在你的程序中,
上面有语句:m_pMainWnd = &dlg;就是把dlg设置成应用程序的主类
当点击了OK 或者CANCEL后,
应用程序主窗口退出了,但此时C++的类还在,就是说dlg还没有析构。
如果你将m_pMainWnd = &dlg去掉后
就能顺利显示了。
下面附上MSDN DoModal 介绍:

CDialog::DoModal
virtual int DoModal( );

Return Value

An int value that specifies the value of the nResult parameter that was passed to the CDialog::EndDialog member function, which is used to close the dialog box. The return value is –1 if the function could not create the dialog box, or IDABORT if some other error occurred.

Remarks

Call this member function to invoke the modal dialog box and return the dialog-box result when done. This member function handles all interaction with the user while the dialog box is active. This is what makes the dialog box modal; that is, the user cannot interact with other windows until the dialog box is closed.

If the user clicks one of the pushbuttons in the dialog box, such as OK or Cancel, a message-handler member function, such as OnOK or OnCancel, is called to attempt to close the dialog box. The default OnOK member function will validate and update the dialog-box data and close the dialog box with result IDOK, and the default OnCancel member function will close the dialog box with result IDCANCEL without validating or updating the dialog-box data. You can override these message-handler functions to alter their behavior.

Note PreTranslateMessage is now called for modal dialog box message processing.
hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
回复1楼: this->hwnd 不存在,若改成对话框handle, m_pMainWnd , 问题依旧。
回复2楼: 加2个,加3个MessageBox, 都不能够正常弹出。
回复3楼: 您的方法可行,消息框可以弹出!!, 谢谢!!
能说说设置了m_pMainWnd, 为什么就不能弹出对话框了呢 ? 非常感谢!
Ryanwen 2009-11-24
  • 打赏
  • 举报
回复
原因就是主消息循环已经退出了,可以用如下方式实现


m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::MessageBox(NULL, " ButtonOK is Clicked", "Message:", MB_OK);
}
M_S_D_N 2009-11-24
  • 打赏
  • 举报
回复
楼主去掉所摘抄代码前面的
m_pMainWnd = &dlg;
wocow3 2009-11-24
  • 打赏
  • 举报
回复
MFC架构,主窗口销毁后会PostQuitMessage,DoModal之后对话框已经销毁,线程队列有WM_QUIT,MessageBox内嵌消息循环碰到WM_QUIT直接退出。
楼主试下连续两个MessageBox,第二个MessageBox就能正常弹出
flyeag 2009-11-24
  • 打赏
  • 举报
回复
试试把NULL,改成this->hwnd
M_S_D_N 2009-11-24
  • 打赏
  • 举报
回复
呵呵,想得心应手,需要了解现象,更要懂得本质。
这段理解的确有问题。
[Quote=引用 12 楼 hejinjing_tom_com 的回复:]
to: M_S_D_N, 老兄的执着令我感动,结贴快了点,如果没有结贴,一定为你加分。留待下次吧。
其实这个帖子到现在止还有一点没有讨论清楚,就是为什么这个WM_QUIT 消息没有被消息循环消耗掉。
假如去掉m_pMainWnd = &dlg. 则pThread->m_pMainWnd != this,
消息循环仍然也会收到WM_QUIT ,否则它怎么退出消息循环呢 ? 而pThread->m_pMainWnd == this 时
会发一个额外的WM_QUIT, 不知这种理解对否 ?
不过说实话, mfc 对话框的的消息循环在哪里 ? 我现在还不知道。
[/Quote]
hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
重新研究了一下MFC代码,发现上面的理解是有问题的。
mfc 对话框消息循环在CWnd::RunModalLoop wincore.cpp 中
它的消息循环退出方式为:
do
{
.....
if (!ContinueModal())
goto ExitModal;
.....
} while(...)
ExitModal:
.....

而ContinueModal()为
BOOL CWnd::ContinueModal()
{
return m_nFlags & WF_CONTINUEMODAL;
}
当你按下OK, 或CANCEL 按钮,会改变m_nFlags, 从而退出循环。
void CDialog::OnOK()
{
if (!UpdateData(TRUE))
{
TRACE0("UpdateData failed during dialog termination.\n");
// the UpdateData routine will set focus to correct item
return;
}
EndDialog(IDOK); //会调用CDialog::EndDialog
}
void CDialog::EndDialog(int nResult)
{
ASSERT(::IsWindow(m_hWnd));

if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL))
EndModalLoop(nResult);

::EndDialog(m_hWnd, nResult); //调用API EndDialog
}
void CWnd::EndModalLoop(int nResult)
{
ASSERT(::IsWindow(m_hWnd));

// this result will be returned from CWnd::RunModalLoop
m_nModalResult = nResult;

// make sure a message goes through to exit the modal loop
if (m_nFlags & WF_CONTINUEMODAL)
{
m_nFlags &= ~WF_CONTINUEMODAL; // 会清除运行标志位,使dialog 循环退出
PostMessage(WM_NULL);
}
}
hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
to: M_S_D_N, 老兄的执着令我感动,结贴快了点,如果没有结贴,一定为你加分。留待下次吧。
其实这个帖子到现在止还有一点没有讨论清楚,就是为什么这个WM_QUIT 消息没有被消息循环消耗掉。
假如去掉m_pMainWnd = &dlg. 则pThread->m_pMainWnd != this,
消息循环仍然也会收到WM_QUIT ,否则它怎么退出消息循环呢 ? 而pThread->m_pMainWnd == this 时
会发一个额外的WM_QUIT, 不知这种理解对否 ?
不过说实话, mfc 对话框的的消息循环在哪里 ? 我现在还不知道。
M_S_D_N 2009-11-24
  • 打赏
  • 举报
回复
参见MFC源代码:
OnNcDestroy是在窗口被销毁时响应的函数;
当对话框句柄记录在m_pMainWnd时,会调用AfxPostQuitMessage(0)往线程消息队列中发送
WM_QUIT消息,致使之后的窗口一初始化便立即退出。

// WM_NCDESTROY is the absolute LAST message sent.
void CWnd::OnNcDestroy()
{
// cleanup main and active windows
CWinThread* pThread = AfxGetThread();
if (pThread != NULL)
{
if (pThread->m_pMainWnd == this) // 注意这里
{
if (!afxContextIsDLL)
{
// shut down current thread if possible
if (pThread != AfxGetApp() || AfxOleCanExitApp())
AfxPostQuitMessage(0);
}
pThread->m_pMainWnd = NULL;
}
if (pThread->m_pActiveWnd == this)
pThread->m_pActiveWnd = NULL;
}
...
}
hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
最终的采纳方案:3楼删除 m_pMainWnd = &dlg; 是最简单的,但考虑其它因素,为删除残留消息(WM_QUIT)

CMfc1Dlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
MessageBox(NULL,"OK pressed","info",MB_OK);
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
MessageBox(NULL, " ButtonCancel is Clicked", "Message:", MB_OK);
}

hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
我仔细研究了各位的发言,并进行了验证,总结如下:
解决方法两种:
1. 不设m_pMainWnd, 即保持 m_pMainWnd = NULL. 来弹出对话框
2. 保留m_pMainWnd = &dlg, 但用 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) 来清除主窗口中残留的消息。
我研究了一下,这个残留的消息正是WM_QUIT 消息。
此时如果调用MessageBox, 这个WM_QUIT 消息会被发往MessageBox, 而使得MessageBox立即退出。所以要想
使MessageBox弹出来,必须前清理掉这个消息。

至于说为什么主窗口消息会被发往不属于任何窗口的MessageBox (它的hWnd 是NULL), 那是因为消息是属于
线程的。如果没有窗口来消耗它,它会在各个窗口中传递。GetMessage会消耗消息, PeekMessage(...PM_REMOVE)也会消耗消息。

谢谢大家的讨论和帮助,结贴了!

M_S_D_N 2009-11-24
  • 打赏
  • 举报
回复
不建议建立额外的消息队列。
[Quote=引用 4 楼 ryanwen 的回复:]
原因就是主消息循环已经退出了,可以用如下方式实现

C/C++ code
m_pMainWnd=&dlg;
INT_PTR nResponse= dlg.DoModal();if (nResponse== IDOK)
{// TODO: Place code here to handle when the dialog is// dismissed with OK}elseif (nResponse== IDCANCEL)
{// TODO: Place code here to handle when the dialog is// dismissed with Cancel MSG msg;if (::PeekMessage(&msg, NULL,0,0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::MessageBox(NULL," ButtonOK is Clicked","Message:", MB_OK);
}
[/Quote]
hjjdebug 2009-11-24
  • 打赏
  • 举报
回复
回4楼:您给出了正确的解决办法,只加一句就可以了 "PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)"
回6楼:您似乎没有解释到关键。

15,980

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧