几个重要的结构体:

struct AFX_MSGMAP
{
AFX_MSGMAP* pBaseMessageMap;
AFX_MSGMAP_ENTRY* lpEntries;
}

 

struct AFX_MSGMAP_ENTRY
{
UINT nMessage;// Windows消息
UINT nCode;// 控制消息的通知码
UINT nID;// 其控制组件的ID
UINT nLastID;// 如果是一定范围的消息映射,此值表示区间的最大值
UINT nSig; // 消息的动作标识
AFX_MSG pfn;// 消息响应函数
}

此结构体的数据由任何一个ON_宏都会把这六个数据初始化,例如 此处的ON_WM_CREATE 和 下面提到的ON_COMMAND:

#define ON_WM_CREATE() \
{ WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_MSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT)OnCreate) },

  

AFX_PMSG 是一个函数指针:

typedef void (CCmdTarget::* AFX_PMSG)(void);

定义一个DECLARE_MESSAGE_MAP()宏

#define DECLARE_MESSAGE_MAP() \
static AFX_MSGMAP_ENTRY _messageEntries[] ;\
static AFX_MSGMAP messageMap; \
virtual AFX_MSGMAP* GetMessageMap() const;

于是DECLARE_MESSAGE_MAP宏就相当于声明了下面的数据结构:

  

  

 这个数据结构的内容填充工作由下面三个宏完成:

#define BEGIN_MESSAGE_MAP (theClass,baseClass) \
AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; }\
AFX_MSGMAP theClass::messageMap = \
{
&(baseClass::messageMap), \
(AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) ; \
} \
AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{
#define ON_COMMAND(id,memberFxn) \
{ WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },
#define END_MESSAGE_MAP() \
{ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} \
};

这个AfxSig枚举的具体作用是什么呢?

上面的宏ON_WM_CREATE和ON_COMMAND也有出现类似的AfxSig_,那么到底作何用处?

从上面两个宏的最后一个参数可以看出,表示的是具体的执行函数,且都进行了类型转换,最终转换为AFX_PMSG.

可是AFX_PMSG的定义呢?我们再写一遍。

typedef void (CCmdTarget::* AFX_PMSG)(void);

这是一个什么鬼?无参,无返回值,不可能所有的函数都如此吧?

这正是AfxSig_的作用所在,当需要调用AFX_MSGMAP_ENTRY中的消息响应函数pfn的时候,具体的动作是这样的(出现在wincore.cpp CWnd::OnWndMsg 和 DispatchCmdMsg中):

union MessageMapFunctions mmf;
mmf.pfn = lpEntry->pfn; // lpEntry 是AFX_MSGMAP_ENTRY的对象
switch(lpEntry->nSig)
{
case AfxSig_is:
lResult = (this->*mmf.pfn_is)((LPTSTR)lParam);
break;
case AfxSig_lwl:
lResult = (this->*mmf.pfn_lwl)(wParam, lParam);
break;
case AfxSig_vv:
lResult = (this->*mmf.pfn_vv)();
break;
.....
}

重点注意: MessageMapFunctions 和AfxSig_. AfxSig_ 定义位于AFXMSG_.H档。

enum AfxSig
{
AfxSig_end = 0, // [marks end of message map] AfxSig_b_D_v, // BOOL (CDC*)
AfxSig_b_b_v, // BOOL (BOOL)
AfxSig_b_u_v, // BOOL (UINT)
AfxSig_b_h_v, // BOOL (HANDLE)
AfxSig_b_W_uu, // BOOL (CWnd*, UINT, UINT)
AfxSig_b_W_COPYDATASTRUCT, // BOOL (CWnd*, COPYDATASTRUCT*)
AfxSig_b_v_HELPINFO, // BOOL (LPHELPINFO);
AfxSig_CTLCOLOR, // HBRUSH (CDC*, CWnd*, UINT)
AfxSig_CTLCOLOR_REFLECT, // HBRUSH (CDC*, UINT)
AfxSig_i_u_W_u, // int (UINT, CWnd*, UINT) // ?TOITEM
AfxSig_i_uu_v, // int (UINT, UINT)
AfxSig_i_W_uu, // int (CWnd*, UINT, UINT)
AfxSig_i_v_s, // int (LPTSTR)
AfxSig_l_w_l, // LRESULT (WPARAM, LPARAM)
AfxSig_l_uu_M, // LRESULT (UINT, UINT, CMenu*)
.... // 后面很多不列举
};

MessageMapFunctions 定义于WINCORE.cpp 中:

