Thinking in MFC---消息机制2(消息映射)

Thinking in MFC---消息机制2

引言

在这一篇,我们会介绍一下在窗口类中,如何将消息路由到消息处理函数中去。


一、AFX_MSGMAP_ENTRY

在开始之前,我们有必要了解这个结构体。

struct AFX_MSGMAP_ENTRY
{
    UINTnMessage;   //windows message
    UINTnCode;      //control code or WM_NOTIFY code
    UINTnID;        //control ID (or 0 for windows messages)
    UINTnLastID;    //used for entries specifying a range of control id's
    UINT_PTRnSig;       //signature type (action) or pointer to message #
    AFX_PMSGpfn;    //routine to call (or special value)
};

其中pfn便是消息处理函数指针。

对于这个结构体中的其他成员变量,从它的命名和注释应该可以了解大概。接下来就看一个相关的宏。


二、GetMessageMap

这个函数就如它的命名,获得消息映射。

我们在使用MFC ClassWizard添加消息响应函数的时候,会自动在我们的窗口类中添加几行代码。

DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(yourclass, baseclass)
END_MESSAGE_MAP()

让我们一个一个看上面三个宏。

第一个DECLARE_MESSAGE_MAP(),从它的命名上就能明白它的作用,它是申明MessageMap。我们去它的定义处看看。

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

这里申明了两个函数。至于这两个函数是做什么,接下来就看它们的定义。就要看接下来两个宏。

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

上面的第一个函数GetMessageMap,其实在调用GetThisMessageMap,所以我们重点就看第二个函数。

GetThisMessageMap其实就是定义了 AFX_MSGMAP_ENTRY结构体数组,而这个结构体就是我们第一节中的消息结构体。

接下来看一下

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

上面这个宏主要是对GetThisMessageMap的结束处理。

从BEGIN_MESSAGE_MAP和END_MESSAGE_MAP这两个宏来看,GetThisMessageMap便是返回该类的消息映射结构体的数组。

接下来我们就要看MFC是怎么使用这个消息映射结构体的。

这里还有两个宏PTM_WARNING_DISABLE和PTM_WARNING_RESTORE,这两个宏作用就是在定义上面两个函数的时候,不会产生warning。

它们两个的源码是这样的

#define PTM_WARNING_DISABLE \
    __pragma(warning( push )) \
    __pragma(warning( disable : 4867 ))
#define PTM_WARNING_RESTORE \
    __pragma(warning( pop ))

有兴趣的读者可以看一下关于warning的一些知识,这个不是我们这里的重点,所以就不做介绍了。


接下来看一下这个宏

ON_WM_PAINT()

这个宏你在class Wizard中添加WM_PAINT事件之后,便会自动添加在

BEGIN_MESSAGE_MAP(yourclass, baseclass)
ON_WM_PAINT()
END_MESSAGE_MAP()

让我们转向ON_WM_PAINT()这个宏定义处看看

#define ON_WM_PAINT() \
    { WM_PAINT, 0, 0, 0, AfxSig_vv, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast<void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
我们联系之前所讲的BEGIN_MESSAGE_MAP中的代码,其实这个ON_WM_PAINT()为messageEntries赋值,这里是一个结构体AFX_MSGMAP_ENTRY。你可以看到第一个参数是WM_PAINT便是nMessage,最后重要的是消息最终路由到那个函数处理,ThisClass :: OnPaint便是处理函数。

类似ON_WM_PAINT()其它宏都是注册了消息处理事件,告诉MFC什么消息交给什么函数处理。


三、窗口消息路由

至于怎么路由,笔者在网上大致找到了一下这么一段代码,说明这段代码只是用来介绍,并非MFC的源码。

BOOLCWnd::OnWndMsg(……)//
{
       if(message == WM_COMMAND)
              OnCommand(wParam,lParam);
       if(message == WM_NOTIFY)
              OnNotify(wParam,lParam,&lResult);
       pMessage = GetMessageMap();
       for(; pMessageMap!=NULL; pMessageMap =pMessageMap->pBaseMap)
       {
              if((lpEntry=AfxFindMessageEntry(pMessageMap->lpEntries,
                     message,0,0))!=NULL)
                     break;
       }
       (this->*(lpEntry->pnf))(……);//调用消息响应函数
}
AFX_MSGMAP_ENTRYAfxFindMessageEntry(……)
{
       while(lpEntry->nSign!=AfxSig_end)
       {
              if(lpEntry->nMessage==nMsg&&lpEntry->nCode==nCode&&nID>=lpEntry->nID
                            &&nID<=lpEntry->nLastID)
              {
                     return lpEntry;
              }
              lpEntry++;
       }
}

在上面的代码中可以找到GetMessageMap的声影,它返回一个消息映射并存放在pMessage变量中。

接下来进行遍历,找到符合要求的消息映射,找到之后使用存放在其中的消息处理函数来调用消息处理函数。

上面提到的OnCommand和OnNotify,笔者这里不做介绍,留在下一篇介绍。


四、总结

开始编程不久,便接触到了MFC。用了MFC写了自己第一个视窗程序,写了第一个游戏,在学校课题演示的时候也是喜欢用MFC做演示。

现在工作了,项目的界面部分也在使用MFC开发的,值得庆幸的是,现在微软对于MFC的界面做了很大的改善,想起刚开始不断重写控件类,那个真是记忆犹新。

在平时编程的时候,VS这个集成工具确实很方便。但现在想把一些MFC零零碎碎的知识整合起来。所以这个系列可能会深入地去介绍MFC。


 

发布了38 篇原创文章 · 获赞 81 · 访问量 18万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章