MFC消息机制 2
接上篇
1、 消息的处理流程
好了,有了前面1、2小节的基础,我们接下来来看消息的处理流程:
以下描述消息从AfxWndProc起点开始的处理流程:
1) AfxWndProc(HWND hWnd,Unit nMsg,….)
说明,这里的hWnd指定了到底从哪个窗口开始接收并处理消息;
在AfxWndProc中,调用了AfxCallWndProc函数,
pWnd已经不是句柄了,而是由句柄得到的一个具体的CWnd指针指向的窗口类CWnd* pWnd=CWnd::FormatHandlePermant(hWnd)
2) AfxCallWndProc(CWnd *pWnd,Unit nMsg,…..)
说明:,在AfxCallWndProc函数中,调用了pWnd->WindowProc (nMsg,…….)
其实至此,消息走到了具体的MFC管理范围之内,以后消息就交给MFC类来处理了!
这里需要注意的是,其实pWnd->WindowProc,并不一定调用的就是CWnd的WindowProc函数,因为WindowProc是个虚函数,实际中,要看从一开始,即AfxWndProc开始,hWnd指的是什么窗口界面,当然这个窗口类肯定继承自CWnd,所以才可以用CWnd基类指针嘛!实际中,CcontrolBar、COleControl、ColePropertyPage、CDialog、等等都重载了WindowProc,而且CFrameWnd、Cview都继承自CWnd。这里其实隐含了一个道理,即从AfxWndProc接收的消息,入口都是[微软用户1] CWnd的继承类,可以是CDialog,CView(以及继承类),CframeWnd等等。
问题,AfxWndProc的起点可以是非CWnd子类吗?比如Cdocument?能否向一个Cdocument SendMessage? 好了先不说这个,继续往下走。。。。
3) CWnd::WindowProc()
在这个函数中,又调用了
OnWndMsg()
以及OnWndMsg处理失败后的DefWindowProc
先不管DefWindowProc,先来看看OnWndMsg(),当然具体调用哪个类的OnWndMsg,要看当前的CWnd子类是什么,是个Cview?CframeWnd?Cdialog?
CWnd的OnWndMsg函数是个虚函数,许多子类都重载了OnWndMsg(),在“深入浅出MFC”中,只分析了CWnd的OnWndMsg[微软用户2]
好了继续往下走,看看CWnd的OnWndMsg是怎么处理消息的:
l 对于WM_Command消息,调用 OnCommand
l 对于WM_Notify消息,调用OnNotify
l 对于除此之外的一般消息,就依据消息映射表上溯了
接下来我们分析对WM_Command消息的处理
4) OnCommand
OnCommand是CWnd的一个虚函数[微软用户3] ,实际中要看This指针指得是CWnd的哪个派生类。这些类都重载了OnCommand,有CWnd、CframeWnd、CMDIFrameWnd、CspliteFrameWnd等等。
实际中,要看消息是从哪个类中进来的,比如消息是从CframeWnd进来的,那么就调用CframeWnd的OnCommand,
而CframeWnd::OnCommand[微软用户4] (…)
{ ……
CWnd::OnCommand()
}
而CWnd::OnCommand()
{…..
OnCmdMsg()
}
注意,这时候消息是从CframeWnd进来的,那么实际调用的就是CframeWnd::OnCmdMsg()
实际中,许多类都重写了OnCmdMsg函数,有如下:
CframeWnd::CWnd
CMDIFrameWnd::CframeWnd
Cview::CWnd
Cdialog::CWnd
那么,消息从哪里进来,就调用谁的OnCmdMsg,比如从Cview进来,自然调用Cview->OnCmdMsg
下面来看看CframeWnd::OnCmdMsg()
5) CframeWnd::OnCmdMsg()
{
(注意,这里出现了多路分支)
1路、 Cview::pView->OnCmdMsg;
2路、 CWnd->OnCmdMsg
3路、 pApp->OnCmdMsg
}
6) 先说1路
Cview::OnCmdMsg
{
1、 CWnd::OnCmdMsg A处
2、 m_pDocument->OnCmdMsg
}
1中,由于CWnd没有重写OnCmdMsg,所以实际调用的是CcmdTarget::OnCmdMsg,那么CcmdTarget::OnCmdMsg做什么工作呢?它的工作就是对比消息映射表,看看能不能拦截该消息。
(这里千万不要犯迷糊,不要被CcmdTarget迷惑,因为是从A处进来的,那么其实对比的是1路 pView的消息映射表)
如果Cview处理不了该消息,那就只好由2处的m_pDocument进行处理了,即
先View然后文档
7) 如果1路处理不了,则交给2路处理
按照“深入浅出MFC”的说法, 2路实际上是判断当前框架的消息映射表,能不能处理当前消息,即沿着CMyFrameWnd->CFrameWnd->CWnd的路径来寻找消息映射匹配,刚开始我还不明白,在2路处,明明是CWnd->OnCmdMsg,怎么会先从CmyFrameWnd开始呢?往前想想,我们分析的这条消息是从哪里进来的?CMyFrameWnd,this指针指向CMyFrameWnd,当然要从CmyFrameWnd开始了,CmyFrameWnd只是借用了CcmdTarget的代码而已
8) 好,如果1路、2路、都处理不了,最后只能寄希望于3路了,即pApp->OnCmdMsg
如果3路也处理不了,那只好go back。。go back。。回到3)的DefWindowProc了
好了 我们完整分析了消息从CmyFrameWnd进来的处理流程,这个处理是参考”深入浅出MFC”中的,书中有一个图9-6,从大局上描述了这个流程。MFC消息处理确实很复杂,需要很多遍才稍微感觉有点儿明白了的。其实,这里面最关键的就是多态!多态!一定不要被代码里面写的类名迷糊,要时刻想想当前代码段儿的This指针指得对象是谁?消息是从哪里进来的?
还有几个问题不知道对不对,或者不理解,如下:
1、“深入浅出MFC”只列举了一个命令消息,从CframeWnd进来的处理流程,那么消息从其它地方进来的流程是什么呢?比如从Cview、Cdialog进来呢?
估计只是从CframeWnd进来的消息处理流程的一段儿,所以候捷只列举了一个,自己可以跟踪验证一下[微软用户6] 。Cview传入的消息,能不能被CframeWnd或者CmyApp处理呢?
2、“深入浅出MFC”只列举了命令消息(WM_Command)的处理流程,那么WM_Notify和其它一般消息的处理流程是什么呢?
[微软用户1]这里,“深入浅出MFC”缺少一个总结,我的理解不知道对不对
[微软用户2]这里“深入浅出MFC”只分析了CWnd的OnWndMsg,其实CVew、等其它CWnd的子类都重载了OnWndMsg,它们有什么不同,自己需要分析一下
[微软用户3]是CcmdTarget的虚函数,还是CWnd的虚函数?
[微软用户4]其它几个重载了OnCommand的函数,是否也都是调用了CWnd::OnCommand呢?
[微软用户5]看来,OnCmdMsg是CcmdTarget的虚函数
[微软用户6]验证消息从Cview、Cdialog进来的流程