MFC消息映射笔记

大家有没有思考过当一个消息出现,应用程序框架是如何将消息与对象建立关系的?

1.消息宏

为了支持消息映射,MFC提供了3种宏。

1.1消息映射的声明和分解宏

消息映射的声明和分界宏包含在CCmdTarget类中,如下表:
这里写图片描述
就是这3个宏组织了一张庞大的消息映射网络。所有继承与CCmdTarget类的派生类均具有这种特性。下面分别说说这个宏


1)DECALRE_MESSAGE_MAP宏

这个宏的定义在AfxWin.h文件中定义,源码如下:


#define DECLARE_MESSAGE_MAP() \
protected: \
    static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
    virtual const AFX_MSGMAP* GetMessageMap() const; \

#define BEGIN_TEMPLATE_MESSAGE_MAP(theClass, type_name, baseClass)          \
    PTM_WARNING_DISABLE                                                     \
    template < typename type_name >                                         \
    const AFX_MSGMAP* theClass< type_name >::GetMessageMap() const          \
        { return GetThisMessageMap(); }                                     \
    template < typename type_name >                                         \
    const AFX_MSGMAP* PASCAL theClass< type_name >::GetThisMessageMap()     \
    {                                                                       \
        typedef theClass< type_name > ThisClass;                            \
        typedef baseClass TheBaseClass;                                     \
        static const AFX_MSGMAP_ENTRY _messageEntries[] =                   \
        {

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
    PTM_WARNING_DISABLE \
    const AFX_MSGMAP* theClass::GetMessageMap() const \
        { return GetThisMessageMap(); } \
    const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
    { \
        typedef theClass ThisClass;                        \
        typedef baseClass TheBaseClass;                    \
        static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
        {

#define END_MESSAGE_MAP() \
        {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
    }; \
        static const AFX_MSGMAP messageMap = \
        { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
        return &messageMap; \
    }                                 \
    PTM_WARNING_RESTORE

可以看出,DECLARE_MESSAGE_MAP宏主要定义了一个长度不定的静态数组变量_messageEntries[]、一个静态变量messageMap和一个虚拟函数GetMessageMap。静态数组_messageEntries[]定义了一张消息映射表,表中每一项指定了类或对象的消息和处理此消息的函数之间的对应关系,代表一条映射,可以用AFX_MSGMAP_ENTRY结构描述。
我们看到了一个陌生的类型 AFX_MSGMAP ,查看其定义:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};

这个结构体第一个成员是一个函数指针,第二个成员类型为 AFX_MSGMAP_ENTRY* ,查看AFX_MSGMAP_ENTRY 定义:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};

从上述结构可以看出,每条映射有两部分内容:前4个成员是关于消息ID的,后两个成员是关于消息对应的执行函数的。其中,nSig成员用来标志不同原型的消息函数,MFC根据nSig的不同把消息派发给对应的函数进行处理。而pfn成员则是一个指向CCmdTarget成员函数的指针,函数指针的类型定义如下:typedef void (AFX_MSG_CALL CCmdTarget::AFX_PMSG)(void)
当初始化消息映射表时,各种类型的消息函数都被转换成相同的类型,而实际执行时,根据nSig把pfn还原成相应类型的消息处理函数,并执行它。
另一个AFX_MSGMAP类型成员变量messageMap,是一个包含消息映射的变量,把消息映射的信息和相关函数打包在一起,其结构定义如下:

struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//指向_GetBaseMessageMap函数
const AFX_MSGMAP_ENTRY* lpEntries;//保存基类消息映射入口_messageEntries的地址。
};

可见,AFX_MSGMAP实际上定义了一单项链表,链表中的每一项是一个指向消息映射表的指针。其主要作用是获取基类和本身的消息映射入口地址。于是我们知道MFC首先把所有消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有消息和他们相关的参数,在通过AFX_MSGMAP获得该数组的首地址及基类的消息映射入口地址。当本身对该消息不响应的时候,就调用其基类的消息响应,因此,基类的消息处理函数就是派生类的默认消息处理函数。


2).BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏

这两个宏的定义也在AfxWin.h文件中,代码如下:

