MFC消息机制

来源:互联网 发布:化学方程式书写软件 编辑:程序博客网 时间:2024/05/22 03:20

MFC消息机制

开场扯会儿淡,话说前面我们已经粗略的学习了Windows消息机制,学的好不好只有天知、地知、自己知!!OK,那么今天就让我们一起再来学一学MFC的消息机制,因为菜鸟想变成老鸟就得不断的学习。嗯啊,两者同样是机制差别还是蛮大滴,就像人与人的差别(好了,越扯越远,回到正题喽!!)

一、让我们先来瞅瞅MFC的映射机制到底是啥玩意儿。。。。。

MFC消息映射机制的实现方法:在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的,也就是他们是绑定在一起的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态对照表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可以知道该类能否处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。

  初看MFC中的各种消息,以及在头脑中根深蒂固的C++的影响,我们可能很自然的就会想到利用C++的三大特性之一:虚拟机制来实现消息的传递,但是经过分析,我们看到事情并不是想我们想象的那样。
  为什么呢?我们也许知道或是不知道,MFC中类的派生层次很多。然而在C++中系统对程序中用到的每一个派生类都要有一个vtable,并且不管基类中的虚函数是否确实在派生类中被重写,但是每一个虚函数在vtable中又都要占用一个4字节大小的入口地址。这样一来,MFC类以及其派生类就开始背着一个很大的虚拟函数表的包袱,这对内存资源是一种很可耻的浪费,有悖于现代编程理念。所以这样做显然是不合适的。。。。
  当然,如果说上面的窗口或控件还可以勉强实现的话,并且这种做法也能够被我们接收。那么,我们通过先前学习知道Windows消息大体上可以分为三类(窗口、命令、控件通知),那么对于命令消息及控件通知消息呢?因为不同的应用程序有不同的菜单和按钮,我们怎么处理呢?在MFC 库的这种消息映射系统就避免了使用大的vtable,并且能够在处理常规Windows消息的同时处理各种各样的应用程序的命令消息。
  说白了,MFC中的消息机制其实质是一张巨大的消息及其处理函数的一一对应表,然后加上分析处理这张表的应用框架内部的一些程序代码.这样就可以避免在SDK编程中用到的繁琐的CASE语句。


二、说多了都是唾沫星子,Let's 举个例子耍耍!!!

    假设我们给视类添加一个鼠标左键的消息响应函数,MFC会相当自动的在一下三处为添加代码(这也是MFC的强大之处,大大的减轻了编程人员的负担):

