小识MFC,一套设计优雅与不优雅并存的类库----小话MFC(2)

Q1: CPoint继承于POINT,这样有什么好处?

A: 继承的一个最基本的好处当然就是减少代码量。CPoint和POINT内部数据一样,只是一个提供了更多的方法来操作对象。

typedef struct tagPOINT
{LONG  x;LONG  y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
class CPoint :public tagPOINT

这样的方式,很自然,CPoint不用再单独加入成员x,y, 而且函数参数可以很自然的实现从派生类到基类的转换,是个不错的设计。

Q2: CDocument, CView实现界面展示、变化以及用户操作UI是MVC架构吗?

A: 看起来有点像,实际并不是纯正的MVC设计。CDocument的设计更多考虑了windows操作系统MDI文档视图,它希望提供一个MDI中每个视图的模板原型。

CView主要实现显示,不过对于用户和UI的交互,MFC架构中并没有提供框架为MVC的控制器来单独考虑,它将控制器放入了CView, CDocument甚至CWinApp中。

这听起来并不是一个很好的设计,但是实际上,视图和操作视图放在一起也并不是一个万恶不赦的设计, 因为UI本来就改来改去,程序员还是可以接受这样的方式。

Q3: CWnd类内部如此多的成员函数,这样设计合理吗?

A: CWnd主要处理一个窗口的显示,包括窗口标题、最大化最小化、内部子控件获取等。不过ms的设计,将此类内部加入了太多和CWnd关系不是很大的东西,导致了此类成员很多,弊端不用说了,此类不是一个优秀设计,它处理了太多不该去处理的东西,使得整个类库设计清晰度降低;

不过,也有一定优点,很多在主框架或者view中可以直接调用它的成员函数,不用花心思再去想需要调用的函数出自哪个类。

Q4: 使用MFC向导创建的应用程序,里面的消息处理流程很复杂,如何很好地查看消息流?

A: 函数堆栈是查看它的很好方式。

如上,是一个使用MFC app wizzard创建的SDI应用程序basic_mfc.exe开始运行后的调用堆栈。

可以看出,应用程序开始运行后,会调用应用程序类的ProcessShellCommand解析命令行参数,此过程可能就进入了消息处理过程(比如,一个应用程序刚打开,默认的处理是打开一个新文档),如下是ProcessShellCommand的部分代码:

	case CCommandLineInfo::FileNew:if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))OnFileNew();if (m_pMainWnd == NULL)bResult = FALSE;break;// If we've been asked to open a file, call OpenDocumentFile()case CCommandLineInfo::FileOpen:if (!OpenDocumentFile(rCmdInfo.m_strFileName))bResult = FALSE;break;// If the user wanted to print, hide our main window and// fire a message to ourselves to start the printingcase CCommandLineInfo::FilePrintTo:case CCommandLineInfo::FilePrint:m_nCmdShow = SW_HIDE;ASSERT(m_pCmdInfo == NULL);if(OpenDocumentFile(rCmdInfo.m_strFileName)){m_pCmdInfo = &rCmdInfo;ENSURE_VALID(m_pMainWnd);m_pMainWnd->SendMessage(WM_COMMAND, ID_FILE_PRINT_DIRECT);m_pCmdInfo = NULL;}bResult = FALSE;break;


从上面可以看出,FileNew就是从这里进去的。OnCmdMsg函数会调用全局函数_AfxDispatchCmdMsg,它的部分代码如下:

case AfxSigCmd_v:// normal command or control notificationASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);(pTarget->*mmf.pfnCmd_v_v)();break;case AfxSigCmd_b:// normal command or control notificationASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);bResult = (pTarget->*mmf.pfnCmd_b_v)();break;case AfxSigCmd_RANGE:// normal command or control notification in a rangeASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);(pTarget->*mmf.pfnCmd_v_u)(nID);break;case AfxSigCmd_EX:// extended command (passed ID, returns bContinue)ASSERT(pExtra == NULL);bResult = (pTarget->*mmf.pfnCmd_b_u)(nID);break;case AfxSigNotify_v:{AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;ENSURE(pNotify != NULL);ASSERT(pNotify->pResult != NULL);ASSERT(pNotify->pNMHDR != NULL);(pTarget->*mmf.pfnNotify_v_NMHDR_pl)(pNotify->pNMHDR, pNotify->pResult);}break;case AfxSigNotify_b:{AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;ENSURE(pNotify != NULL);ASSERT(pNotify->pResult != NULL);ASSERT(pNotify->pNMHDR != NULL);bResult = (pTarget->*mmf.pfnNotify_b_NMHDR_pl)(pNotify->pNMHDR, pNotify->pResult);}break;


它其实是对不同的消息类型调用不同的默认回调函数,有的是空参数的,有的以一个整形为参数的,等等。最终的返回值表征是否已经处理,外部会根据这个返回值决定是否继续处理下去。

如下是接下来的处理:

这里可以看到,MFC类库内部完成了主要的消息传递过程,最终到达应用程序document类的OnNewDocument来完成最后的处理。

ok,当应用程序启动后,手动点击菜单的新建或者工具栏中的新建,调用堆栈如下:

注意,上面的调用堆栈并不完全,堆栈最底层的是在ntdll中线程启动的代码,这里不列出了。

不过可以看出,应用程序启动后,对于菜单或者工具栏的操作将通过应用程序类的Run函数,它会将UI命令传递进去,让适当的模块处理,这和刚刚启动时的调用堆栈不一致。

在这里,我们主要看看AfxInternalPumpMessage这个函数:

BOOL AFXAPI AfxInternalPumpMessage()
{_AFX_THREAD_STATE *pState = AfxGetThreadState();if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)){
#ifdef _DEBUGTRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT.\n");pState->m_nDisablePumpCount++; // application must die
#endif// Note: prevents calling message loop things in 'ExitInstance'// will never be decrementedreturn FALSE;}#ifdef _DEBUGif (pState->m_nDisablePumpCount != 0){TRACE(traceAppMsg, 0, "Error: CWinThread::PumpMessage called when not permitted.\n");ASSERT(FALSE);}
#endif#ifdef _DEBUG_AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur));
#endif// process this messageif (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))){::TranslateMessage(&(pState->m_msgCur));::DispatchMessage(&(pState->m_msgCur));}return TRUE;
}


可以看到,它其实主要就是GetMessage, TranslateMessage, DispatchMessage这3个函数,是windows应用程序消息处理基本过程。

后面的调用关系就不具体说了。


微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是程序员小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/333956.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序大能量:盲盒平台搭建与营销策略

一、引言 在移动互联网的浪潮下,小程序以其轻量级、即用即走的特点,成为了商家与消费者沟通的新桥梁。盲盒经济作为近年来兴起的消费趋势,结合小程序平台,不仅为用户带来了全新的购物体验,也为商家带来了更多的商业机…

【管理咨询宝藏115】某大型电力集团5年战略发展报告终稿

本报告首发于公号“管理咨询宝藏”,如需阅读完整版报告内容,请查阅公号“管理咨询宝藏”。 【管理咨询宝藏115】某大型电力集团5年战略发展报告终稿 【格式】PDF版本 【关键词】战略规划、大型国企、战略报告 【核心观点】 - 战略领导人敏锐的直觉和城…

【乐吾乐3D可视化组态编辑器】模型类型与属性

编辑器地址:3D可视化组态 - 乐吾乐Le5le 本章主要为您介绍模型的属性功能。 一个模型至少会包含一个节点(Node),从节点类型上可以分为转换节点(TransformNode)、网格(Mesh)、实例网…

5.27作业

定义自己的命名空间my_sapce&#xff0c;在my_sapce中定义string类型的变量s1&#xff0c;再定义一个函数完成对字符串的逆置。 #include <iostream> #include <string.h>using namespace std; namespace my_space {string s1;void RevString(string &s1); } v…

OrangePi AIpro 开箱初体验及语音识别样例

OrangePi AIpro 开箱初体验及语音识别样例 一、 前言 首先非常感谢官方大大给予这次机会&#xff0c;让我有幸参加此次活动。 OrangePi AIpro联合华为精心打造&#xff0c;采用昇腾AI技术路线&#xff0c;具体为4核64位处理器AI处理器&#xff0c;集成图形处理器&#xff0c;…

【JavaScript】P3 JavaScipt 注释方法、结束符、输入输出

小结&#xff1a; Js 注释&#xff1a; 单行注释&#xff1a;//多行注释&#xff1a;/* */ Js 结束符&#xff1a; 分号; 可以加也可以不加 Js 输入输出&#xff1a; 输入&#xff1a;prompt()输出&#xff1a;document.write() 在页面中打印&#xff0c;console.log() 在控制…

浅谈金融行业数据安全分类分级

数据安全管理是一项从上而下的、多方配合开展的工作。在进行数据安全管理组织架构建设时&#xff0c;需要从上而下建设&#xff1b;从而全面推动数据安全管理工作的执行和落地&#xff1b;以保证数据安全的合法合规、并长效推动业务的发展和稳定运行。 金融行业机构应设立数据…

