发信人: 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]
|
|