MFC消息映射的原理

原文地址:http://www.cnblogs.com/lidabo/p/3694726.html

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表;两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵)。那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多。

 

嗯…怎么办呢?于是各大c++名库(比如QT,MFC,VCL…)在消息映射的实现方面,抛开了虚函数的方式,而用了第二种方法:查找名称表,其原理五花八门,各显神通,让我想到了春秋时代,各国诸侯置周天子不顾,挟天子令诸侯,各自为政的阶段,呵呵~

 

现在先说MFC的做法:MFC消息映射机制的原理,也就是MFC是怎么做到一个消息来了,就调用相应的成员函数的?(在VC编程里面用的是消息循环机制,那比较好理解,就是在消息处理程序里面来个大大的swicth…)

 

好了,在c++ stl成熟的现在,很多人都会想到用map来匹配(据我所知,有些公司很喜欢这样用,抛开了mfc的做法),但是当时stl没有流行,所以mfc设计者们就来个链表查找。

 

先看用法:

首先,要用消息处理的类,必须要继承自CcmdTarget类;

然后,在类的声明中有如下的宏:DECLARE_MESSAGE_MAP()和需要实现的消息映射

然后,在类的实现文件中,有如下宏:BEGIN_MESSAGE_MAP(…), … END_MESSAGE_MAP()

具体例子如下所示:

 

//头文件中:

class CMainFrame : public CFrameWnd

{

……

// 生成的消息映射函数

protected:

     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

     DECLARE_MESSAGE_MAP()

};

//实现文件中:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

     ON_WM_CREATE()

END_MESSAGE_MAP()

 

 

那些宏展开之后如下所示:

 

class CMainFrame : public CFrameWnd

{

……

// 生成的消息映射函数

protected:

     int OnCreate(LPCREATESTRUCT lpCreateStruct);

 

// 下三行为宏DECLARE_MESSAGE_MAP()的展开

protected:

     static const AFX_MSGMAP* PASCAL GetThisMessageMap();

     virtual const AFX_MSGMAP* GetMessageMap() const;

};

 

// 下10行为宏BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)的展开

const AFX_MSGMAP* CmainFrame::GetMessageMap() const

{

return GetThisMessageMap();

}

const AFX_MSGMAP* CMainFrame::GetThisMessageMap()

{

     typedef CmainFrame ThisClass;

     typedef CframeWnd TheBaseClass;

     static const AFX_MSGMAP_ENTRY _messageEntries[] =

     {

         // 下2行为宏ON_WM_CREATE()的展开

{ WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_PMSG) (AFX_PMSGW)(static_cast

< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) },

         // 下5行为宏END_MESSAGE_MAP()的展开

{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }

     };

static const AFX_MSGMAP messageMap = { &TheBaseClass::GetThisMessageMap, _messageEntries[0] };

     return &messageMap;

}

 

 

上面这些几乎全是数据结构的初始化代码,先不管这里面的结构放的是什么内容,先看类声明那个宏展开的两个函数究竟在mfc框架的那个地方使用?(先看过程,在看数据结构,这是面向对象分析的不二法门~)

 

下面,说一下别人总结的mfc流程中的消息分派,(参考文章会放在附录里面的,这只是笔记嘛,呵呵)

 

注意:我安装的是VS2005,MFC源码在安装的目录下面的  VC/atlmfc/src/mfc目录里面

 

1、   先假定mfc的消息入口点是CWnd::WindowProc函数(至于是如何流入的,请参考其他文章),其代码就好像vc编程里面那个WinMain函数的消息循环部分差不多:

 

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

    LRESULT lResult = 0;

 

    if (!OnWndMsg(message, wParam, lParam, &lResult))

 

        lResult = DefWindowProc(message, wParam, lParam);

 

    return lResult;

}

 

看,就是看一下该消息在OnWndWsg里是否找到对应的处理函数,如果没找到,用DefWindowProc处理;

 

2、   那么,这个OnWndWsg里面就是调用以上宏展开的那个函数来取得相关的消息映射滴~简化过后的代码如下:

 

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)

