深入浅出MFC学习笔记 消息

本文详细解析MFC的消息映射和命令传递机制,包括消息的传递路线、窗口类与窗口函数的关系、命令消息与通知消息的区别、消息映射表的使用以及消息处理函数的类型。通过理解这些机制,读者可以更深入地掌握MFC窗口程序的底层工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章将会对MFC的消息映射和 命令传递机制做深入探讨。

    MFC规定了消息传递的路线,消息会按照这个路线传递下去,找不到归宿的话就交给DefWindowProc。

    在产生窗口之前,如果我们在创建窗口时指定的窗口类为NULL时,MFC会自动注册五个默认的窗口类,每个窗口类有自己的窗口函数。不同窗口得到的消息应该交由不同的窗口函数来处理。所谓的命令传递机制是为了让消息的流动有线路可循,实现一个巨大的网,实现所有可能的路线。这就是所谓的消息映射图。

    WM_COMMAND是来自菜单或工具栏,被称为命令消息。wParam记录着这一消息来自哪个菜单项目。

   除了命令消息控件传给父窗口的消息,也是以WM_COMMAND类型的,但是它们被称为notification消息。

    Command target即命令的目的地。因此所有派生自CCmdTarget类的类都可以处理命令消息。

    派生自CWnd的类可以处理一般的windows消息和COMMAND消息。

    DECLARE_MESSAGE_MAP()

    BEGIN_MESSAGE_MAP

      ON_COMAMND()

    END_MESSAGE()

    这些宏实现了消息和消息处理函数的映射,不再介绍哦。

    在BEGIN和END之间除了ON_COMMAND之外,还可以有其他的形式,标准的windows消息并不需要我们指定处理函数的名称。如:

   ON_WM_CHAR();WM_CHAR     OnChar

   ON_WM_CLOSE();   WM_CLOSE   OnClose

   ON_WM_CREATE();  WM_CREATE  OnCreate

这些宏MFC已经定义好,将标准的消息和消息处理函数对应起来。

AFX_DAT,AFX_DATADEF,AFX_MSG_CALL和afx_msg一样现在还没有被使用。

    虽然所有继承自CCmdTarget的类都可以处理命令消息,都应该有DECLARE_MESSAGE_MAP。。。但是CWinThread却没有。它不参与消息处理。因此CWinApp在填写消息映射表是就会跳过CWinThread。

    DECLARE_MESSAGE_MAP(CWinApp,CCmdTarget);

   消息映射表有C++多态的味道,但是MFC却没有使用多态来实现它,究其原因估计是多态会导致很大的负担。

    前面的介绍中,以AfxWinProc作为起点,此处MFC仍以此作为开始,但是却省略了很多的细节。通常消息是在消息队列中等待窗口抓取,但是MFC安装了钩子,就可以提前抓取或抓取不是自己的消息。这是在所有派生自CWnd的对象创建之际发生的。如:

Bool CWnd::CreateEx()

{

。。。

 AfxHookWindowCreate(this);

HWND hWnd=::CreateWindowEx();

...

}

AfxHookWindowCreate安装了WH_CBT类型的钩子,任何窗口显示之前,注册的钩子函数会被调用。

    细节不再深入介绍,只要知道以上这些干了些偷天换日的勾当,把注册窗口类时注册的窗口函数,更换为AfxWndProc。于是AfxWndProc便是DispatchMessage的目的地。之所以弄得这么复杂书上说是为了兼容什么3D constrols。不懂,暂且放这儿吧。记住结论就是了。

上面可以知道AfxWndProc是消息的源头,这在前面几张也提到过。在此函数内部会调用AfxClassWndProc,后者会调用pWnd->WindowProc();在整个MFC中拥有windowProc的类有CWnd,CControlBar,COleControl,CDialog......具体调用谁的WindowProc要看pWnd 的类型。

