精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● VB和Basic>>〓〓..技术文章连载..〓〓>>MFC消息机制>>[转载自白云黄鹤]MFC消息机制(二)

主题:[转载自白云黄鹤]MFC消息机制(二)
发信人: 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]

[关闭][返回]