{

    LRESULT lResult = 0;

    const AFX_MSGMAP* pMessageMap;

 

     //取得消息映射结构,GetMessageMap为虚函数,所以实际取的是CmainFrame的消息映射

    pMessageMap = GetMessageMap();

 

    // 查找对应的消息处理函数

    for (pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)

        if (message < 0xC000)

            if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)

                goto LDispatch;

    ... ...

LDispatch:

    //通过联合来匹配正确的函数指针类型

    union MessageMapFunctions mmf;

mmf.pfn = lpEntry->pfn;

……

 

其中的pMessageMap = GetMessageMap();语句要留意,因为GetMessageMap是虚函数,在上面的CmainFrame类中已经重写了,所以,调用的实际上是CmainFrame的GetMessageMap;返回一个表格的指针pMessageMap,然后根据消息的类型在里面寻找,这个表格的数据结构如下:

struct AFX_MSGMAP

{

     const AFX_MSGMAP* (* pfnGetBaseMap)();

     const AFX_MSGMAP_ENTRY* lpEntries;

};

 

再看CmainFrame中的代码:

static const AFX_MSGMAP messageMap =    { &TheBaseClass::GetThisMessageMap, _messageEntries[0] };

就知道:这个表格其实是一个链表,放的是基类的GetMassageMap函数指针,和当前类的真正表格_messageEntries的入口地址。再看_messageEntries里面放的又是什么:

 

nMessage

nCode

nID

nLastID

nSig

nPfn

WM_CREATE

0

0

0

AfxSig_is

&CmainClass::OnCreate

0

0

0

0

AfxSig_end

0

 

好了,现在看到了WM_CREATE 就连着 &CmainClass::OnCreate,再结合上面CWnd::OnWndMsg的代码:

 

    for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)

        if (message < 0xC000)

            if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)

                goto LDispatch;

就可以推理得到,AfxFindMessageEntry就是在找message和_messageEntries.nMessage的相等,如果不等,就往基类里面找: pMessageMap = pMessageMap->pBaseMap

 

找到了就跳到Ldispatch,其中,有:mmf.pfn = lpEntry->nPfn;这个就是放着该消息处理函数的内容。

 

3、   总结:

其实那些宏的作用就是用来初始化那个链表messageMap 和当前类处理函数的表格_messageEntries,当框架代码运行到pMessageMap = GetMessageMap();时候,由于虚函数的作用,使得当面运行当前窗口CmainFrame的GetMassageMap,把CmainFrame的表格给pMessageMap,然后再从派生类往基类一层层找响应的处理函数。

 

这与c++虚函数机制相比,其好处是可以省空间,每个类里面只有自己重写的函数信息,其他信息在基类里面找;
不好的地方就是搜链表,从当前类往基类上搜,时间上可能久一点;

mfc消息分派的基本原理就是这样的,但是还有很多的细节没有深入,比如那些不同的成员函数有不同的参数,他们是怎么传递的,怎么在表格里面统一参数的信息...这些看下面的参考文献【1】,有较为详细的说明。

 

4、   补充:用的框架代码里面,最显著的是一种模式:模板方法模式(template method pattern)。具体

的说明请参考其他文章。

 

这里为了补充,举一个例子说明是怎么调用的:

class A
{
public:
    void callPrint(){ print(); };
protected:
    static void printThis(){ cout << "this is a A object!/n"; };
private:
    virtual void print(){ printThis(); };

};

class B : public A
{
protected:
    static void printThis(){ cout << "This is a B object!/n";}
private:
    void print(){ printThis();}
};

如下语句输出什么:

 

B  b;

A *pa = &b;

pa->callPrint();

 

也许你知道输出的是“this is a B object!/n”,但是,知道为什么嘛?参考文章【3】中有详细的讲解,会令你满意的。

(注意上面例子中的virtual和访问权限,试着利用虚函数的实现机制来解释他是怎么作用的。)

 

 

参考文章:

 

【1】、MFC消息分派:http://blog.csdn.net/linzhengqun/archive/2007/11/28/1905671.aspx

 

【2】、MFC教程之消息映射的实现:http://www.vczx.com/tutorial/mfc/mfc4.php

 

