| 
| 发信人: sodo() 整理人: cobe(1999-12-17 10:26:27), 站内信件
 |  
| 现在,再让我们来看一下MFC的窗口是如何响应消息的。 
 我们先来看一段建立一个窗口的代码.
 
 class CMsgWnd : public CWnd
 {
 public:
 CMsgWnd() {}
 
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CMsgWnd)
 virtual BOOL Create(CWnd* pParentWnd);
 //}}AFX_VIRTUAL
 
 virtual ~CMsgWnd() {}
 
 protected:
 //{{AFX_MSG(CMsgWnd)
 afx_msg void OnPaint();
 afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
 };
 
 BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)
 //{{AFX_MSG_MAP(CMsgWnd)
 ON_WM_PAINT()
 ON_WM_LBUTTONDOWN()
 //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 
 BOOL CMsgWnd::Create(CWnd *pParentWnd)
 {
 return CWnd::Create(AfxRegisterWndClass(CS_DBLCLKS),
 "Message Window", WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),
 pParentWnd, 12345);
 }
 
 void CMsgWnd::OnPaint()
 {
 CPaintDC dc(this); // device context for painting
 
 dc.TextOut(0,0,"hello");
 }
 
 void CMsgWnd::OnLButtonDown(UINT nFlags, CPoint point)
 {
 MessageBox("OnLButtonDown");
 
 CWnd::OnLButtonDown(nFlags, point);
 }
 
 
 以上是一段标准的CWnd窗口类的子类实现代码.
 我想大家应该是可以看的懂的.
 注意到CMsgWnd类中有一句代码 DECLARE_MESSAGE_MAP()
 我们来看看这个宏是如何定义的:
 
 typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
 // 这是一个指向 CCmdTarget 类成员函数的指针类型.
 
 struct AFX_MSGMAP_ENTRY // 消息映射表之消息入口
 {
 UINT nMessage; // 消息
 UINT nCode; // 控制码或者是 WM_NOTIFY 消息的通知码
 UINT nID; // 控件的ID,如果是窗口消息则为0
 UINT nLastID; // 如果是一个范围的消息,那么这是最后一个控件的ID
 UINT nSig; // 消息处理类型
 AFX_PMSG pfn; // 消息处理函数
 };
 
 struct AFX_MSGMAP // 消息映射表
 {
 #ifdef _AFXDLL // 如果MFC是动态连接的,
 // 就是编译时选择 Use MFC in a shared DLL
 const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
 #else // MFC是静态连接的,编译时选择 Use MFC in a static library.
 const AFX_MSGMAP* pBaseMap;
 #endif
 // 如果MFC是动态连接的, 就用pfnGetBaseMap函数返回基类的消息映射表
 // 否则 pBaseBap 指向基类的消息映射表
 
 const AFX_MSGMAP_ENTRY* lpEntries; // 指向消息入口的指针
 };
 
 
 #ifdef _AFXDLL // 如果MFC是动态连接的
 #define DECLARE_MESSAGE_MAP() \
 private: \
 static const AFX_MSGMAP_ENTRY _messageEntries[]; \ // 消息入口
 protected: \
 static AFX_DATA const AFX_MSGMAP messageMap; \ // 消息映射表
 static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \
 // 该函数返回基类的消息映射表
 virtual const AFX_MSGMAP* GetMessageMap() const; \
 // 该函数返回当前类的消息映射表
 
 #else // 静态连接的
 #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; \
 #endif
 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 // 以上这段代码实际上是嵌入在你的类中.
 
 在 .CPP 文件中我们看到还有这段宏,
 BEGIN_MESSAGE_MAP(CMsgWnd, CWnd)
 //{{AFX_MSG_MAP(CMsgWnd)
 ON_WM_PAINT()
 ON_WM_LBUTTONDOWN()
 //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 现在我们再来看看它是被如何定义的.
 
 #ifdef _AFXDLL
 #define BEGIN_MESSAGE_MAP(theClass, baseClass) \
 const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \
 { return &baseClass::messageMap; } \ // 返回基类的消息映射表
 ^^^^^^^^^^^^^^^^^^^^^^
 const AFX_MSGMAP* theClass::GetMessageMap() const \
 { return &theClass::messageMap; } \ // 返回当前类的消息映射表
 
 AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ // 消息映射表
 { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
 { \
 
 #else
 #define BEGIN_MESSAGE_MAP(theClass, baseClass) \
 const AFX_MSGMAP* theClass::GetMessageMap() const \
 { return &theClass::messageMap; } \ // 返回当前类的消息映射表
 
 AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ // 消息映射表
 { &baseClass::messageMap, &theClass::_messageEntries[0] }; \
 ^^^^^^^^^^^^^^^^^^^^^^ 可以看到这是 MFC 动态连接与静态连接的区别所在
 
 动态连接时使用函数 _GetBaseMessageMap 返回 &baseClass::messageMap
 而静态连接是直接使用.至于Microsoft为什么要这样做,
 好像没有什么很好的理由.
 当然这个并不重要,我们暂且不用理会.
 
 const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
 //开始初始化消息入口
 { \
 #endif
 
 #define END_MESSAGE_MAP() \
 {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
 }; \ ^^^^^^^^^^^^^^^^^^^^^^ 指示消息映射结束.
 
 在这两个宏之间还有
 ON_WM_PAINT()
 ON_WM_LBUTTONDOWN()
 它们是我们在ClassWizard中选择了WM_PAINT和WM_LBUTTONDOWN消息后,
 MFC自动加入的,那么这两个又是如何定义的呢?
 
 ......
 
 #define ON_WM_PAINT() \
 { WM_PAINT, 0, 0, 0, AfxSig_vv, \
 ^^^^^^^^ ^ ^ ^ ^
 | | | | +------------- 消息处理类型
 | | | +------------------- LastID
 | | +---------------------- ID=0, 窗口消息
 | +------------------------- 控制码
 +-------------------------------- WM_PAINT 消息
 (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)
 (void))&OnPaint },
 ^^^^^^^^^^^^^^^ 消息处理函数
 .....
 
 #define ON_WM_LBUTTONDOWN() \
 { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
 ^^^^^^^^^^^^^^ WM_LBUTTON 消息
 (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)
 (UINT, CPoint))&OnLButtonDown },
 ^^^^^^^^^^^^^ 消息处理函数
 ......
 
 现在我们差不多可以看得出来了,消息处理函数可以
 靠_messageEntries来找到每个消息的处理函数.
 我们可以再看看CWnd类来验证我们的想法.
 
 .............................................................. continu
 ed
 
 --
 十年磨一剑,霜寒未曾试;
 今日拔剑问,谁有不平事?
 ----老猫
 
 ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.103.47.234]
 
 |  |