MFC源码分析实战(五)——层层封装下的消息循环

5 MFC 设置。 阅读详情

消息循环

至此,我们还有最后一站——消息循环
为此,当程序调试运行到CWnd::CreateEx尾部时,对GetMessage/PeekMessage下条件断点,以窗口句柄为条件:
很快就断下来了:
这里写图片描述
在AfxWinMain中,InitInstance完成后,就开始执行Run函数:
这里写图片描述
Run又是一个虚函数,CWinApp类中override了它,因此进入CWinApp::Run:
这里写图片描述
在其中又调用了CWinThread::Run,其中调用了PeekMessage以PM_NONREMOVE的方式偷看消息队列,并在空闲时执行OnIdle虚函数;如果不空闲,则进入 CWinThread::PumpMessage()函数,该函数又调用了AfxInternalPumpMessage函数:

BOOL AFXAPI AfxInternalPumpMessage()
{
    _AFX_THREAD_STATE *pState = AfxGetThreadState();

    if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
    {
        return FALSE;
    }
  // process this message

    if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
    {
        ::TranslateMessage(&(pState->m_msgCur));
        ::DispatchMessage(&(pState->m_msgCur));
    }
  return TRUE;
}

基本上是一个标准的消息循环了。不过注意在GetMessage后有一个AfxPreTranslateMessage,并且可以根据其返回值决定是否翻译并分发消息(如果不分发消息的话,根本就没回到user32领空,因此AfxWndProc根本就收不到该消息)

BOOL __cdecl AfxPreTranslateMessage(MSG* pMsg)
{
  CWinThread *pThread = AfxGetThread();
  if( pThread )
    return pThread->PreTranslateMessage( pMsg );
  else
    return AfxInternalPreTranslateMessage( pMsg );
}

BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
    ASSERT_VALID(this);
    return AfxInternalPreTranslateMessage( pMsg );
}

BOOL AfxInternalPreTranslateMessage(MSG* pMsg)
{
//    ASSERT_VALID(this);

    CWinThread *pThread = AfxGetThread();
    if( pThread )
    {
        // if this is a thread-message, short-circuit this function
        if (pMsg->hwnd == NULL && pThread->DispatchThreadMessageEx(pMsg))
            return TRUE;
    }

    // walk from target to main window
    CWnd* pMainWnd = AfxGetMainWnd();
    if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
        return TRUE;

    // in case of modeless dialogs, last chance route through main
    //   window's accelerator table
    if (pMainWnd != NULL)
    {
         CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
         if (pWnd->GetTopLevelParent() != pMainWnd)
            return pMainWnd->PreTranslateMessage(pMsg);
    }

    return FALSE;   // no special processing
}

这其中,最重要的是CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg)

BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
{
    ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));
    ASSERT(pMsg != NULL);

    // walk from the target window up to the hWndStop window checking
    //  if any window wants to translate this message

    for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
    {
        CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
        if (pWnd != NULL)
        {
            // target window is a C++ window
            if (pWnd->PreTranslateMessage(pMsg))
                return TRUE; // trapped by target window (eg: accelerators)
        }

        // got to hWndStop window without interest
        if (hWnd == hWndStop)
            break;
    }
    return FALSE;       // no special processing
}

其从消息所属窗口开始,到其父窗口,一直到主窗口,以此调用其PreTranslateMessage,直到找到一个返回TRUE的,就返回TRUE,大家都不处理,则返回FALSE。这里,从hWnd到CWND*的转换又是通过CWnd::FromHandlePermanent完成的。

