项目中使用了消息通信机制,因为消息类型非常多,相应的,处理消息的地方代码也非常多。
自然而然想到MFC中的消息映射:
创建一个缺省MFC框架程序的解决方案Test,在Test.h中看到以下内容:
class Ctest_mfcApp : public CWinApp
{
public:
Ctest_mfcApp();
// 重写
public:
virtual BOOL InitInstance();
// 实现
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
其中,最紧要的就是DECLARE_MESSAGE_MAP()这个宏,相关内容展开如下:
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)
};
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
#define DECLARE_MESSAGE_MAP() /
protected: /
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); /
virtual const AFX_MSGMAP* GetMessageMap() const; /
其中AFX_PMSG不再解析下去,我们认为这是一个指向特定消息对应的实现函数的函数指针,这几个宏的作用可简单理解成为Test这个项目定义了一个静态的消息映射表。当消息到来时,从消息队列中弹出消息并分发到具有入口实现的上层CWnd派生窗口。用户只需要注册消息,实现消息入口函数就够了,这在MFC中一般放在.cpp文件头部。Test.cpp中头部有以下内容:
BEGIN_MESSAGE_MAP(CTest, CWinApp)
ON_COMMAND(ID_APP_ABOUT, &CTest::OnAppAbout)
// 基于文件的标准文档命令
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
// 标准打印设置命令
ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
这里是为消息枚举值与消息实现函数建立映射,其中涉及到的宏的展开如下:
#define ON_COMMAND(id, memberFxn) /
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, /
static_cast<AFX_PMSG> (memberFxn) },
#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; /
} /
PTM_WARNING_RESTORE
按照上述思路得到的相似代码如下:
// Test.h
typedef void (* funCall)(void*); // 消息执行函数类型
struct tagMsgEntry // 消息入口结构
{
int nMsgType; // 消息类型
funCall MsgRun; // 消息执行函数
};
struct tagMsgMap // 消息映射表结构
{
const tagMsgMap* (__stdcall* funGetBaseMsgMap)();
const tagMsgEntry* pMsgEntries; // 消息入口集
};
class CMessage
{
// ...
protected:
static const tagMsgMap* __stdcall GetThisMsgMap();
virtual const tagMsgMap* GetMsgMap() const;
};
// Test.cpp
const tagMsgMap* CMessage::GetMsgMap() const
{
return GetThisMsgMap();
}
const tagMsgMap* __stdcall CMessage::GetThisMsgMap()
{
static const tagMsgEntry MsgEntries[] =
{
{ MSG_SOME_ONE, SomeOneFunc },
{ MSG_SOME_TWO, SomeTwoFunc },
{ MSG_NULL, NULL }
};
static const tagMsgMap msgMap =
{
&CBaseMessage::GetThisMsgMap, // 父类消息映射表
&MsgEntries[0]
};
return &msgMap;
}
int CMessage::MsgProc(int nMsgType)
{
switch( nMsgType )
{
case MSG_SOME_ONE:
{
}
break;
}
return CBaseMessage::MsgProc(nMsgType);
}
这种处理的优点在于,子类没有定义的消息实现接口,可以使用父类接口实现。不过在现在的消息处理中,我们一般不需要由基类来完成,因此可以不依赖基类接口,使用宏可以使代码看上去更加简洁。
___________________________________________________________
简化版本的消息映射采用以下方式,简单清晰:
// Test.h
#define REG_MSG_FUNC(nMsgType, MsgFunc) /
CMessge::RegisterCallFunc(nMsgType, MsgFunc); /
typedef void (* function)(void*);
typedef std::map<int, function> MSG_MAP;
typedef MSG_MAP::const_iterator MSG_CITR;
class CMessage
{
// ...
public:
static const MSG_MAP& GetMsgMap();
static void RegisterCallFunc(int nMsgType, void(* Func)(void *))
{
s_MsgMap[nMsgType] = Func;
}
int CMessage::Run(int nMsgType) // 消息公用执行函数
{
MSG_ITR itr = s_MsgMap.find(nMsgType);
if( s_MsgMap.end() != itr )
{
itr->second(this);
}
}
protected:
static MSG_MAP s_MsgMap; // 消息映射表
};
// UserTest.cpp -- 用户在使用时对自己关心的消息予以注册, 入口函数予以实现即可
REG_MSG_FUNC(MSG_SOME_ONE, SomeOneFunc)
void SomeOneFunc(CBaseMessage *pMsg)
{
return;
}
消息映射机制的简单实现
最新推荐文章于 2021-12-21 10:21:44 发布