union MessageMapFunctions
{
AFX_PMSG pfn; // generic member function pointer BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_D)(CDC*);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_b)(BOOL);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_u)(UINT);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_h)(HANDLE);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_u_u)(CWnd*, UINT, UINT);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_COPYDATASTRUCT)(CWnd*, COPYDATASTRUCT*);
BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_HELPINFO)(LPHELPINFO);
HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_W_u)(CDC*, CWnd*, UINT);
HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_u)(CDC*, UINT);
int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_W_u)(UINT, CWnd*, UINT);
int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_u)(UINT, UINT);
int (AFX_MSG_CALL CCmdTarget::*pfn_i_W_u_u)(CWnd*, UINT, UINT);
int (AFX_MSG_CALL CWnd::*pfn_i_s)(LPTSTR);
int (AFX_MSG_CALL CWnd::*pfn_i_S)(LPCTSTR);
LRESULT (AFX_MSG_CALL CWnd::*pfn_l_w_l)(WPARAM, LPARAM);
LRESULT (AFX_MSG_CALL CWnd::*pfn_l_u_u_M)(UINT, UINT, CMenu*);
	....  // 后面很多不列举
};

其实真正的函数只有pfn一个,但通过union后,它就有了许多类型不同的形象。这里可以看出union自动转型的作用。  

//***********************************************************************************************

上面讲基本内容解释完毕,下面以实例来演示宏展开后的结果:

//in header file
class CView : public CWnd
{
public:
//...
DECLARE_MESSAGE_MAP()
}; //in implementation file
#define CViewid 122
//...
BEGIN_MESSAGE_MAP(CView, CWnd)
ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP //被展开之后为 //in header file
class CView : public CWnd
{
public:
//...
static AFX_MSGMAP_ENTRY _messageEntries[];
static AFX_MSGMAP messageMap;
virtual AFX_MSGMAP* GetMessageMap() const;
}; //in implementation file
AFX_MSGMAP* CView::GetMessageMap() const
{
return &CView::messageMap;
} AFX_MSGMAP CView::messageMap = { &(CWnd::messageMap), (AFX_MSGMAP_ENTRY*)&(CView::_messageEntries) }; AFX_MSGMAP_ENTRY CView::_messageEntries[] = {
{WM_COMMAND, 0, (WORD)122, (WORD)122, 1, (AFX_MSGMAP)0},
{0, 0, 0, 0, 0, (AFX_MSGMAP)0}
};

以图表示则为:

  

MFC中定义了各种类似ON_COMMAND这样的宏,把各式各样的消息与特定的消息处理函数关联起来。

下面的代码中每一个CCmdTarget的衍生类都产生类似上图的消息映射表:

