从学习Windows的消息机制学习到MFC的消息映射,这是我最喜欢的一节。在Win32编程中,你回去定义各种消息,在WndProc中通过switch ..case..进行消息的分类处理。带着这种思维走进MFC,你会发现MFC采用一种消息映射表模式来简化消息处理。虽说书上理解起来很简单,关于消息如何在MFC框架中流动,其中原理是什么,这才是我们应该学会的,而不是仅仅学会如何使用。
*Message Mapping(消息映射)
当然我们希望消得以下流动
消息流动的终点在CCmdTarget,我们来看看消息如何实现以上的流动的,先来介绍两个数据结构:
typedef void (CCmdTarget::*AFX_PMSG)(void);
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn;
};
即:定义一个AFX_MSGMAP_ENTRY,通过结构我们可以获得消息对应的处理函数指针。
struct AFX_MSGMAP
{
AFX_MSGMAP* pBaseMessageMap;
AFX_MSGMAP_ENTRY* lpEntries;
};
即:通过AFX_MSGMAP,可以获得类成员的AFX_MSGMAP_ENTRY指针,从而获得类中所有消息映射,通过pBaseMessageMap获得基类的AFX_MSGMAP指针,这样可以实现"同宗"的流动。
同样,MFC也为了降低我们的工作量,构建了几个宏: DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP,END_MESSAGE_MAP。
来看看具体的定义:
#define DECLARE_MESSAGE_MAP() \
static AFX_MSGMAP_ENTRY _messageEntries[]; \
static AFX_MSGMAP messageMap; \
virtual AFX_MSGMAP* GetMessageMap() const;
即:还是老套路,声明static变量来保证每个类中静态成员唯一,_messageEntries[]
保存了类的消息映射信息,messageMap是子类和父类消息映射的纽带,消息通过向上流动可以实现遍历。虚函数GetMessageMap() 返回类对应的messageMap。
如图:
#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 END_MESSAGE_MAP() \
{ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} \
};
即:BEGIN_MESSAGE_MAP与END_MESSAGE_MAP是成对出现的,看了宏定义确实如此,当然在实际代码中我们经常写到:
BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP()
在俩宏的中间加入了我们的消息映射,当然ON_COMMAND宏我们也可以自行实现,
只需要实现宏完成一个_messageEntries的初始化。
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
当然,我们对END_MESSAGE_MAP()中的AfxSig_end定义为:
enum AfxSig
{
AfxSig_end = 0, // [marks end of message map]
}
代表消息映射的结束。
当然,这两个宏怎么用呢,其实很简单,下面用CView作为Demo:
头文件:
class CView :public CWnd
{
public:
CView()
{
std::cout << "CView Constructor \n";
}
~CView()
{
std::cout << "CView Destructor \n";
}
DECLARE_MESSAGE_MAP()
};
Cpp文件:
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(CMyViewid, 0)
END_MESSAGE_MAP()
我们在main函数中测试一下:
void printlpEntries(AFX_MSGMAP_ENTRY *lpEntry)
{
struct
{
int classid;
char* classname;
}classinfo[] =
{
CCmdTargetid, "CCmdTarget",
CWinThreadid, "CWinThread",
CWinAppid, "CWinApp",
CMyWinAppid, "CMyWinApp",
CWndid, "CWnd",
CFrameWndid, "CFrameWnd",
CMyFrameWndid, "CMyFrameWnd",
CViewid, "CView",
CMyViewid, "CMyView",
CDocumentid, "CDocument",
CMyDocid, "CMyDoc",
0, " "
};
for (int i = 0; classinfo[i].classid != 0; i++)
{
if (classinfo[i].classid == lpEntry->nID)
{
std::cout << lpEntry->nID << " ";
std::cout << classinfo[i].classname << std::endl;
break;
}
}
}
void MsgMapPrinting(AFX_MSGMAP * pMessageMap)
{
for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap)
{
AFX_MSGMAP_ENTRY * lpEntry = pMessageMap->lpEntries;
printlpEntries(lpEntry);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
CWinApp * pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
pApp->Run();
CMyDoc *pMyDoc = new CMyDoc;
CMyView *pMyView = new CMyView;
CFrameWnd * pMyFrame = (CFrameWnd *)pApp->m_pMainWnd;
AFX_MSGMAP *pMessageMap = pMyDoc->GetMessageMap();
std::cout << std::endl << "MyDoc MessageMap : " << std::endl;
MsgMapPrinting(pMessageMap);
pMessageMap = pMyView->GetMessageMap();
std::cout << std::endl << "MyView MessageMap : " << std::endl;
MsgMapPrinting(pMessageMap);
pMessageMap = pMyFrame->GetMessageMap();
std::cout << std::endl << "MyFrame MessageMap : " << std::endl;
MsgMapPrinting(pMessageMap);
pMessageMap = pApp->GetMessageMap();
std::cout << std::endl << "pApp MessageMap : " << std::endl;
MsgMapPrinting(pMessageMap);
system("pause");
return 0;
}
结果: