深入浅出MFC(第九章)

来源:互联网 发布:省市区json数据下载 编辑:程序博客网 时间:2024/06/06 04:47

1、概述

Windows程序的本质是借着消息来维持脉动,每一个消息都有一个代码,并以WM_开头的常数表示,消息会循着Application Framework规定的路线,游走于各个对象之间,直到找到它的消息处理函数,找不到的话,Framework最终就把它交给::DefWindowProc函数去处理。

按理说:每一个窗口类别有它自己的窗口函数。不同窗口所获得的消息,应该由不同的窗口函数来处理,如果没有能够处理,最后再交给Windows API函数::DefWindowProc处理,但是命令绕行机制打破了这一点。

2、MFC把消息分类

命令消息(WM_COMMAND):命令消息意味着使用者命令程序做某些动作。凡是由UI对象产生的消息都是这种命令消息,可能来自菜单栏或加速键或工具栏按钮。SDK程序主要靠消息的wParam辨识,MFC程序则主要是靠菜单项目的识别码(menu ID)。凡是衍生自CCmdTarget之类别的,都能接受该消息。


标准消息:除WM_COMMAND之外,任何以WM_开头的都算是这一类,衍生自CWnd之类别的,均可以接受此消息。


Control Notification:这种消息由控制组件产生,为的是向其父窗口通知某种情况。该类消息也是以WM_COMMAND形式呈现。


消息实践:

命令消息: #define ID_CESHI   32771     //Resource.h  自动          afx_msg  void OnCeshi();     //头文件中    自动          ON_COMMAND(ID_CESHI,Onceshi);//消息映射宏中 自动 ID_CESHI  就是ID          void onceshi()               //实现函数    自动          {}                           //手动控件消息: #define IDC_BUTTON1 1000          //Resource.h  自动          afx_msg void OnBnClickedButton1();//自动          ON_BN_CLICKED(IDC_BUTTON1,OnBnClickedButton1);//ON_BN_CLICKED是ON_CONTROL(BN_CLICKED,ID,function)宏,相比命令消息多了BN_CLICKED描述 自动                           void OnBnClickedButton1()         //实现函数          {}                                //手动窗口消息:  窗口类(自身)处理→基类处理→CWnd∷DefWindowProc()处理;自定义消息:自定义窗口消息的消息标志都大于WM_USER(至少是WM_USER+100,因为许多控件都使用这一范围的WM_USER消息)           ①、在 Resource.h 中定义消息标记               #define WM_MYMSG (WM_USER+1000)   //手动           ②、实现函数fn声明               afx_msg void fn();                //手动               ③、在消息映射表中加入消息映射宏               BEGIN_MESSAGE_MAP()               ON_MESSAGE(WM_MYMSG,fn)           //手动               END_MESSAGE_MAP()           ④、编写fn实现函数               void fn(){}                       //手动          其他地方就可以发送消息          pWnd->PostMessage(WM_MYMSG, (WPARAM)p, 0) )

3、消息流动深入分析

3.1、DECLARE_MESSAGE_MAP

(在.H文件中):只是声明而已

3.2、BEGIN_MESSAGE_MAP/END_MESSAGE_MAP

(在.CPP文件中):填充结构宏

常用消息表入口宏:  
  ON_WM_XXXX:预定义的窗口消息   例如ON_WM_CHAR  ,对应WM_CHAR ,消息处理函数OnChar
  ON_COMMAND:命令  
  ON_UPDATE_COMMAND_UI:更新命令  
  ON_XXXX:控件通知  
  ON_MESSAGE:用户自定义的消息  
  ON_REGISTERED_MESSAGE:注册的窗口消息  
  ON_COMMAND_RANGE:指定ID范围的命令  
  ON_UPDATE_COMMAND_UI_RANGE:指定ID范围的更新命令  
  ON_CONTROL_RANGE:指定ID范围的控件  
  ON_NOTIFY:通知消息  
  ON_NOTIFY_RANGE:指定ID范围的通知消息  
  ...  
  实现示例:  
  #define   ON_COMMAND(id,   memberFxn)   /  
  {   WM_COMMAND,   CN_COMMAND,   (WORD)id,   (WORD)id,   AfxSig_vv,   (AFX_PMSG)&memberFxn   },  
  //   ON_COMMAND(id,   OnFoo)   is   the   same   as  
  //       ON_CONTROL(0,   id,   OnFoo)   or   ON_BN_CLICKED(0,   id,   OnFoo)  