1)在头文件中(.h文件

// 生成的消息映射函数

protected:

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnLButtonDown(UINT nFlags,CPoint point);//afx_msg宏表示声明的是一个消息响应函数。

2)在源文件中(.cpp文件

BEGIN_MESSAGE_MAP(CMFCTestView, CView)

// 标准打印命令

ON_COMMAND(ID_FILE_PRINT,&CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT,&CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW,&CView::OnFilePrintPreview)

ON_WM_LBUTTONDOWN()

END_MESSAGE_MAP()

   在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间定义了消息映射表。宏ON_WM_LBUTTONDOWN()会把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理消息。

#define ON_WM_LBUTTONDOWN() \

{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \

(AFX_PMSG)(AFX_PMSGW) \

(static_cast< void (AFX_MSG_CALLCWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },

3)在源文件中(.cpp文件

   void CMFCTestView::OnLButtonDown(UINT nFlags, CPoint point)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

   MessageBox("View Clicked");

  CView::OnLButtonDown(nFlags, point);

}

    通过上面的过程我们可以知道,一个MFC消息响应函数在程序中有三处相关信息:函数原型,函数实现,以及用来关联消息和消息响应函数的宏。


三、入龙潭虎穴只是为了更深入的理解!!!


MFC消息机制的实现原理MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里的资源(消息参数,可获得窗口句柄,查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给应用程序框架窗口类的基类,基类利用这个指针调用WindowProc()函数(位于WinCore.cpp文件中)对消息进行处理。

现在我把这个过程简单的通过下面的关系图给描述一下:


收到一个消息-------》根据消息的第一个参数确定其与哪一个窗口句柄相关

--------》查找那个句柄和C++指针的对照表-------》找到相关的C++对象指针

---------》将该指针传递给应用程序框架窗口类的基类-

--------》基类调用WindowProc函数-------》WindowProc调用OnWndMsg函数

---------》OnWndMsg处理过程:a.首先判断收到的消息是否有相关的消息处理函数

      b.若是存在消息处理函数,那么就调用该处理函数;

 若没有就交给基类处理。



上面我们说过C++对象指针,那么它究竟是什么玩意儿呢?其实C++对象指针即指CMFCTestView*,窗口句柄与CMFCTestView对象的指针CMFCTestView*存在一一对应的关系。

好了,我觉得我们此刻有必要来学习一下MFC消息的处理过程。。


MFC下的消息处理流程

1、MFC下的消息处理流程由.cpp文件中的AfxInternalPumpMessage开始:

[cpp] view plaincopy
  1. BOOL AFXAPI AfxInternalPumpMessage()  
  2. {  
  3.     MSG msg;  
  4.     ::GetMessage(&msg, NULL, NULL, NULL);  
  5.     if (!AfxPreTranslateMessage(&msg))  
  6.     {  
  7.         ::TranslateMessage(&msg);  
  8.  ::DispatchMessage(&msg);  
  9.     }  
  10.     return TRUE;  
  11. }  

注:以上代码为示意代码,具体请参照MFC的源码。

    可以看出来,其消息处理流程也类似先前学过的Win32下的消息处理过程,只不过是在调用TranslateMessage、DispatchMessage处理消息前增加了类似过滤的函数AfxPreTranslateMessage。该函数会调用CWnd类的PreTranslateMessage函数,函数返回True则消息将不会被处理。我们经常会通过重载CWnd类的PreTranslateMessage来改变MFC的消息控制流程。
2、窗口过程函数
    通过调用DispatchMessage将消息分发给了具体的窗口过程函数处理。MFC下的所有窗口都拥有公用的窗口过程函数AfxWndProc。该函数的示意代码如下:

[cpp] view plaincopy
  1. LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.         CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);  //从HWND获取对应的CWnd*  
  4.         if (pWnd == NULL || pWnd->m_hWnd != hWnd)  
  5.                 return ::DefWindowProc(hWnd, nMsg, wParam, lParam);  
  6.         else      
  7.                 return pWnd->WindowProc(nMsg, wParam, lParam);  
  8. }  

    很显然,调用了CWnd类的虚函数virtual CWnd::WindowProc处理。

[cpp] view plaincopy
  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. }  

    WindowProc函数又调用了CWnd类的虚函数virtual CWnd::OnWndMsg处理。

[cpp] view plaincopy
  1. BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)  
  2. {  
  3.     LRESULT lResult = 0;  
  4.     union MessageMapFunctions mmf;  
  5.     mmf.pfn = 0;  
  6.     CInternalGlobalLock winMsgLock;  
  7.     // special case for commands  
  8.     if (message == WM_COMMAND)  
  9.     {  
  10.         if (OnCommand(wParam, lParam))  
  11.         {  
  12.             lResult = 1;  
  13.             goto LReturnTrue;  
  14.         }  
  15.         return FALSE;  
  16.     }  
  17.   
  18.     // special case for notifies  
  19.     if (message == WM_NOTIFY)  
  20.     {  
  21.         NMHDR* pNMHDR = (NMHDR*)lParam;  
  22.         if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))  
  23.             goto LReturnTrue;  
  24.         return FALSE;  
  25.     }  
  26.       
  27.     ......  
  28.   
  29.     return TRUE;  
  30. }  
    在OnWndMsg函数中会根据具体的消息类型,在MFC的消息映射表中找到对应的函数处理。
    以上就是MFC处理消息的大致流程。
下面是两个函数的函数原型:

virtual LRESULT    WindowProc(UINT message,WPARAM wParam, LPARAM lParam);

virtual BOOL          OnWndMsg(UINT message, WPARAMwParam, LPARAM lParam, LRESULT* pResult);

四、尾语:咬牙坚持,不断学习,明天GO ON!!

原创粉丝点击