【3】、与大虾对话:领悟设计模式:http://www.myfaq.com.cn/A200508/2005-08-07/183608.html


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《Visual C++权威剖析--MFC原理、机制与开发实例》是一本介绍MFC(Microsoft Foundation Classes)的原理、机制以及开发实例的权威性书籍。MFC是微软公司用于开发Windows应用程序的一套类库,是在C++基础上实现的,为开发者提供了一些常用的类和函数,简化了Windows应用程序的开发过程。 这本书首先对MFC的基本原理进行了详细的介绍。它解释了MFC的框架结构,包括应用程序对象、窗口类和消息映射等重要组成部分。同时,它还深入讲解了MFC的事件处理机制、资源管理和界面设计等方面的内容,帮助读者全面理解MFC的内部机制。 除了原理的介绍,这本书还给出了一些实际的开发实例。通过这些实例,读者可以学习如何使用MFC来创建一个完整的Windows应用程序。这些实例包括了常见的功能,例如窗口创建、菜单设计、对话框布局以及文件操作等。通过实际的案例,读者可以加深对MFC的理解,并且在实践中提高自己的开发能力。 总体来说,《Visual C++权威剖析--MFC原理、机制与开发实例》是一本权威且实用的书籍,适合有一定C++基础并且对Windows应用程序开发感兴趣的读者。通过学习这本书,读者可以更好地掌握MFC原理和使用技巧,提高自己的软件开发能力。 ### 回答2: 《Visual C++权威剖析--MFC原理、机制与开发实例》是一本介绍MFC(Microsoft Foundation Classes)的书籍。MFC 是微软公司推出的一个非常重要的C++框架,用于在Windows操作系统上开发图形用户界面应用程序。 这本书首先解释了MFC的基本原理和机制。MFC建立在Windows API之上,它提供了一套面向对象的类库,用于简化Windows GUI应用程序的开发。MFC封装了很多常用的控件和功能,并且提供了很多易于使用的类和函数来处理用户输入、显示输出、消息处理、资源管理等。 书中还介绍了如何使用MFC开发实例。通过这些实例,读者可以学习如何创建一个基本的MFC应用程序,并了解如何利用界面设计器来设计用户界面。书中还提供了一些示例代码,展示了如何处理按钮点击事件、菜单点击事件、对话框等。通过这些实例,读者可以学习到MFC的具体用法和开发技巧。 此外,书中还深入探讨了MFC的高级特性和扩展机制。例如,它介绍了如何使用ActiveX 控件、数据库编程、多线程编程等。这些内容可以帮助读者更加深入地了解MFC并提高开发能力。 总的来说,《Visual C++权威剖析--MFC原理、机制与开发实例》是一本详细介绍MFC的权威书籍。它适合有一定C++基础,想要学习MFC开发的读者。通过阅读这本书,读者可以系统地学习MFC原理、机制和开发实例,从而提高Windows GUI应用程序的开发能力。 ### 回答3: 《Visual C++权威剖析--MFC原理、机制与开发实例》是一本面向Visual C++开发者的权威指南,全面介绍MFC(Microsoft Foundation Class)的原理、机制和开发实例。MFC是微软提供的一组C++类库,用于开发Windows应用程序。 该书首先介绍了MFC的发展历史以及其与Windows操作系统的关系。接着详细解析了MFC的体系结构和设计原理,包括C++面向对象编程、事件和消息处理、窗口和控件等基本概念。通过深入理解MFC原理和机制,读者可以更加高效地利用MFC进行应用程序开发。 本书还提供了大量的开发实例,涵盖了MFC的各个方面,包括界面设计、数据处理、线程管理等。每个实例都提供了详细的代码和演示步骤,读者可以根据实例来学习并实践MFC的开发技术。通过实例的学习,读者可以了解MFC在实际开发中的应用场景和解决问题的方法。 此外,该书还介绍了一些高级的MFC主题,如自定义控件、打印和预览、国际化等。这些主题涉及到一些常见但较为复杂的开发问题,通过学习这些内容可以进一步提升开发者的技术水平。 总之,《Visual C++权威剖析--MFC原理、机制与开发实例》是一本全面系统的MFC教程,适合有一定C++编程基础的开发者阅读。通过学习这本书,读者可以深入了解并熟练掌握MFC原理、机制和开发技术,提高自己在Windows应用程序开发中的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值