首页
社区
课程
招聘
[原创]MFC消息映射机制分析
发表于: 2011-8-21 18:00 7517

[原创]MFC消息映射机制分析

2011-8-21 18:00
7517

最简单的MFC程序,必须包含两个类CMyApp和CMainWindow类。

CMainWindow类从CFrameWind类派生。再往上为CWnd类,所有窗口类的父类,再往上为CCmdTarget类。

CMyApp类从CWinApp类派生。再往上是CWinThread类。在网上为CCmdTarget类。

windows是消息机制,MFC框架中需要将消息机制与消息响应函数对应起来。传统的windows程序是用callback和swich / case来实现响应不同的消息。
消息映射是要用宏定义的方式实现多态的效果,但不是多态,下面就要展开所有的与消息映射有关的宏。。如果在本类中没有查找到响应该消息的函数,就要去它的基类中寻找响应函数,是一个上行查询。

class CMyApp : public CWinApp
{
public:
    virtual BOOL InitInstance ();
};

class CMainWindow : public CFrameWnd
{
public:
    CMainWindow ();

protected:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnPaint ();
    DECLARE_MESSAGE_MAP ()            //消息映射声明部分宏
};

----------------------------------------

                宏定义

----------------------------------------

#ifdef _AFXDLL
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \

#else
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \

#endif

-----------------------------------------------

有两个选择,是根据动态链接或静态链接而产生的不同。

展开后第一个为一个结构体数组,结构体定义如下:

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 nSig;       // signature type (action) or pointer to message #
AFX_PMSG pfn;    // routine to call (or special value)
};

windows的消息在这可以看到

后面是另一个结构体数组,定义如下:

struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};

从结构体可以看出,第一个是指向基类消息映射的指针,第二个是指向当前类消息映射的指针。

接着是两个函数:

static const AFX_MSGMAP* PASCAL _GetBaseMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
返回值为AFX_MSGMAP结构体的指针。从字面上可以看出一个是获得消息映射,另一个是获得基类的消息映射。

BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT ()
END_MESSAGE_MAP ()

---------------------------------------------

此上为消息映射实现代码,在里面只响应两个消息,一个是WM_LBUTTONDOWN,另一个是WM_PAINT消息。

此处有两个宏,我们查看宏定义:

以下为BEGIN_MESSAGE_MAP宏定义:

#ifdef _AFXDLL
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \
  { return &baseClass::messageMap; } \
const AFX_MSGMAP* theClass::GetMessageMap() const \
  { return &theClass::messageMap; } \
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \

#else
#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[] = \
{ \

以下为END_MESSAGE_MAP宏定义:

#define END_MESSAGE_MAP() \
  {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \

我们可以看到第一个宏的最后一个参数,第二个宏两个各含有一个花括号,可以看出是一个AFX_MSGMAP_ENTRY结构的数组,而之间是我们的消息响应,查看消息响应的宏定义:

#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
  (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
  (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))&OnPaint },
此部分经整理后会是下面一个形式:

const AFX_MSGMAP* PASCAL CMainWindow::_GetBaseMessageMap()    //返回基类的消息映射
{
return &CFrameWnd::messageMap;
}

const AFX_MSGMAP* CMainWindow::GetMessageMap() const          //返回当前消息映射
{
return &CMainWindow::messageMap;
}

AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CMainWindow::messageMap =    //数组,参数是两个函数返回值
{
&CMainWindow::_GetBaseMessageMap, &CMainWindow::_messageEntries[0]
};

AFX_COMDAT const AFX_MSGMAP_ENTRY CMainWindow::_messageEntries[] =   //消息映射的数组
{
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
{ WM_PAINT, 0, 0, 0, AfxSig_vv, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))&OnPaint },
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
-------------------------------------------------------------------------------------------------

就是在此部分实现的消息映射,将系统消息与消息响应函数进行绑定。

消息传递过来,通过CMainWindow::GetMessageMap() 获得 CMainWindow::messageMap 数组,在这个数组中有这个类消息映射的入口,进入查找,如果没有,通过CMainWindow::_GetBaseMessageMap()返回的基类的messageMap数组,可以获得基类的消息映射入口,一直上行找到找到这个消息响应的地方。

在说一下MFC程序的WinMain,MFC程序的WinMain函数被隐藏掉了,我们可以进入调试模式,找到这个函数的call stack就能看到WinMain函数被调用的地方。我在本机上的VC++6.0在call stack显示的时候有些问题,不完全,下面有一部分是参考的资料,没有去编程环境的虚拟机中的VC++6.0进行测试。

WinMain调用AfxWinMain,程序开始运行。

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

AFXWinMain调用InitInstance()函数,之后调用一个CWinApp的Run()函数返回CWinThread::Run()函数,调用基类的Run()函数,在此进入消息循环。

调用InitInstance()函数时消息循环还未开始。消息循环卸载CWinThread中。

昨天弄这个的时候太晚了,比较乱,过几天时间够的话可能会写一个MFC框架的分析。
这是我的新浪blog地址,欢迎大家指教。
b12K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3u0D9L8$3N6Q4x3X3g2K6K9h3&6S2i4K6u0W2j5$3!0E0i4K6u0W2j5$3&6Q4x3V1k6V1K9h3&6Y4L8h3!0J5M7r3S2D9K9h3&6Y4


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 6
支持
分享
最新回复 (7)
雪    币: 53
活跃值: (56)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
写得很差劲么  - -  完全没有看的 - -
2011-8-22 19:35
0
雪    币: 128
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
鼓励一下, 现在貌似mfc没落了, 很多都搞C#去了
2011-8-23 09:00
0
雪    币: 111
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
visual studio 2010 sp1 更新了MFC库,增加动画和Direct2D的支持. MFC没落了吗?
2011-8-23 09:17
0
雪    币: 31
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
5
理解原理很重要,很喜欢一句话    “你要了解真相,真相会让你自由。”
2011-8-31 19:04
0
雪    币: 11810
活跃值: (3462)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
分析得很是到位细致,赞一个!
2011-8-31 21:15
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我还要努力学习哈。。
2011-9-1 10:59
0
雪    币: 232
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
呵呵,写得很不错,MFC没落倒不至于,不过它承载的东西太多太臃肿了,
可以尝试用用WTL之类的,
这让我想起来MFC深入浅出那本书了,呵呵
2011-9-4 23:17
0
游客
登录 | 注册 方可回帖
返回