通过以上操作得到消息映射网络(可以理解为消息到CCmdTarget之间有很多拦路虎):

3.3、所有的消息都进入AfxWndProc

窗口和AfxWndProc建立关联的过程:

    MFC中最初是以DefWindowProc为消息处理函数进行注册.当新的CWnd派生类创建时(CreateEx中),::CreateAWindowEx之前会调用AfxHookWindowCreate()设置HOOK:_AfxCbtFilterHook(),以处理窗口的激活,创建,销毁等消息._AfxCbtFilterHook等到HCBT_CREATEWND消息到来时调用_AfxStandardSubClass(),由它调用SetWindowLong()将AfxWndProc()放入窗口过程函数(偷天换日),最后是由AfxWndProc()函数替代DefWindowProc处理实际的消息.----MFC不直接注册AfxWndProc作为消息处理函数以支持3D控件,此时要保证处理过程按以下顺序调用:AfxWndProc,3D控件的WndProc,默认的DefWindowProc。

(通常消息都是停留在消息队列中等待被所隶属的窗口抓取,如果你设立了hook,就可以更早一步抓取消息,并且可以抓取不属于你的消息,送往你设定的一个所谓的滤网函数,任何窗口即将产生之前,滤网函数一定会先被调用)
 

3.4、消息流动

AfxWndProc消息处理过程  
  1.AfxWndProc处理消息时首先判断是否是WM_QUERYAFXWNDPROC,是就返回1,表示使用MFC的消息映射系统.  
  2.调用AfxCallWndProc.AfxCallWndProc在WM_INITDIALOG消息中将调用_AfxHandleInitDialog使对话框居中.AfxCallWndProc还将在线程状态中对消息对得保存,并最后调用窗口对象的窗口过程:虚函数WindowProc();  
  3.CWnd::WindowProc调用CWnd::OnWndMsg(),如返回FALSE(一般的消息,窗口消息了),则再调用CWnd::DefWindowProc();  
  4.CWnd::OnWndMsg()对应于SDK程序中的switch语句.首先它过滤特殊的消息WM_COMMAN,WM_NOTIFY,WM_ACTIVATE,WM_SETCURSOR并调用框架类对应的特殊处理函数.其它消息进入消息映射表中去查找处理函数.  

3.4.1、一般的消息

处理一般窗口消息:AfxWndProc--AfxCallWndProc--AfxCallWndProc--CWnd::WindowProc--CWnd::OnWndMsg--AfxFindMessageEntry--实际处理函数 


3.4.2、WM_COMMAND的处理

