《MFC Windows 程序设计》(第2版) 1.3.6
MFC的DECLARE_MESSAGE_MAP宏在类声明中添加3个成员:一个名为_messageEntries的私有的AFX_MSGMAP_ENTRY结构数组,其中包含将消息与消息处理程序相关联的信息;一个名为messageMap的静态AFX_MSGMAP结构,其中包含一个指向类中的_messageEntries数组的指针和一个指向基类中的messageMap结构的指针;以及一个名为GetMessageMap的虚拟函数,该函数返回messageMap的地址(对于一个动态而不是静态链接到MFC的MFC应用程序,其宏执行稍微有些不同,但工作原理是相同的)。BEGIN_MESSAGE_MAP包含GetMessageMap函数的实现,和用来初始化messageMap结构的代码。BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间出现的宏填入到_messageEntries数组中,而END_MESSAGE_MAP使用一个NULL条目标记了数组的结尾。对于下面的语句:
// In the class declaration
DECLARE_MESSAGE_MAP()
// In the class implementation
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
编译器的预处理程序将生成下面的代码:
// In the class declaration
private:
static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
static const AFX_MSGMAP messageMap;
virtual const AFX_MSGMAP* GetMessageMap() const;
// In the class implementation
const AFX_MSGMAP* CMainWindow::GetMessageMap() const;
{ return &CMainWindow::messageMap; }
const AFX_MSGMAP CMainWindow::messageMap = {
&CFrameWnd::messageMap,
&CMainWindow::_messageEntries[0]
};
const AFX_MSGMAP_ENTRY CMainWindow::_messageEntries[] = {
{ WM_PAINT, 0, 0, 0, AfxSig_vv,
(AFX_PMSG)(AFX_PMSGW)(void(CWnd:: * )(void))OnPaint },
{ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
只要这个基础结构的位置合适,框架就可以调用GetMessageMap来获取一个指向CMainWindow的messageMap结构的指针。然后它可以搜索_messageEntries数组来查看CMainWindow是否具有此消息的处理程序。此外,如果需要,它还能够维持一个指向CFrameWnd的messageMap结构的指针并搜索基类的消息映射。
下面就是对当一个CMainWindow的消息抵达时所发生事件的相当详细的描述。要分派此消息,框架调用了CMainWindow从CWnd继承下来的虚拟WindowProc函数。WindowProc调用OnWndMsg,而OnWndMsg又调用GetMessageMap来获取一个指向CMainWindow::messageMap的指针,并搜索CMainWindow::_messageEntries来获取一个其消息ID与当前正等待处理的消息的ID相匹配的条目。如果找到了该条目,对应的CMainWindow函数(其地址与该消息ID一同存储在_messageEntries数组中)就被调用。否则,OnWndMsg参考CMainWindow::messageMap获得一个指向CFrameWnd::messageMap的指针并为基类重复该过程。如果基类没有该消息的处理程序,则框架将上升一个级别,参考基类的基类,相当系统地沿着继承链向上走,直到它找到一个消息处理程序或者将该消息传递给Windows进程默认处理为止。图1-5从CMainWindow的消息映射条目开始,用示意图阐明了CMainWindow的消息映射,并表明了框架在搜索一个匹配特定消息ID的处理程序时所经过的路径。
MFC的消息映射机制的作用相当于,它是将消息连接到消息处理程序而不使用虚拟函数的一种非常有效的方式。虚拟函数在空间上并不有效,因为它们需要虚表,而且即使虚表中的函数没有被覆盖,虚表也会消耗内存。相反,消息映射所使用的内存的数量与它所包含的条目的个数成比例。由于程序员要执行一个包含所有不同消息类型的处理程序的窗口类是非常少见的,因此消息映射只是大约在每当CWnd使用HWND包装时保持几百字节的内存。