CWnd::OnWndMsg中的小宇宙

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    LRESULT lResult = 0;
    union MessageMapFunctions mmf;
    mmf.pfn = 0;
    CInternalGlobalLock winMsgLock;
    // special case for commands
    if (message == WM_COMMAND)
    {
        if (OnCommand(wParam, lParam))
        {
            lResult = 1;
            goto LReturnTrue;
        }
        return FALSE;
    }

    // special case for notifies
    if (message == WM_NOTIFY)
    {
        NMHDR* pNMHDR = (NMHDR*)lParam;
        if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
            goto LReturnTrue;
        return FALSE;
    }

    // special case for activation
    if (message == WM_ACTIVATE)
        _AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));

    // special case for set cursor HTERROR
    if (message == WM_SETCURSOR &&
        _AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))
    {
        lResult = 1;
        goto LReturnTrue;
    }

   // special case for windows that contain windowless ActiveX controls
   BOOL bHandled;

   bHandled = FALSE;
   if ((m_pCtrlCont != NULL) && (m_pCtrlCont->m_nWindowlessControls > 0))
   {
      if (((message >= WM_MOUSEFIRST) && (message <= AFX_WM_MOUSELAST)) ||
         ((message >= WM_KEYFIRST) && (message <= WM_IME_KEYLAST)) ||
         ((message >= WM_IME_SETCONTEXT) && (message <= WM_IME_KEYUP)))
      {
         bHandled = m_pCtrlCont->HandleWindowlessMessage(message, wParam, lParam, &lResult);
      }
   }
   if (bHandled)
   {
      goto LReturnTrue;
   }

    //否则是普通消息,垂直向上传递即可,具体代码见上

其中我们可以看到,ON_COMMAND消息被区别对待了,在CWnd类中,有一个名为OnCommand的虚函数专门处理此类消息。而CFrameWnd中重载了该函数,CView类中没有重载。因此

CFrameWnd类的对象,调用的时CFrameWnd::OnCommand,该函数沿CFrameWnd的消息映射族谱,按正常消息向上查找

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
    // return TRUE if command invocation was attempted
{
    HWND hWndCtrl = (HWND)lParam;
    UINT nID = LOWORD(wParam);

    CFrameWnd* pFrameWnd = GetTopLevelFrame();
    ENSURE_VALID(pFrameWnd);
    if (pFrameWnd->m_bHelpMode && hWndCtrl == NULL &&
        nID != ID_HELP && nID != ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP)
    {
        // route as help
        if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))
            SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
        return TRUE;
    }

    // route as normal command
    return CWnd::OnCommand(wParam, lParam);
}

该类中对帮助按钮所发出的COMMAND消息进行了特别处理,如果不是此类消息,则调用CWnd::OnCommand
CView类并未实现OnCommand方法,故与CWnd类的对象一样,调用的是CWnd::OnCommand。这样,除了CFrameWnd中的特殊帮助类消息外,所有ON_COMMAND消息又殊途同归了。

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
    // return TRUE if command invocation was attempted
{
    UINT nID = LOWORD(wParam);
    HWND hWndCtrl = (HWND)lParam;
    int nCode = HIWORD(wParam);

    // default routing for command messages (through closure table)

    if (hWndCtrl == NULL)
    {
        //特殊处理句柄为NULL的
    }
    else
    {
        // control notification
        ASSERT(nID == 0 || ::IsWindow(hWndCtrl));

        if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
            return TRUE;        // locked out - ignore control notification

        // reflect notification to child window control
        if (ReflectLastMsg(hWndCtrl))
            return TRUE;    // eaten by child

        // zero IDs for normal commands are not allowed
        if (nID == 0)
            return FALSE;
    }
    return OnCmdMsg(nID, nCode, NULL, NULL);
}

关键是在最后调用了虚函数OnCmdMsg,不同类型不同的处理函数。
先看看CFrameWnd::OnCmdMsg

BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
{
    CPushRoutingFrame push(this);

    // pump through current view FIRST
    CView* pView = GetActiveView();
    if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
        return TRUE;

    // then pump through frame
    if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
        return TRUE;

    // last but not least, pump through app
    CWinApp* pApp = AfxGetApp();
    if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
        return TRUE;

    return FALSE;
}