简要举例:.  


      1).第一站:虚函数CWnd::OnCommand();消息是框架类产生的,故调用框架类的OnCommand()实现.OnCommand检查表示控件的LPARAM参数,控件产生的消息会在LPARAM中包含控件窗口,对控件通知消息会调用特写处理过程.如消息是为某个控件产生的,会OnCommand在将消息直接发送给该控件后返回;否则,它保证产生命令的用户界面元素没有被禁用,然后将调用OnCmdMsg.  
      2).CFrame::OnCmdMsg().它按以下顺序查找在消息映射表中查找处理函数:活动视图,活动视图的文档,主窗口,应用程序.找到后就调用DispatchCmdMsg以执行所找到的处理函数,没找到时调用DefWindowProc.  
      3).static   BOOL   DispatchCmdMsg():根据函数签名(消息表入口项中的nSig变量)执行不同操作.一般菜单命令的签名是AfxSig_xx,会直接调用处理函数,其它签名可能要预先分解消息参数LPARAM,WPARAM;  
      函数签名的定义:  
  union   MessageMapFunctions   {  
      Afx_PMSG   pfn   ;//一般成员函数指针.  
   
      BOOL   (AFX_MSG_CALL   CWnd::*pfn_bD)(CDC*);  
      BOOL   (AFX_MSG_CALL   CWnd::*pfn_bb(BOOL);  
      .......  
      };  
      在OnWndMsg中会将此联合中的pfn设为消息处理函数的地址:mmf.pfn=lpEntry->pfn,同时,查找合适的签名,从WPARAM,LPARAM中取出必要的参数,使用与签名一致的原型调用处理函数.  


      到达框架窗口的命令路由:AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CFrameWnd::OnCommand--CWnd::OnCommand--CFrameWnd::OnCmdMsg--CCmdTarget::OnCmdMsg--DispatchCmdMsg--CMainFrame类命令处理AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CFrameWnd::OnCommand--CWnd::OnCommand--CFrameWnd::OnCmdMsg--CCmdTarget::OnCmdMsg--DispatchCmdMsg--CMainFrame类命令处理函数  
      到达文档的命令路由:AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CFrameWnd::OnCommand--CWnd::OnCommand--CFrameWnd::OnCmdMsg--CView::OnCmdMsg--CDocument::OnCmdMsg--CCmdTarget::OnCmdMsg--DispatchCmdMsg--文档类命令处理函数  
      到达视图的命令路由:AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CFrameWnd::OnCommand--CWnd::OnCommand--CFrameWnd::OnCmdMsg--CView::OnCmdMsg--CCmdTarget::OnCmdMsg--DispatchCmdMsg--视图类命令处理函数  
      到达应用程序类的命令路由:AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CFrameWnd::OnCommand--CWnd::OnCommand--CFrameWnd::OnCmdMsg--

CWinApp::OnCmdMsg--CCmdTarget::OnCmdMsg--DispatchCmdMsg--应用程序类命令处理函数  
      到达对话框类的命令路由:AfxWndProc--AfxCallWndProc--CWnd::WindowProc--CWnd::OnCommand--CWnd::OnCmdMsg--CDialog::OnCmdMsg--DispatchCmdMsg--对话框类命令处理函数  

拐弯上溯:

总体概况:


注意一下上图的理解:举个例子 兵分三路:1和2一路,3是一路,4是一路,当然有拦路虎即终结。


3.4.3、其它类型的消息

1).WM_NOTIFY. 
CWnd::ONWndMsg()用CWnd::OnNotify()来进行处理.OnNotify调用OnChildNotify()将消息送给控件. 
2).消息反射. 
可用消息反射宏来实现,以便控件自己处理特定的消息. 
3).WM_ACTIVATE. 
OnWndMsg()中调用_AfxHandleActivate()检查最高层是否是WM_ACTIVATE,是则向最高层窗口发送WM_ACTIVATETOPLEVEL消息. 
4).WM_SETCURSOR. 
在_AfxHandleSetCursor()中处理.有鼠标按下时会激活最后一个活动窗口. 

--------------------------------- 
PreTranslateMessage----消息预处理 

共两个入口:CWinApp::PreTranslateMessage,CWnd::PreTranslateMessage. 
在消息由TranslateMessage()和DispatchMessage()处理前,CWinApp::Run()调用CWinApp::PreTranslateMessage,然后,CWinApp::PreTranslateMessage会从消息结构中对指定的目标窗口及应用程序的主窗口调用每个窗口的CWnd::Translatemessage(). 
预处理过程返回TRUE,则消息不再进行后继处理.  




原创粉丝点击