#define BEGIN_MESSAGE_MAP(theClass, baseClass) /
      const AFX_MSGMAP* theClass::GetMessageMap() const /
            { return &theClass::messageMap; } /
      AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = /
      { &baseClass::messageMap, &theClass::_messageEntries[0] }; /
      AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = /
      { /
实际应用BEGIN_MESSAGE_MAP(myview,CView)等价于
      const AFX_MSGMAP* myview::GetMessageMap() const /
            { return & myview::messageMap; } /
      AFX_COMDAT AFX_DATADEF const AFX_MSGMAP myview::messageMap = /
      { & CView::messageMap, & myview::_messageEntries[0] }; /
      AFX_COMDAT const AFX_MSGMAP_ENTRY myview::_messageEntries[] = /
      { /
END_MESSAGE_MAP和BEGIN_MESSAGE_MAP是成对出现的
#define END_MESSAGE_MAP() /
            {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } /
      }; /

可以看出,DECLARE_MESSAGE_MAP宏在其类中申请了一个全局结构和获得该结构的函数,而在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间填写刚才的全局结构,将消息和对应的函数联系起来,并通过AFX_MSGMAP中的pBaseMap指针,将各类按继承顺序连接起来,从而提供消息流动的道路。

2.消息映射宏

除了上面的3个宏外,常用的宏还有:
这里写图片描述
最常用的ON_COMMAND宏定义如下:

#define ON_CONMMAND(id,memberFxn)
{
      WM_COMMAND,CN_COMMAND,(WORD)id,(WORD)id,AfxSig_vv,
      (AFX_PMSG)&OnFileNew}

根据上面的定义,ON_COMMAND(ID_FILE_NEW,OnFileNew),将被预编译器展开为如下代码:

{WM_COMMAND,CN_COMMAND,(word)ID_FILE_NEW,(WORD)ID_FILE_NEW,AfxSig_vv,(AFX_PMSG)&OnFileNew},

虽然消息映射宏很重要,但是通常不需要用户直接使用它们。当使用ClassWizzard把消息处理函数与消息关联在一起的时候,他会将源文件中自动创建消息映射入口,所以就算读者没有理解上面的东西,会使用ClassWizard消息就可以。

3.消息路由传动

大部分消息流动是在用户与应用程序之间进行的,一个消息一般针对一类类型对象。MFC中CWinApp类的Run函数负责把消息从应用程序的消息队列中取出,发送到应用程序窗口函数WinProc中。该函数根据消息的类别,再传送到响应的对象中。
任何派生于CCmdTarget类的对象都能接受命令消息。这些类对象组成一个有序链表,链表中的每一个
对象都可以同时接收到命令消息,命令消息按照一定的路径传送。链表中各个对象处理消息的优先级和顺序并不相同。下面以实例分析一下:
建一个基于MFC的单文档应用程序,运行后,点击帮助菜单:
这里写图片描述
会弹出一个对话框,我们简略分析一下消息映射,首先将视图切换到ClassView,双击MFC_DISCOVER.h文件,有如下代码:

class CMFC_DISCOVERApp : public CWinApp
{
public:
    CMFC_DISCOVERApp();


// 重写
public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();

// 实现
    afx_msg void OnAppAbout();  //关于对话框
    DECLARE_MESSAGE_MAP()    //消息宏映射申明
};

extern CMFC_DISCOVERApp theApp;

宏DECLARE_MESSAGE_MAP声明了消息映射,并初始化消息映射表。
再打开MFC_DISCOVER类的实现文件MFC_DISCOVER.cpp,观察下面的代码:

BEGIN_MESSAGE_MAP(CMFC_DISCOVERApp, CWinApp)
    ON_COMMAND(ID_APP_ABOUT, &CMFC_DISCOVERApp::OnAppAbout)
    // 基于文件的标准文档命令
    ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
    ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
    // 标准打印设置命令
    ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
``
当windows接收到ID_APP_ABOUT消息时,通过查找消息映射表找到相应的处理函数OnAppAbout来相应消息:

// 用于运行对话框的应用程序命令
void CMFC_DISCOVERApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}

`
你若将
ON_COMMAND(ID_APP_ABOUT, &CMFC_DISCOVERApp::OnAppAbout)`注释,则无法出现关于对话框了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Classes)是一组用于开发 Windows 应用程序的 C++ 类库。在 MFC 消息映射是一种机制,用于将 Windows 消息与相应的消息处理函数关联起来消息映射是通过在类的消息映射定义消息处理函数来实现的。消息映射表是一个由宏定义和消息映射项组成的静态数组。每个消息映射项指定了一个 Windows 消息的 ID 和相应的消息处理函数。 消息处理函数是类的成员函数,用于处理特定的 Windows 消息。当一个窗口接收到一个消息时,MFC 会根据消息映射的定义找到对应的消息处理函数,并执行该函数来处理该消息。 下面是一个简单的示例,展示了如何使用消息映射消息处理函数: ```cpp // 声明消息映射表 BEGIN_MESSAGE_MAP(CMyWnd, CWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() // 定义消息处理函数 void CMyWnd::OnPaint() { // 处理 WM_PAINT 消息 // ... } void CMyWnd::OnLButtonDown(UINT nFlags, CPoint point) { // 处理 WM_LBUTTONDOWN 消息 // ... } ``` 在上面的示例,`CMyWnd` 是一个继承自 `CWnd` 的自定义窗口类。通过在消息映射使用 `ON_WM_PAINT` 宏和 `ON_WM_LBUTTONDOWN` 宏,将 `OnPaint` 函数和 `OnLButtonDown` 函数与 `WM_PAINT` 消息和 `WM_LBUTTONDOWN` 消息关联起来。 当窗口接收到相应的消息时,就会调用对应的消息处理函数进行处理。 需要注意的是,消息映射消息处理函数必须是类的成员函数,并且符合特定的函数签名。 希望这个简单的解释能够帮助你理解 MFC 消息映射消息处理机制。如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值