可以看出,框架类的OnCmdMsg依次调用了CView::OnCmdMsg、CWnd::OnCmdMsg(未override,为CCmdTarget::OnCmdMsg)
而CView::OnCmdMsg又依次调用CWnd::OnCmdMsg(未override,为CCmdTarget::OnCmdMsg)和CDocument::OnCmdMsg。其中CCmdTarget::OnCmdMsg是从CMyView->CView->CWnd->CCmdTarget的顺序找其消息映射表。
如果没找到的话,就返回,由CDocument::OnCmdMsg继续查找:CMyDoc->CDocument->CCmdTarget
如果还是没找到的话,CView::OnCmdMsg返回FALSE,由CWnd::OnCmdMsg(CCmdTarget::OnCmdMsg)接手:
CMyFrameWnd->CFrameWnd->CWnd->CCmdTarget
如果仍然没有找到,则由pApp->OnCmdMsg接手:
CMyWinApp->CWinApp->CCmdTarget
全剧终~

全面解析MFC源码:深入学习与应用 本文还有配套的精品资源,点击获取 简介:《深入浅出MFC源代码》是为程序员深入理解并掌握Microsoft Foundation Classes (MFC)编写的书籍。MFC是微软提供的C++类库,主要用于Windows应用开发,尤其在桌面领域应用广泛。本书通过详尽的源码分析,使读者能够理解MFC核心原理,提升开发效率和程序质量。MFC的架构包括框架类、文档/视图架构、控件... 阅读详情

相关推荐

掌握MFC核心技术与实战源码分析

MFC(Microsoft Foundation Classes)是微软公司为了简化Windows应用程序的开发而推出的一套C++类库。自1992年推出以来,MFC已成为Windows平台上最受欢迎和最常用的开发框架之一。它封装了Windows API(应用程序编程接口),将复杂的底层通信简化为对象操作,极大地提高了开发效率和可维护性。MFC提供了一系列的控件类,用于处理用户界面的各个方面。以下是一些最常用的控件类:CButton:用于创建按钮控件,可以是命令按钮、复选框、单选按钮等。

weixin_35756690的博客 836

OnCommand的解释

