最简单的MFC程序,必须包含两个类CMyApp和CMainWindow类。
CMainWindow类从CFrameWind类派生。再往上为CWnd类,所有窗口类的父类,再往上为CCmdTarget类。
CMyApp类从CWinApp类派生。再往上是CWinThread类。在网上为CCmdTarget类。
windows是消息机制,MFC框架中需要将消息机制与消息响应函数对应起来。传统的windows程序是用callback和swich / case来实现响应不同的消息。
消息映射是要用宏定义的方式实现多态的效果,但不是多态,下面就要展开所有的与消息映射有关的宏。。如果在本类中没有查找到响应该消息的函数,就要去它的基类中寻找响应函数,是一个上行查询。
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance ();
};
class CMainWindow : public CFrameWnd
{
public:
CMainWindow ();
protected:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnPaint ();
DECLARE_MESSAGE_MAP () //消息映射声明部分宏
};
----------------------------------------
宏定义
----------------------------------------
#ifdef _AFXDLL
#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
-----------------------------------------------
有两个选择,是根据动态链接或静态链接而产生的不同。
展开后第一个为一个结构体数组,结构体定义如下:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
windows的消息在这可以看到
后面是另一个结构体数组,定义如下:
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
从结构体可以看出,第一个是指向基类消息映射的指针,第二个是指向当前类消息映射的指针。
接着是两个函数:
static const AFX_MSGMAP* PASCAL _GetBaseMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
返回值为AFX_MSGMAP结构体的指针。从字面上可以看出一个是获得消息映射,另一个是获得基类的消息映射。
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT ()
END_MESSAGE_MAP ()
---------------------------------------------
此上为消息映射实现代码,在里面只响应两个消息,一个是WM_LBUTTONDOWN,另一个是WM_PAINT消息。
此处有两个宏,我们查看宏定义:
以下为BEGIN_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_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \
#else
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \
以下为END_MESSAGE_MAP宏定义:
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
我们可以看到第一个宏的最后一个参数,第二个宏两个各含有一个花括号,可以看出是一个AFX_MSGMAP_ENTRY结构的数组,而之间是我们的消息响应,查看消息响应的宏定义:
#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))&OnPaint },
此部分经整理后会是下面一个形式:
const AFX_MSGMAP* PASCAL CMainWindow::_GetBaseMessageMap() //返回基类的消息映射
{
return &CFrameWnd::messageMap;
}
const AFX_MSGMAP* CMainWindow::GetMessageMap() const //返回当前消息映射
{
return &CMainWindow::messageMap;
}
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CMainWindow::messageMap = //数组,参数是两个函数返回值
{
&CMainWindow::_GetBaseMessageMap, &CMainWindow::_messageEntries[0]
};
AFX_COMDAT const AFX_MSGMAP_ENTRY CMainWindow::_messageEntries[] = //消息映射的数组
{
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
{ WM_PAINT, 0, 0, 0, AfxSig_vv, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))&OnPaint },
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
-------------------------------------------------------------------------------------------------
就是在此部分实现的消息映射,将系统消息与消息响应函数进行绑定。
消息传递过来,通过CMainWindow::GetMessageMap() 获得 CMainWindow::messageMap 数组,在这个数组中有这个类消息映射的入口,进入查找,如果没有,通过CMainWindow::_GetBaseMessageMap()返回的基类的messageMap数组,可以获得基类的消息映射入口,一直上行找到找到这个消息响应的地方。
在说一下MFC程序的WinMain,MFC程序的WinMain函数被隐藏掉了,我们可以进入调试模式,找到这个函数的call stack就能看到WinMain函数被调用的地方。我在本机上的VC++6.0在call stack显示的时候有些问题,不完全,下面有一部分是参考的资料,没有去编程环境的虚拟机中的VC++6.0进行测试。
WinMain调用AfxWinMain,程序开始运行。
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
AFXWinMain调用InitInstance()函数,之后调用一个CWinApp的Run()函数返回CWinThread::Run()函数,调用基类的Run()函数,在此进入消息循环。
调用InitInstance()函数时消息循环还未开始。消息循环卸载CWinThread中。
昨天弄这个的时候太晚了,比较乱,过几天时间够的话可能会写一个MFC框架的分析。
这是我的新浪blog地址,欢迎大家指教。
http://blog.sina.com.cn/dingmorphling
- 标 题:MFC消息映射机制分析
- 作 者:morphling
- 时 间:2011-08-21 18:00:10
- 链 接:http://bbs.pediy.com/showthread.php?t=139101