【Sql Server】随机查询一条表记录,并重重温回顾下存储过程的封装和使用

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言随机查询语…

自定义CSS属性(@property)解决自定义CSS变量无法实现过渡效果的问题

且看下面的代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>demot</title&g…

Python批量docx或doc文档转换pdf

说明&#xff1a; 1、因为项目需要&#xff0c;需要手动将十几个word文档转换成pdf文档 2、python请安装3.9.0以上&#xff0c;否则一些依赖库无法正常用 #! /usr/bin/python3 # -*- coding: utf-8 -*-import os import comtypes.client# 批量将docx文件转换pdf文件 def docx_t…

第十二周 5.21面向对象的三大特性(封装、继承、多态)(二)

三、多态 1.理解: (1)多态:父类型的引用存储不同子类型的对象 父类类名 引用名 new 子类类名(); 引用 对象 父类型 子类型 …

Java面试八股之AQS对资源的共享方式

AQS对资源的共享方式 AQS设计了一套灵活的机制&#xff0c;不仅支持独占&#xff08;Exclusive&#xff09;锁模式&#xff0c;也支持共享&#xff08;Shared&#xff09;锁模式&#xff0c;使得资源可以被一个或者多个线程以不同的方式访问。这两种模式通过控制一个内部的vol…

B站pink老师HTML5基础(一)

文章目录 一、网页1.什么是网页2.什么是HTML二、常用浏览器 三、Web标准四、HTML标签1.HTML基本结构标签 五、快捷键六、常用标签1.标题标签2.段落和换行标签3.文本格式化标签4.div标签和span标签5.图像标签6.图像路径7.超链接标签8.特殊字符 一、网页 1.什么是网页 2.什么是H…

MySQL的SQL语句

SQL1 查询所有列 SELECT * FROM user_profileselect id,device_id,gender,age,university,province from user_profileSQL2 查询多列 题目&#xff1a;现在运营同学想要用户的设备id对应的年龄、性别和学校的数据&#xff0c;请你取出相应数据 SELECT device_id,gender,age,…

【全开源】宇鹿家政系统(FastAdmin+ThinkPHP+原生微信小程序)

&#xff1a;助力家政行业数字化升级 一、引言&#xff1a;家政服务的新篇章 随着移动互联网的普及和人们生活水平的提高&#xff0c;家政服务的需求日益增长。为了满足这一市场需求&#xff0c;并推动家政行业的数字化升级&#xff0c;我们特别推出了家政小程序系统源码。这…

张量 t-product 积(matlab代码)

参考文献&#xff1a;Tensor Robust Principal Component Analysis with a New Tensor Nuclear Norm 首先是文章2.3节中 t-product 的定义&#xff1a; 块循环矩阵&#xff1a; 参考知乎博主的例子及代码&#xff1a;&#xff08;t-product与t-QR分解&#xff0c;另一篇傅里叶对…

【保姆级教程】基于OpenCV+Python的人脸识别上课签到系统

【保姆级教程】基于OpenCVPython的人脸识别上课签到系统 一、软件安装及环境配置1. 安装IDE&#xff1a;PyCharm2. 搭建Python的环境3. 新建项目、安装插件、库 二、源文件编写1. 采集人脸.py2. 训练模型.py3. 生成表格.py4. 识别签到.py5. 创建图形界面.py 三、相关函数分析1.…

【二叉树】非递归实现前中后序遍历

目录 前言 算法思想 非递归实现前序遍历 过程分析 代码 非递归实现中序遍历 过程分析 代码 非递归实现后序遍历 过程分析 代码 前言 1&#xff09;前序&#xff1a;根 左子树 右子树 2&#xff09;中序&#xff1a;左子树 根 右子树 3&#xff09;后序&#xff1…

使用Python类的构造函数和析构函数

1、问题背景 当使用Python类时&#xff0c;可以使用构造函数和析构函数来初始化和清理类实例。构造函数在创建类实例时自动调用&#xff0c;而析构函数在删除类实例时自动调用。 在上面的代码示例中&#xff0c;Person类具有一个构造函数__init__和一个析构函数__del__。构造…

深度学习-序列模型

深度学习-序列模型 1. 定义2. 应用领域3. 典型模型4. 技术细节5. 总结 序列模型是一种处理序列数据的机器学习模型&#xff0c;其输入和/或输出通常为序列形式的数据。以下是关于序列模型的详细解释&#xff1a; 1. 定义 序列模型是输入输出均为序列数据的模型&#xff0c;它…