//in head file
class CObject
{
//....
//注意:CObject并不属于消息流动网的一份子
}; class CCmdTarget :public CObject
{
//...
DECLARE_MESSAGE_MAP()
}; class CWinThread :public CCmdTarget
{
//...
//注意:CWinThread并不属于消息流动网的一份子
}; class CWinApp :public CWinThread
{
//...
DECLARE_MESSAGE_MAP()
}; class CDocument :public CCmdTarget
{
//...
DECLARE_MESSAGE_MAP()
}; class CWnd :public CCmdTarget
{
//...
DECLARE_MESSAGE_MAP()
}; class CFrameWnd :public CWnd
{
//...
DECLARE_MESSAGE_MAP()
}; class CView :public CWnd
{
//...
DECLARE_MESSAGE_MAP()
}; class CView :public CWnd
{
//...
DECLARE_MESSAGE_MAP()
}; class CMyWinApp :public CWinApp
{
//...
DECLARE_MESSAGE_MAP()
}; class CMyFrameWnd :public CFrameWnd
{
//...
DECLARE_MESSAGE_MAP()
}; class CMyDoc :public CDocument
{
//...
DECLARE_MESSAGE_MAP()
}; class CMyView :public CView
{
//...
DECLARE_MESSAGE_MAP()
};
//in implementation file
BEGIN_MESSAGE_MAP(CWnd, CCmdTarget)
ON_COMMAND(CWndid, 0)
END_MESSAGE_MAP BEGIN_MESSAGE_MAP(CFrameWnd, CWnd)
ON_COMMAND(CFrameWndid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
ON_COMMAND(CDocumentid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CView, CWnd)
ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget)
ON_COMMAND(CWinAppid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(CMyFrameWndid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(CMyDocid, 0)
END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(CMyViewid, 0)
END_MESSAGE_MAP() //同时也设定了消息的终极标靶CCmdTarget的映射表内容:
AFX_MSGMAP CCmdTarget::messageMap =
{
NULL,
&CCmdTarget::_messageEntries[0]
}; AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries =
{
{0, 0, CCmdTargetid, 0, AfxSig_end, 0 }
}

以上构成了完整的消息流动网,如下图:

 

MFC 消息映射、分派和传递的更多相关文章

  1. MFC消息映射与命令传递

    题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用.我发觉很多编程的朋友,虽然每天都面对消息, ...

  2. 深入浅出MFC——消息映射与命令传递(六)

    1. 消息分类: 2. 万流归宗——Command Target(CCmdTarget): 3. "消息映射"是MFC内建的一个信息分派机制.通过三个宏(DECLARE_MESSA ...

  3. 剖析MFC六大关键技术(五六)--消息映射与命令传递

    说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西.有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样.特别是窗口消息及对消息的处理跟以前 ...

  4. MFC六大核心机制之五、六:消息映射和命令传递

    作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...

  5. MFC消息映射的原理:笔记

    多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里 ...

  6. MFC技术内幕系列之(四)---MFC消息映射与消息传递内幕

    ////////////////////////////////////////////////////////////////////////////////////                 ...

  7. MFC消息映射机制以及画线功能实现

    ---此仅供用于学习交流,切勿用于商业用途,转载请注明http://www.cnblogs.com/mxbs/p/6213404.html. 利用VS2010创建一个单文档标准MFC工程,工程名为Dr ...

  8. MFC编程入门之五(MFC消息映射机制概述)

    在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作.比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应. 一.什 ...

  9. VS2010/MFC编程入门之五(MFC消息映射机制概述)

    VS2010/MFC编程入门之五(MFC消息映射机制概述)-软件开发-鸡啄米 http://www.jizhuomi.com/software/147.html 上一讲鸡啄米为大家简单分析了MFC应用 ...

  10. MFC消息映射机制

    1.MFC应用框架主要类之间的关系 MFC自动生成的框架中重要的类有:C-App.CMainFrame.C-Doc和C-View. 其他的类如CClassView.CFileView等都是在框架窗口( ...

随机推荐

  1. ElasticSearch入坑指南之概述及安装

    ---恢复内容开始--- ElasticSearch入坑指南之概述及安装 了解ElasticSearch ElasticSearch(简称ES)基于Lucene的分布式全文检索引擎.使用ES可以实现近 ...

  2. jsp 页面间传递参数

    JSP页面间传递参数是经常需要使用到的功能,有时还需要多个JSP页面间传递参数.下面介绍一下实现的方法. (1)直接在URL请求后添加 如:< a href="thexuan.jsp? ...

  3. 使用YXHUD

    使用YXHUD 这是本人自己设计的一个类,但功能很不完善,先看看效果: 源码: YXHUD.h 与 YXHUD.m // // YXHUD.h // UILabel // // Created by ...

  4. 《C++ Primer Plus》读书笔记之十二—C++中的代码重用

    第14章 C++中的代码重用 1.C++代码重用方法:公有继承.使用本身是另一个类的对象的类成员(这种方法称为包含.组合或层次化).私有或保护继承.类模板等. 2.模板特性意味着声明对象时,必须指定具 ...

  5. 电脑技巧——DOS和windows的区别?

    本质:都是微软公司的操作系统,某种从程度上说windows是dos的后续操作系统版本.只是windows相比dos有质的飞跃.dos只支持命令操作,windows则有了良好的图形操作界面,window ...

  6. git did not exit cleanly (exit code 128)

    github,pull和push的时候出问题,提示git did not exit cleanly (exit code 128) 使用HTTP格式的url,不要使用SSH格式的url,在官网上赋值下 ...

  7. Sailing

    Sailing 目录 1基本信息 2歌曲简介 3歌词内容 4歌手简介 5专辑介绍 1基本信息编辑 歌曲: Sailing 所属专辑: Atlantic Crossing 艺人:Rod Stewart[ ...

  8. P2619 [国家集训队2]Tree I

    Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...

  9. 20145203 实验五 Java网络编程及安全

    20145203 实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 实验要求 1.基于Java Socket实现安全传输 2 ...

  10. virtualbox+vagrant学习-2(command cli)-22-vagrant validate命令

    Validate 格式: vagrant validate [options] 该命令用于验证你的Vagrantfile文件 userdeMacBook-Pro:~ user$ vagrant val ...