OnCommand(WPARAM wParam,LPARAM lParam); 悬赏分:50 - 解决时间:2005-12-9 16:01参数是什么意思? 我用VC++编程,他是CWND的成员函数OnCommand是响应WM_COMMAND消息的,一般是响应控件和菜单的命令消息时使用。 在使用LRESULT CALLBACK WndProc(HWND hWnd,UINT nMsg, WPA

Danisの一棵树的软件历程 3177

VS2010 MFC实现类似QQ消息弹框功能源码分析

为了提高开发效率,掌握VS2010的一些基本操作和快捷键非常有用。例如,使用Ctrl+S保存文件,Ctrl+C和Ctrl+V进行复制和粘贴,F5开始调试,Ctrl+F查找代码等。快捷键可以帮助你快速执行常见的开发任务,减少鼠标点击次数,提升工作效率。MFC(Microsoft Foundation Classes)是一套由微软公司提供的C++类库,旨在简化Windows应用程序的开发。

weixin_35886636的博客 1047

MFC深入浅出--命令传递

  Command Routing (命令传递) MFC对于消息循环的规定:#若是一般的Windows消息(WM_xxx),则一定是由派生类流向基类,没有旁流的可能。#若是命令消息WM_COMMAND,就有奇特的路线了。 不管规则是怎么制定的。现在需要一个推动引擎。全局函数AfxWndProc就是推动引擎的起始点。它原本在CwinThread::Run()中被调用。但为了实验目的,

armman的专栏 1429

MFC网络编程实战:打造仿QQ聊天室

CSocket类是MFC(Microsoft Foundation Classes)库中用于网络通信的一个类。它基于Winsock API封装,为开发者提供了一个更为便捷和面向对象的网络编程接口。CSocket类通过封装底层的socket API,简化了网络编程的过程,使得开发者可以不用深入了解底层网络协议的细节,即可实现基本的网络通信功能。CSocket在内部使用SOCKET数据类型来创建一个网络连接,并对这个连接进行操作。它提供了同步和异步两种通信模式。

weixin_35973118的博客 1089

MFC图形绘制项目实战与源码分析

MFC库,即Microsoft Foundation Classes,为开发者提供了一个封装Windows API的框架,极大地简化了Windows应用程序的开发过程。它允许开发者使用C++进行面向对象编程,同时提供了一套标准的GUI组件,如窗口、按钮、编辑框等,以及文档/视图架构,以便构建结构化的应用程序。OnDraw()是CView类中的一个虚函数,用于定义视图绘制的逻辑。每当视图需要被重绘或更新时,MFC框架会调用OnDraw()函数。

weixin_42327217的博客 1021

【MFC】06.MFC六大机制:窗口创建机制-笔记

接上文【MFC】05.MFC第一大机制:程序启动机制-笔记,这一篇文章来带领大家逆向分析MFC第二大机制:窗口创建机制的源码。 我们知道,在Win32编程中,如果我们要创建一个窗口,基本步骤为: 注册窗口 创建一个窗口,必须要给一个类名称 消息处理回调函数

WdIg-2023的博客 1417

深入探索MFC:Windows桌面应用程序开发实战及源码分析

MFC是一个为Windows API提供封装的C++库,它把底层的Windows编程接口转换成了更易理解和使用的类层次结构。MFC的应用程序框架减少了重复编码的工作量,同时提供了一些标准用户界面元素和功能,比如窗口、控件和绘图工具,这使得开发人员可以专注于业务逻辑的实现,而非底层的细节。虽然标准控件可以满足大部分需求,但在某些情况下可能需要自定义控件以达到特定的用户交互效果。MFC允许开发者通过继承和扩展现有控件或从CWnd派生新控件来实现自定义控件。继承现有控件。

weixin_42594427的博客 558

菜单命令消息路由过程分析

本文详细描述了MFC中菜单命令路由过程分析

Keep Moving~ 2019

基于MFC的Windows扫雷游戏源码分析与实践

MFC(Microsoft Foundation Classes)是一个C++类库的集合,封装了Windows API(应用程序编程接口),简化了Windows程序的开发。MFC用于创建图形用户界面(GUI),并且支持多文档界面(MDI)和单文档界面(SDI)应用程序。扫雷游戏是一个经典的逻辑思维游戏,玩家需要在一个格子布局中找出所有没有地雷的格子,同时避免触雷。该游戏在MFC的框架下开发,能有效展示MFC在Windows应用程序中的应用。

weixin_32480007的博客 430

C++与MFC结合的Windows钩子技术源码分析与实战

在Windows操作系统中,钩子技术是编程中用于监视系统或应用程序中的事件消息传递的机制。它允许开发者在消息被传递到目标窗口过程之前截获并处理消息。这种技术在开发全局热键、键盘监听、系统监控以及自定义输入法等方面有着广泛的应用。钩子(Hook)技术是Windows编程中一种广泛应用于事件处理的机制。它允许开发者截获系统或应用程序中的特定事件,并允许在事件到达目标窗口过程之前对其进行修改、处理或截断。

weixin_35751412的博客 853

MFC源码实战分析(三)——消息映射原理与消息路由机制初探

如果在看完上一篇文章后觉得有点晕,不要害怕。本节我们就不用这些宏,而是用其中的内容重新完成开头那个程序,进而探究MFC消息映射的本来面目。 MFC消息映射机制初探 还我本来面目 class CMyWnd : public CFrameWnd { public: //原有代码 void OnLButtonDown(UINT nFlags, CPoint point)

LPWSTR的博客 1299

MFC源码剖析之--MFC来龙去脉

这个是本人在xue学习vc时研究了很长时间的MFC掩码剖析

MFC程序消息流程

记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断不下来,在系统模块中经常转得晕头转向,而一无所获。MFC程序是一种常见类型的程序,我静下心来,潜心研究了一下MFC消息流程。弄清原委之后,一切豁然开朗,发现跟踪MFC程序和消息处理原来是如此。。。,跟踪按钮事件处理也由此变得特别简单。 于是,我将这些研究整理成文,以备后忘。并希望对和我一样

lwglucky的专栏 1306

Postman调试海康ISAPI接口全记录:从鉴权失败到成功修改设备名的踩坑指南

本文详细记录了使用Postman调试海康ISAPI接口的全过程,从鉴权失败到成功修改设备名的实战经验。涵盖Digest认证机制、XML请求体构造、常见错误排查及代码实现过渡,帮助开发者高效解决海康设备集成中的各种问题。

weixin_42620913的博客 666

如何把DeepSeek这个奈斯工具接入到Pycharm里

DeepSeek接入到Pycharm中,提高编程效率,以及帮助解决遇到的问题。

学无止境,何以言满 2624

MFC源码实战分析(二)——消息响应与处理机制初探

在上一节中,我们借助MFC框架中的两个类,编写了一个Windows窗口应用程序。本节我们来探究MFC的消息映射机制,在上一节程序的基础上增加响应鼠标WM_LBUTTONDOWN的消息。 增加消息处理能力 Win32原始消息机制 在此之前,我们先来回忆一下Win32SDK程序中与消息处理相关的部分: WinMain函数中,注册窗口类时,传入了窗口过程WndProc的地址。然后创建窗

LPWSTR的博客 812

别再问“这游戏啥引擎做的”了,手把手教你用SteamDB一键查询(附实战案例)

本文详细介绍了如何使用SteamDB一键查询游戏引擎信息,包括Unity 3D、Unreal Engine等热门引擎的识别方法。通过实战案例和高级查询技巧,帮助玩家快速获取游戏技术底细,优化游戏体验和兼容性分析。

weixin_31412781的博客 833

透过宏定义了解MFC的消息响应机制

  消息系统是MFC的重要组成部分。MFC的消息响应机制并不复杂,而且MFC的开发环境 Visual Studio来供了非常好的自动化工具,自动生成代码。MFC也定义了丰富的宏来简化消息响应的代码。这使得很多初学者都能快速开发出基于消息响应机制的应用程序。然后也正是这些带来方便的宏,使很多人摸不着头脑。当不小心的代码操作使自动化工具不好用的时候,看着那一组更像是一堆的宏,很多人只好一头雾水。

4076

40岁从智驾转具身智能之路

我的工作经历比较复杂,本科毕业后工作2年再次读研的,本科和硕士都是机械专业,硕士毕业后干了5年CAM和机器人再转入到自动驾驶行业。在自动驾驶干了8年,发现自动驾驶已经到了一个瓶颈,再次转行做具身智能。我就谈谈自己的最近这次转型之路。

hzm8341的博客 642

高效IT运维报告编写指南:实战经验与模板分享

本文提供了一份全面的IT运维报告编写指南,涵盖实战经验与实用模板分享。文章详细解析了高效运维报告的核心要素,包括结构设计、数据收集自动化和可视化技巧,并提供了月度报告和故障分析报告模板。通过自动化工具和写作技巧,帮助团队提升报告质量,支持决策和问题追踪。

u5v6w7的博客 930

VC++/MFC消息映射机制(3):MFC消息路由的源码分析

VC++/MFC消息映射机制(3):MFC消息路由的源码分析 若对C++语法不熟悉,建议参阅《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解。 注意:本文最好结合本人所作前两篇与消息映射机制有关的文章一起阅读。 注意:消息路由包含了消息映射功能 1、MFC源码的消息映射宏,把模仿程序中的消息映射数组ss和消息映射表...

黄邦勇帅的博客 1195

《探秘 BME680温度湿度气压气体传感器模块:环境感知的卓越之选》:此文为AI自动生成

BME680 是一款由博世公司推出的集成环境传感器模块,在环境监测和物联网等领域中发挥着重要作用。它能够同时测量温度、湿度、气压和气体浓度等多个环境参数,为各种应用提供准确可靠的环境数据。该模块采用小巧的封装形式,便于集成到各种设备中,无论是智能家居、可穿戴设备还是工业监测系统,都能轻松适配。例如,在智能家居系统中,BME680 模块可以监测室内环境参数,如温度、湿度、气压和空气质量等。通过与智能家居控制器或智能手机应用程序的连接,用户可以实时了解室内环境状况,并根据需要进行调整。

zheng_ruiguo的专栏 1874
上一篇: MFC源码实战分析(四)——hWnd与CWnd之千里情缘一线牵
下一篇: C++中lambda表达式详解与原理分析
LPWSTR
博客等级 码龄10年 73粉丝 47原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值