最简单的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