struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows消息代号 UINT nCode; // WM_NOTIFY的控制代码 UINT nID; // WM_COMMAND下面的ID号,如果为其他的消息,则这个数字为0 UINT nLastID; //和前面的ID一起组成一个范围,用于发送一次消息,处理执行多次 UINT nSig; // 标志消息处理函数的类型 AFX_PMSG pfn; // 函数调用指针 }; typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
最后一句定义一个,类成员函数指针,正式由于这个指针的存在使得所有继承自CCmdTarget类别的函数都具有消息处理能力(CObject将所有的类集合成一个树形结构)。
struct AFX_MSGMAP { const AFX_MSGMAP* pBaseMap; //基类的消息映射,用于形成列表 const AFX_MSGMAP_ENTRY* lpEntries; //自身的消息处理数组 };
#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; \定义两个静态成员变量,表明不论有几个类实例都只能有一份消息处理映射和消息入口,至于GetMessagemap定义为虚拟函数是为了避免继承的时候能获取到相应的消息映射入口。
#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[] = \ { \ *******************************************************************************#define END_MESSAGE_MAP() \ {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \填充相应的数据结构,并且最后填充_messageEntries数组,这些数组是利用其它的宏来实现的。最后定义消息处理数组的结尾。
#define ON_COMMAND(id, memberFxn) \ { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },这个是对WM_COMMAND消息的处理,很明显,实际上有用的是id和memberFxn,至于AfxSig_vv表示返回void,参数为void
#define ON_COMMAND_RANGE(id, idLast, memberFxn) \ { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)idLast, AfxSig_vw, \ (AFX_PMSG)(void (AFX_MSG_CALL CCmdTarget::*)(UINT))&memberFxn },这里有两个地方和上面的ON_COMMAND不一样,一个是利用idLast限定了范围,另外就是消息处理函数利用了强制类型转换。因为之前的宏里面定义的函数式void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void)和我们的放进去的函数不相同,所以需要进行强制转换。至于其他的消息处理类似于这里的调用。下面让大家看一下整个消息处理的一个小角。BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { const AFX_MSGMAP* pMessageMap; const AFX_MSGMAP_ENTRY* lpEntry; UINT nMsg = 0; if (nCode != CN_UPDATE_COMMAND_UI) { nMsg = HIWORD(nCode); nCode = LOWORD(nCode); } if (nMsg == 0) nMsg = WM_COMMAND; for (pMessageMap = GetMessageMap(); pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap) { lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID); if (lpEntry != NULL) { return _AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); } }另外需要注意的是,因为有些消息有默认处理函数。而在整个MFC当中消息处理顺序是DOC、FRAME、VIEW、APP。所以在截取消息的时候需要注意顺序,如果你所添加的消息处理函数没有被调用很有可能是这个函数的默认调用在这个函数之前已经将消息处理完毕。