MFC消息的产生与传递

 首先定义一个名词:向上匹配。对于 除WM_COMMAND以外的消息,在消息网中都只能是从派生类流向基类。派生类及基类都有自己的消息映射表AFX_MSGMAP_ENTRY。消息映射表中存储了类所有重写的消息处理。当前给定了一个消息,为了执行该消息所对应的处理函数,需要在当前类中搜索消息映射表AFX_MSGMAP_ENTRY,若消息的ID存在该表中,那么说明当前类重写了该消息的处理方式,那么就调用相应的处理函数;若消息的ID不存在该表中,说明当前类没有重写该消息,于是就向上追溯,找到其基类,重复上述ID的比较,直到找到或者到达CCmdTarget为止。

这个过程从当前类中匹配,找不到就向上从基类匹配,依然找不到则继续向上……称之为向上匹配。注意这个过程只针对除WM_COMMAND以外的消息,即WM_xxx。所以WM_xxx消息只会从派生类流向基类,不会横流或逆流。

总之,WM_xxx消息只会纵向流动

 

WM_COMMAND消息则不一定。若一个CFrameWnd接收到消息,则消息会如下顺序传递:

CFrameWnd->CView->CDocumet->CDocumetTemplate

               ->CFrameWnd

               ->CWinApp

该顺序是固定的。如果一个CView或者一个CDcoumet接收到消息,消息同样是按照上面的顺序传递,只是起点从各自的类开始而已。

也就是说,WM_COMMAND消息的传递顺序是:先View,后Dcoument,再CWnd(实际上是CCmdTarget),最后CWinApp

 

现在,CWinThread::Run产生了一个消息。该消息首先会被发送给AfxWndProc

①    AfxWndProc是个全局函数,也是所有消息的推动引擎起点。AfxWndProc会在CWinThread::Run中被调用,也就是说,CWinThread::Run产生消息,并将消息发送给AfxWndProc。该函数的作用是将消息传递给AfxCallWndProc函数。

②    AfxCallWndProc是个全局函数,该函数的作用是将传来的消息发送给当前类的WindowProc函数处理。由于WindowProc是个虚函数,所以每个类调用的WindowProc是不一定相同的。

③    WindowProc接收到消息后,会判断该消息是否为WM_COMMAND消息。若不是,那么只要从当前类开始,向上匹配找到消息处理函数即可;若是WM_COMMAND消息,那么就从CCmdTarget的所有派生类中来找对应的实现函数OnCommand,寻找过程见④。若最终找到了,就执行相应的处理函数;若最终没有找到,就执行默认的DefWindowProc

④    从当前类开始,判断OnCommand函数的重写情况。一般在重写函数的末尾,都会调用其基类的OnCommand函数。于是这样不停上溯,当上溯到该类的主要基类时,比如CFrameWnd,会在该基类的OnCommand函数中调用虚函数OnCmdMsg

⑤    OnCmdMsg函数是个虚函数,于是根据当前的类对象,会调用不同的OnCmdMsg函数。在这个OnCmdMsg函数里,会获取其他主要基类(见过程⑥)并对这些基类的OnCmdMsg函数加以匹配调用。每个基类最终都会回溯到CCmdTarget中的OnCmdMsg函数,在这里面会调用GetMessageMap来获取所有命令,并对命令的ID进行向上匹配。但GetMessageMap是个虚函数,所以获取的命令也是当前类的消息映射表。若第一个基类匹配失败,就继续匹配第二个基类。直到匹配到合适的命令ID,或者匹配失败,回到步骤③中调用默认的DefWindowProc

⑥    获取其他主要基类的顺序是:

CFrameWnd->CView->CDocumet->CDocumetTemplate

                     ->CFrameWnd

                     ->CWinApp

  

消息发送给CFrameWndOnCmdMsg()函数开始处理消息:

CFrameWnd::OnCmdMsg()
{
	//1
    if(pView->OnCmdMsg())
    {
        return TRUE;
    }
    //2
    if(CWnd::OnCmdMsg())
    {
		return TRUE;
    }
    //3
    if(pApp->OnCmdMsg())
    {
        return TRUE;
    }
}

由上可知CFrameWnd::OnCmdMsg()函数内有三个分支,分别用123表示。

首先消息进入分支1注意此时主体是pView

CView::OnCmdMsg()
{
	//①
	if(CWnd::OnCmdMsg())
	{
		return TRUE;
	}
	
	//②
	m_pDocumet->OnCmdMsg();
}

进入分支1,又产生了两个新的分支,分别用①,②表示。

然后消息进入分支

由于是CWnd::OnCmdMsg(),但CWnd并没有改写基类CCmdTarget的函数OnCmdMsg(),所以实际上分支进入的是CCmdTarget::OnCmdMsg():

CCmdTarget::OnCmdMsg()
{

       向上匹配

}

CCmdTarget::OnCmdMsg()中,开始对消息进行向上匹配。注意此时主体是pView。若匹配成功,则调用相应处理函数,并返回TRUE。从而消息传递终止。

若匹配不成功,则消息继续传递,进入分支

CDocumet::OnCmdMsg()
{
	//③
	if(CCmdTarget::OnCmdMsg())
	{
		return TRUE;
	}
	
	//④
	m_pDocTemplate->OnCmdMsg();
}

此时,主体变成了m_pDocumet。在分支中,同样出现了两个分支③,④

先进入分支。同样是进入到CCmdTarget::OnCmdMsg()中开始向上匹配,注意此时的主体是m_pDocumet

若依然没有匹配成功,则进入分支主体变成m_pDocTemplate,并进一步进入到CCmdTarget::OnCmdMsg()中开始向上匹配。

若依然没有匹配成功,则一路返回,返回到最初的分支1,第一个if语句判断失败。进入到第二个if语句,即分支2。此时主体变成了CFrameWnd。然后进入CWnd::OnCmdMsg()。

//2
if(CWnd::OnCmdMsg())
{
 return TRUE;
 }

CWnd并没有改写基类CCmdTarget的函数OnCmdMsg(),所以实际上进入CCmdTarget::OnCmdMsg(),开始向上匹配。

若依然没有匹配成功,则一路返回,返回到最初的分支2,第二个if语句判断失败。进入到第三个if语句,即分支3。此时主体变成了pApp。然后进入pApp->OnCmdMsg()。

//3
if(pApp->OnCmdMsg())
{
	return TRUE;
}

CWinApp并没有改写基类CCmdTarget的函数OnCmdMsg()(注意中间跳过了CWinThread),所以实际上进入CCmdTarget::OnCmdMsg(),开始向上匹配。

若匹配成功,则调用相应的处理函数;

若匹配失败,则说明没有该命令消息的处理函数。返回FALSE

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值