MFC消息响应原理和深入分析的整理

来源:互联网 发布:mac照片导u盘 编辑:程序博客网 时间:2024/05/21 23:50

首先对于这个映射机制有点类似一个表。就是一个消息对应于一个消息处理函数,这也就是映射。这里要知道MFC的一个窗口创建过程

一、MFC的入口点与简单的执行过程


MFC隐藏了windows程序的入口点winMain,其实是在appmodule.cpp文件下,该入口点调用MFC的全局函数AfxWinMain作为MFC的入口点

然后AfxWinMain会进行一些初始化操作,并执行全局CWinApp的InitInstance函数,即我们重写的一个虚函数

在CWinApp函数中,会初始化我们的窗口指针,调用了该窗口的构造函数,在构造函数中会有一个Create函数,该函数会注册一个窗口类,但是还没有类名和窗口过程,接着会执行一个PreCreateWindow的函数,处理好后,会给一个类名和一个DefWindowProc,并注册好这个窗口类,紧接着,会执行AfxHookWindowCreate函数,作用是将该窗口的窗口过程变为AfxWindowProc,即为一个全局的窗口过程函数,即MFC下所有窗口都共享一个窗口过程。窗口类注册好后,就用CreateWindow的api函数创建了一个窗口,窗口的创建就完成了。这是由入口点大概的执行流程,中间也忽略了不少函数,但是差不多就是windows sdk写程序的大概流程了,注册窗口类之类的全在里面了。

最主要的还是窗口的消息循环,由上述步骤可以发现,MFC的窗口的WindowProc是一个函数,即全局的AfxWindowProc,接下来记录下MFC的消息流向即消息映射。


先说明要产生消息的映射的几个步骤:

第一步:


这里是MFC窗体所共有的窗口过程,即任何窗体接受到的消息,最终都会流向这里,首先 该函数会由消息的接受句柄来得到它的窗体指针,接着会调用AfxCallWndProc函数.还有我们可以看到return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);  这个函数。接下来就去看看这个函数

第二步:

  1. LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,  
  2.     WPARAM wParam = 0, LPARAM lParam = 0)  
  3. {  
  4.        ......
  5.         lResult = pWnd->WindowProc(nMsg, wParam, lParam);  
  6.   
  7.         // more special case for WM_INITDIALOG  
  8.         if (nMsg == WM_INITDIALOG)  
  9.             _AfxPostInitDialog(pWnd, rectOld, dwStyle);  
  10.     }  
  11.     CATCH_ALL(e)  
  12.     {  
  13.         lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg);  
  14.         TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n",  
  15.             lResult);  
  16.         DELETE_EXCEPTION(e);  
  17.     }  
  18.     END_CATCH_ALL  
  19.   
  20.     pThreadState->m_lastSentMsg = oldState;  
  21.     return lResult;  
  22. }  

我们只看  lResult = pWnd->WindowProc(nMsg, wParam, lParam); 这句,中间代码有省略。。。 其实发现这里又回到了产生消息的窗口处理过程中,AfxWindowProc相当于一个中转,然后根据消息的类型(这里消息是和句柄有关,一个窗口产生消息,那么这个消息是和这个窗口句柄有关,同时,窗口句柄有个窗口类指针有关。相当于这个消息是关联这这个窗口类的指针,这里要正确理解窗口和窗口类的区别) 转发到消息的关联窗口去,然后该窗口类调用 pWnd->WindowProc

而且我们可以发现这个WindowProc 是个虚函数,会调用基类的 WindowProc 。本身它也是从基类继承过来的。接下来就看看这个函数吧:

  1. LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     // OnWndMsg does most of the work, except for DefWindowProc call  
  4.     LRESULT lResult = 0;  
  5.     if (!OnWndMsg(message, wParam, lParam, &lResult))  
  6.         lResult = DefWindowProc(message, wParam, lParam);  
  7.     return lResult;  
  8. }  
发现这里是调用OnWndMsg(message, wParam, lParam, &lResult));这个函数可以有大用处呢!待会来剖析它。它和我们一般要定义的3个宏有关
DECLARE_MESSAGE_MAP(),BEGIN_MESSAGE_MAP,END_MESSAGE_MAP  其实也没什么因为这个OnWndMsg()只不过是调用他们定义的函数而已。
嘿嘿。。。待会后面具体来看看怎么个调用法。这里先吧消息的执行过程走一遍。
这个OnWndMsg(message, wParam, lParam, &lResult));是对消息进行处理,并且调用相应的处理函数。如果没有,则调用窗口默认的 DefWindowProc(message, wParam, lParam); 
消息过程就这样走完了。但是这里并没有看到消息映射啊? 其实刚刚已经提到了,OnWndMsg(message, wParam, lParam, &lResult)),这里就是包含了消息的映射了。消息处理过程涉及到多态,这里需要把握看这些函数具体调用的是基类的还是产生消息的窗口类。

接下来我们就看看


二、消息的响应机制