在CWnd::windowProc中会调用OnWndMsg函数,它是用来分辨并处理消息的专职机构。如果是命令就交给OnCommand处理,如果是通知消息就交给OnNotify处理。之所以要区分命令消息和通知消息是因为它们的上溯路径并不是单纯的只往父类上去,可能有其他路径。而一般的windows消息却沿着直线上溯。

在OnCommand又调用了OnCmdMsg函数。具体调用哪个OnCommand,OnCmdMsg也是不定的,也得看this指针的类型。OnCmdMsg它是专门处理命令消息的函数。

bool CFrameWnd::OnCmdMsg(UINT nID,int nCode)

{

     CView*pView=GetActiveView();

   if(pView->OnCmdMsg(nID,nCode))//处理则返回否则继续传递。

      return true;

   if(CWnd::OnCmdMsg(nID,nCode))

     return true;

   CWinApp*pApp=AfxGetApp();

   if(pApp->OnCmdMsg(nID,nCode)

       return true;

    return fasle;

}

bool CView::OnCmdMsg(UINT nID,int nCode)

{

  cout<<"CView::OnCmdMsg()"<<endl;

   if(CWnd::OnCmdMsg(nID,nCode))

     return true;

   bool bHandled=false;

   bHandled=m_pDocument->OnCmdMsg(nID,nCode);

   return bHandled;

}

Bool CDocument::OnCmdMsg(UINT nID,int nCode)

{

    cout<<"CDocument::OnCmdMsg()"<<endl;

    if(CCmdTarget::OnCmdMsg(nID,nCode))

     return true;

    return false;

}

以上就是消息的传递路线,这里跟第三章模拟的命令传递路线是相同的,具体可以第三章。

OnCmdMsg是各类专门对付命令消息的函数。本类处理过后又会调用下一个类的OnCmdMsg,这样命令消息就会不断传递下去。

   如果MDI主窗口接收到一个命令消息,主窗口会把消息传递给CMDIChildWnd子窗口。子窗口传递给它对应的视图,视图检查自己的消息映射表,如果没有找到对应项,将其传递给document。Document检查自己的消息 映射表,找到对应项则调用该函数,消息传递 结束。如果没有找到继续传递,document把该消息传递到document template对象。如果还是没有找到,则传回到view,view将其传递给MDI子窗口本身。继续传递给CWinApp,交给 默认的消息处理函数。

在定义CRuntimeClass时,MessageMapEntries内定义了消息函数的指针类型,

typedef void(AFX_MSG_CALL 

                     CCmdTarget:::*AFX_PMSG)(void);

此时消息处理函数指针类型为返回值为空,参数为空,这是不符合实际的,实际使用中会经过类型转换,使其可以接收参数和返回值。

由于消息处理函数的类型各异,MFC使用了AfxSig_来说明消息处理函数的类型。在找到某消息的消息处理函数之后,判断其类型再进行响应转换。

union MessageMapFunctions mmf;

 mmf.pfn=lpEntry->pfn;

switch(lpEntry->nSig)

{

  case AfxSig_isg:

       lResult=(this->*mmf.pfn_is)(LPTSTR)lParam);

       break;

   case Afx_Sig_lwl:

       lResult=(this->*mmf.pfn_lwl)(wParam,lParam);

       break;

   case AfxSig_vv:

      (this->*mmf.pfn_vv)();

      break;

    ........

}

AfxSig_is代表参数为LPTSTR字符串,返回值为int.

Afx_lwl代表参数wiewParam和lParam。返回值为LRESULT。

Afx_vv代表参数和返回值都为void.

(this->*mmf.pfn_vv)();中的pfn_vv是union MessageMapFunctions的一个成员。如

union MessageMapFunctions

{

   AFX_PMSG  pfn;

   bool (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);

   void (AFX_MSG_CALL CWnd::*pfn_VV)(CDC*);

   .............

};

注意MessageMapFunctions是union类型的哦。

真是佩服当年MFC开发人员的智慧!!!!!

转载于:https://www.cnblogs.com/jiangzhaowei/p/5098886.html

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值