几个重要的结构体:

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. 安装Linux Centos系统硬盘分区方法

    一.硬盘回顾 无论是安装Windows还是Linux操作系统,硬盘分区都是整个系统安装过程中最为棘手的环节.硬盘一般分为IDE硬盘.SCSI硬盘和SATA硬盘三种,在Linux系统中,IDE接口的硬盘 ...

  2. [翻译] JTSReachability

    JTSReachabilit An adaptation of Apple's Reachability with some block-based conveniences. 这是一个苹果的网络检测 ...

  3. AD用户登录验证,遍历OU(LDAP)

    先安装python-ldap模块 1.验证AD用户登录是否成功 import sqlite3,ldap domainname='cmr\\' username='zhangsan' ldapuser ...

  4. Spring中的统一异常处理方式

    源自:https://segmentfault.com/a/1190000016236188 在具体的SSM项目开发中,由于Controller层为处于请求处理的最顶层,再往上就是框架代码的. 因此, ...

  5. QuickBI助你成为分析师-数据建模(一)

    摘要: 数据集编辑功能界面介绍以及常见问题总结. 在数据集编辑界面可以进行数据建模来更好的展示数据,创建数据集默认将数值类型字段作为度量,日期.字符串等类型作为维度,度量可以根据维度分组展示.下面来介 ...

  6. 铁乐学Python_day06-整数和字符串小数据池

    python小数据池(内存地址) 今天来学习认识一下python中的小数据池. 我们都知道 ==是用来作比较,要是两个变量的数值相等, 用==比较返回的bool值会是True: a = 1000 b ...

  7. wordpress利用rsync同步备份

    我搭建的wordpress服务器现在使用的系统是opensuse, 服务器上面已做脚本和计划任务将wordpress使用的数据库与web目录每天压缩备份放到 /data/backup目录下 opera ...

  8. September 21st 2017 Week 38th Thursday

    What fire does not destroy, it hardens. 烈火摧毁不了的东西,只会变得更坚固. The true gold can stand the test of fire, ...

  9. php实现随机数字、字母的验证码

     php实现随机数字.字母的验证码 可自定义生成验证码文字的大小.数量.干扰项等等,也可以自定义验证文字的字体... 废话不多说,直接上代码: 1.classgd.class.php <?php ...

  10. 【转载】Java 集合框架

    http://wangkuiwu.github.io/2012/02/03/collection-03-arraylist/ 网上比较全的Java集合框架教程. 注:transient是Java语言的 ...