MFC模态模式对话框,MessageBox消息循环原理,定时器TIMER消息响应处理函数重入

2020.10

 

一、MessageBox和定时器TIMER

MessageBox是Win32 API全局函数,必须指定标题和样式。共有4个参数。没有父窗口就NULL。返回值是int,看选什么按钮。

比如:MessageBox( NULL, "选什么?", "标题", MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);

 

::表示全局函数。

在MFC中,可使用全局API函数::MessageBox(NULL,………),子程序会被暂时中断在这个MessageBox上!其后的代码无法运行,等到MessageBox返回后会继续运行代码。但父窗口可以被点出来,其它程序段可以运行,比如其它按钮。  因为NULL没有指定父窗口,所以不会使父窗口无效!

MFC中直接使用MessageBox,则是使用CWnd类的MessageBox方法,这时父窗口无法被点出来,等同于::MessageBox(m_hWnd,………)。同时子程序被暂时中断在这个MessageBox上。但关了其中一个消息框,则父窗就可以点出来了。 原理应该是m_hWnd 指定了父窗,MessageBox使父窗口无效,然后进入自己的消息循环,关闭时,使父窗有效。

 

在TIMER的响应函数中,有MessageBox的话,则会暂时中断在这个MessageBox上,但当下个时间到时,又会出现一个新的MessageBox,且也是暂时中断在这个MessageBox上...  即出现了多个消息响应函数的例程!!多次重入!!每个例程都中断在MessageBox上,MessageBox返回后,例程会继续执行到结束!  各个消息框都是父窗的子窗,所以是平等的,所以各个消息框都可以点出来,但父窗点不出来。  可以关闭任何一个消息框,但是这个重入例程MessageBox之后的代码不会运行,要按倒序关闭其后的消息框后才会运行。

 

二、消息框原理分析:参考《深入探讨MFC消息循环和消息泵》和调试时断点看代码

 

注意:

MFC是单线程运行的。

MFC内部是有消息循环。

消息循环的DispatchMessage()会阻塞,需要等窗口消息处理函数处理完了以后,才会返回。

窗口消息处理函数是由操作系统调用的。

窗口消息处理函数是可以多次重入的!类似函数自己可以嵌套一样。

单线程运作:阻塞→重入→阻塞→重入。。。

 

时间到→操作系统放WM_TIMER到线程队列→MFC消息循环DispatchMessage()阻塞→系统调用窗口消息处理函数OnTimer()处理完→消息循环继续

 

操作系统一到时间就会放WM_TIMER到线程队列。因为是单线程,如果窗口消息处理函数如果一直没处理完,则WM_TIMER会堆积在队列中。

 

OnTimer()中有消息框时情况就复杂了。MessageBox内部是一个模态对话框,模态对话框有消息循环。

 

时间1到,系统放WM_TIMER到队列

MFC程序主消息循环DispatchMessage()阻塞

操作系统调用第1个OnTimer()

MessageBox生成一个模态对话框对象1,显示出来,运行对话框内的消息循环1

时间2到,系统放WM_TIMER到队列

MessageBox1的消息循环1 DispatchMessage()阻塞

操作系统调用第2个OnTimer() (函数重入了!)

MessageBox生成一个模态对话框对象2,显示出来,运行对话框内的消息循环2

时间3到,MessageBox2的消息循环2 DispatchMessage()阻塞,操作系统调用第3个OnTimer() (函数重入了!)。。。

 

即一个串一个,只有最后一个消息框的消息循环在起作用,前面的消息循环全部阻塞了。

 

OnTimer()是主窗(父窗)函数,所以各个MessageBox都是子窗,各个子窗是都可以点出来,但父窗会被子窗失效掉,所以父窗点不出来。

 

当任何一个子窗关掉后,会使父窗有效,这时父窗可以点出来了。

 

三、消息框对话框关闭时原理

关闭时是怎样?为什么关闭时是按倒序在执行代码?

 

MessageBox无法调试进入内部看代码,就用模态对话框来代替吧:

CAboutDlg dlgAbout;

dlgAbout.DoModal();  // DoModal就会生成模态对话框

CDialog::DoModal()里面有CreateRunDlgIndirect()。

CWnd::CreateRunDlgIndirect里面有RunModalLoop()。

CWnd::RunModalLoop里面有消息循环AfxPumpMessage(),这里一直进行消息循环!循环中还有CWnd::ContinueModal()判断m_nFlags标志看是否需要退出循环。

 

当关闭对话框时,如果点“确定”就是触发CAboutDlg::OnOK(),点“X”就是触发CAboutDlg::OnCancel()。 这两个函数进去都是运行EndDialog()。

CDialog::EndDialog里面先运行EndModalLoop(),然后是运行::EndDialog()。

CWnd::EndModalLoop()里面有

m_nFlags &= ~WF_CONTINUEMODAL;  把标志设置成不再继续运行对话框

PostMessage(WM_NULL);   发送一个空消息以确保消息队列中有消息,这样消息循环不会因为没有消息而阻塞

::EndDialog()无法进入看代码,功能是把对话框关了不显示,然后把父窗激活。

把对话框关了不显示,这样我们就点不到这个对话框,就不会再触发窗口消息处理函数了。

 

所以清楚了:

点击关闭其中一个消息框时(比如消息框1),生成了一个消息(这个消息是关联这个消息框1的),“最后一个”消息框(比如消息框3)的消息循环DispatchMessage()这个消息后,操作系统重入消息框1对象的OnOK()或OnCancel(),设置m_nFlags标志成不再继续运行,发送一个消息框1的消息WM_NULL,以及用::EndDialog()把消息框1销毁了并把父窗激活。  

这个时候,这个消息框1对象仍存在,它的消息循环仍阻塞在DispatchMessage()中,只有等消息框3、消息框2的消息循环退出、OnTimer()退出后,才会从DispatchMessage()返回,然后发现m_nFlags不需要继续运行消息框了,于是退出消息循环,然后退出消息框对象,然后退出OnTimer(),消息循环就又交还给MFC程序的主循环了。

 

四、非模式对话框

非模对话框没有自己的消息循环,也不会使父窗失效。

用Create()来显示非模对话框,函数会立刻返回,所以必须要用new,这样对话框对象就保留在内存中。

如果用CAboutDlg dlgAbout;,则处理函数结束后,对象也被销毁,即对话框一显示就被关掉了。

CAboutDlg* pdlg = new CAboutDlg;

pdlg ->Create(IDD_ABOUTBOX);     //非模式对话框,函数立刻返回,无自己的消息循环

pdlg->ShowWindow(SW_SHOW);

 

 

所以,关键是消息循环、消息处理函数重入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值