在MFC中,消息分为三大类:
1、命令消息
常见的就是由菜单或快捷键或工具栏产生的消息,MFC通过菜单项的识别码分辨来自各处的消息。
以下是两个例子:
//**.h DECLARE_MESSAGE_MAP() afx_msg void OnFileWrite(); afx_msg void OnFileRead(); //**.cpp BEGIN_MESSAGE_MAP(CtestView, CRichEditView) ON_COMMAND(IDM_FILE_WRITE, &CtestView::OnFileWrite) ON_COMMAND(IDM_FILE_READ, &CtestView::OnFileRead) END_MESSAGE_MAP() ... CtestView::OnFileRead()//响应函数 {..}
凡是派生于CCmdTarget的类,均有资格接收命令消息。
2、标准消息
除命令消息外,任何以WM_开头的消息都是标准消息。
以下示例了一个WM_CREATE消息:
//**.h DECLARE_MESSAGE_MAP() afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); //**.cpp BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() ... int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)//消息响应 {...}
凡派生至CWnd的类,均可以接收此类消息。也就是说,CWnd及其子类,可以接收命令消息和标准消息
3、控件消息
称为Control Notification,这种消息由控件产生,是为了向其父窗口(通常为对话框)通知某种消息。
以下的例子说明了listbox中的项目被选择时,产生一个LBN_SELCHANGE消息。
//**.h DECLARE_MESSAGE_MAP() afx_msg void OnLbnSelchangeList1(); //**.cpp BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) ON_LBN_SELCHANGE(IDC_LIST1, &CAboutDlg::OnLbnSelchangeList1) END_MESSAGE_MAP() ... void CAboutDlg::OnLbnSelchangeList1()//消息响应 {...}
下面说说消息映射。消息映射其实一个巨大的网,一种数据结构。作用是决定消息的流动路线。
上面的消息映射,都出现了四个奇怪的宏。
DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP
ON_COMMAND
END_MESSAGE_MAP
其中ON_COMMAND是命令消息的宏,当然也可以是标准消息或控件消息,以ON_开头定义。
如:ON_WM_CREATE,ON_LBN_SELCHANGE
下面我们来层层挖掘.
首先是DECLARE_MESSAGE_MAP宏,这个宏声明了一个MessageMap数据类型。
#define DECLARE_MESSAGE_MAP() / protected: / static const AFX_MSGMAP* PASCAL GetThisMessageMap(); / virtual const AFX_MSGMAP* GetMessageMap() const; /
这里看到AFX_MSGMAP,定义如下:
struct AFX_MSGMAP { const AFX_MSGMAP* PBaseMap; const AFX_MSGMAP_ENTRY* lpEntries; }
可以看出,pBaseMap指向基类的MessageMap,这个指针提供了一个走访整个继承链表的方法。而lpEntries指向了AFX_MSGMAP_ENTRY结构体.这个结构便是消息映射的网络的单个节点。
这个结构体的定义如下,这个结构体的作用是让消息nMessage对应于函数pfn。
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) };
其中pfn的定义为函数的指针
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
到此为止,只是声明了一种类型,并没有真正的实例化。
上面已经定义了一个MessageMap的结构,谨记这个结构
struct AFX_MSGMAP
{
const AFX_MSGMAP* PBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
}
下面的BEGIN/ON/END就是来实例化这个结构,以组成一个庞大的消息映射网络。
下面是详细定义BEGIN/ON/END
#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; /
于是,对于这样的宏
BEGIN_MESSAGE_MAP(CtestView, CRichEditView)
ON_WM_CREATE()
ON_COMMAND(IDM_FILE_READ, &CtestView::OnFileRead)
END_MESSAGE_MAP()
其中ON_WM_CREATE被定义为:(afxmsg_.h中)
#define ON_WM_CREATE() / { WM_CREATE, 0, 0, 0, AfxSig_is, / (AFX_PMSG) (AFX_PMSGW) / (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) },
ON_COMMAND被定义为:(afxmsg_.h中)
#define ON_COMMAND(id, memberFxn) / { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, / static_cast<AFX_PMSG> (memberFxn) },
展开后就是
const AFX_MSGMAP* CtestView::GetMessageMap() const { return GetThisMessageMap(); } const AFX_MSGMAP* PASCAL CtestView::GetThisMessageMap() { typedef CtestView ThisClass; typedef CRichEditView TheBaseClass; static const AFX_MSGMAP_ENTRY _messageEntries[] = { {WM_CREATE, 0, 0, 0, AfxSig_is,(AFX_PMSG) (AFX_PMSGW)(static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) } {WM_COMMAND,CN_COMMAND,(WORD)IDM_FILE_READ,(WORD)IDM_FILE_READ,AfxSigCmd_v,static_cast<AFX_PMSG> (&CtestView::OnFileRead) }, {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } }; static const AFX_MSGMAP messageMap = { &CRichEditView::GetThisMessageMap, &_messageEntries[0] }; return &messageMap;
简单的文字替换,呵呵,这样就一目了然了。
经过以上的处理,现在为CtestView生成了一个messagemap
CtestView::messagemap
{
pBaseMap->Cview::messagemap;
lpEntries-> messageEntries[];
}
所有派生自CCmdTarget的类都可以接收消息,也就是说,这些类都有一个DECLARE/BEGIN/END宏组,但请注意,CWinThread没有,即CWinApp是直接跳到CCmdTarget的,定义如下:
BEGIN_MESSAGE_MAP(CWinApp,CCmdTarget)
好了,通过这些宏,我们构造了一个庞大的消息映射网络.
下一章讨论,MFC如何利用这个网络进行消息映射,到底是谁把消息放入messagemap中,谁执行消息的比较操作等等等。这些都会在下一章揭晓。