OnWndMsg(message, wParam, lParam, &lResult))这个函数是怎么样一个消息映射。
一般我们创建消息映射的3个步骤.忘记了?? 
http://www.cnblogs.com/zengxiangchun/archive/2012/04/27/2473080.html    复习一下吧!!!

1、消息响应函数原型(以WM_LBUTTON来说)

//{{AFX_MSG(CMyView)afx_msg void OnLButtonDown(UINT nFlags,CPoint point);//}}AFX_MSGDECLARE_MESSAGE_MAP()

这里首先对消息处理函数 OnLButtonDown()的一个声明

其次是定义了一个宏 DECLARE_MESSAGE_MAP() 。
2、定义的4个宏
DECLARE_MESSAGE_MAP

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

     ON_WM_LBUTTONDOWN()

END_MESSAGE_MAP()


先看DECLARE_MESSAGE_MAP() 
protected:

     static const AFX_MSGMAP* PASCAL GetThisMessageMap();

     virtual const AFX_MSGMAP* GetMessageMap() const;

可以发现 声明了2个函数, GetMessageMap() , GetThisMessageMap() 返回类型是AFX_MSGMAP* ,这两个函数的作用就是获得指向MSGMAP的指针,每一个窗口类都会有这样一个 MSGMAP。AFX_MSGMAP是一种数据结构类型,看下它的定义吧:

struct AFX_MSGMAP

{

     const AFX_MSGMAP* (* pfnGetBaseMap)();//这里是获得基类的 MSGMAP*

     const AFX_MSGMAP_ENTRY* lpEntries;//本类的 AFX_MSGMAP_ENTRY*

};

再看

BEGIN_MESSAGE_MAP()END_MESSAGE_MAP()


这两个宏是连在一起看的。重写基类的 GetMessageMap() ?哪个重写? 当然是产生消息的那个窗口类呗。这里是CmainFrame::

最后看      ON_WM_LBUTTONDOWN()

{ WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_PMSG) (AFX_PMSGW)(static_cast

< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) }

可以发现这个宏表示了 ON_WM_CREATE()和OnCreate()处理函数想关联,而且你可以发现这个宏是放在 AFX_MSGMAP_ENTRY _messageEntries[](这是一个MessageEntry的数组,包含许多消息实体,譬如刚刚说的ON_WM_LBUTTONDOWN() 中,所以只需要查找到了本窗口类的messageEntries[],再从中查找对应于Message消息的MessageEntry就能够找到相应的处理函数。这里就是后面先从本窗口类messageEntries[] 中来查找Message。

上面都是一些初始化的代码。这里要记住DECLAE_ MESSAGE_MAP声明了2个函数GetMessageMap() , GetThisMessageMap() ,然后

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

      ON_WM_LBUTTONDOWN()

END_MESSAGE_MAP()  

重写GetMessageMap(),获得指向AFX_MSGMAP 的指针 。什么是AFX_MSGMAP ? 忘了? 再看看上面的定义吧,就是一个结构体,包含本窗口类的AFX_MSGMAP_ENTRY和基类的AFX_MSGMAP的指针啦。。。

好啦。上面的一些结构体的定义和函数声明定义搞清楚了。接下来就要进入

OnWndMsg(message, wParam, lParam, &lResult))  啦。。。。 先看函数



其中的pMessageMap = GetMessageMap();语句要留意,因为GetMessageMap是虚函数,在上面的CmainFrame类中已经重写了,所以,调用的实际上是CmainFrameGetMessageMap;返回一个本窗口类的 AFX_MSGMAP指针 pMessageMap,这个MSGMAP包含了 基类的MSGMAP指针和本窗口类的_messageEntries的入口地址。所以我们可以知道

for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)

        if (message < 0xC000)

            if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)

                goto LDispatch;

AfxFindMessageEntry就是在找message和_messageEntries.nMessage的相等,如果不等,就往基类里面找:  pMessageMap = pMessageMap->pBaseMap  

找到了就跳到Ldispatch,其中,有:mmf.pfn = lpEntry->nPfn;这个就是放着该消息处理函数的内容。这里是一个函数指针。


所以我们到现在应该知道
OnWndMsg(message, wParam, lParam, &lResult));这个函数是怎么样一个消息映射吧。通俗一点讲就是,先从本窗口类的AFX_MSGMAP(这个MSGMAP包含了 基类的MSGMAP指针和本窗口类的_messageEntries的入口地址)中的_messageEntries来找,如果没有找到Message对应的MessageEntry,那么就从对应的基类开始找。如果找到了,那么这个MessageEntry就包含处理函数指针,只需要调用这个函数处理就行了。这就是消息的映射。  
综合上面的消息的处理流程和消息的映射机制。对MFC的消息处理应该不陌生吧。嘿嘿,大家一起共进步吧!!!

这里也只是整理了一下下面2为大牛的学习笔记。有什么不明白直接看他们2个的学习笔记吧!!

http://blog.csdn.net/jiangnanyouzi/article/details/3722203

http://blog.csdn.net/sryan/article/details/7392814