小识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 printing
	case 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 notification
		ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED
		ASSERT(pExtra == NULL);
		(pTarget->*mmf.pfnCmd_v_v)();
		break;

	case AfxSigCmd_b:
		// normal command or control notification
		ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED
		ASSERT(pExtra == NULL);
		bResult = (pTarget->*mmf.pfnCmd_b_v)();
		break;

	case AfxSigCmd_RANGE:
		// normal command or control notification in a range
		ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED
		ASSERT(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 _DEBUG
		TRACE(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 decremented
		return FALSE;
	}

#ifdef _DEBUG
  if (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 message

	if (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等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

视频太大怎么压缩变小 视频太大了怎么压缩

视频作为一种多媒体形式,在当今社会的重要性日益凸显,其应用范围广泛,影响力深远。 但是视频文件的大小也在不断增加,这给存储和传输带来了很大的困扰。那么,当视频文件过大时,我们该如何将其压缩变小呢&am…

免费分享一套SpringBoot+Vue企业客户关系CRM管理系统【论文+源码+SQL脚本+PPT】,帅呆了~~

大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue企业客户关系CRM管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue企业客户关系CRM管理系统系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue企业客户关系CRM管…

MySQL(进阶)--索引

目录 一.存储引擎 1.MySQL体系结构​编辑 2.存储引擎简介 3.存储引擎特点 (1.InnoDB (2.MyISAM (3.Memory 4.存储引擎选择 二.索引 1.索引概述 2.索引结构 3.索引分类 4.索引语法 (1.创建索引 (2.查看索引 (3.删除索引 5.SQL性能分析 (1.SQL执行频率 (2.慢查…

基于stm32和HC_SR04超声波模块的测距和报警

基于stm32和HC_SR04超声波模块的测距和报警 目录 **基于stm32和HC_SR04超声波模块的测距和报警****一.工作原理****二.功能实现****HC_SR04初始化和读取距离****使用呼吸灯表示距离远近****主函数编写** **三.效果****四.关于modbus和串口RS485****五.总结** 一.工作原理 (1)采…

CISCN——2024——re——app-debug

输入检查类题型 package com.example.re11113;import android.os.Bundle; import android.util.Log; import android.view.View.OnClickListener; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; im…

生信网络学院|05月31日《SOLIDWORKS Manage 产品周期管理》

课程主题:SOLIDWORKS Manage 产品周期管理 课程时间:2024年05月31日 14:00-14:30 主讲人:付舰 生信科技 PLM实施顾问 1、SOLIDWORKS Manage介绍 2、周期流程管理 3、产品项目管理 4、项目会议管理 5、项目问题管理 安装腾讯会议客户端…

python的下载与安装

1.下载python 下载地址:Download Python | Python.org 进入到python的官网,点击downloads这个标签进入下载版本列表。 找到需要下载的版本,点击download。 选择executable这个文件类型进行下载。(尽量不要选择zip会有文件缺失&a…

智能座舱-车载声学技术训练营

语音交互赋能车载智能终端,成为智能座舱生态构建的核心功能 曾几何时,至少十年前,车内语音交互,大家都认为是“智障”阶段,基本上除了难用作为评价找不到其他的形容词做修饰。 但是随着技术的不断发展,特别…

工作软件新宠儿

想要让你的工作效率飞起来吗?👀 是时候告别那些大众化的工作软件啦!今天,我要给大家种草几款不常见的但超级实用的工作软件🌱,保证让你事半功倍哦!🌟 1️⃣ 亿可达 它是一款自动化…

mySQL事务、存储引擎

什么是事务,事务特性————>保证数据完整性 1,事务是作为单个逻辑工作单元执行的一些列操作。 2,多个操作为一个整体向系统提交,要么执行,要么都不执行。 3,事务是一个不可分割的工作逻辑单元。 事务四特性: 原子性atomi…

滤布压滤机液压比例放大器

滤布压滤机是一种常用的固液分离设备,主要用于固体颗粒的过滤和脱水。其工作原理是通过压力对物料进行过滤和脱水处理。滤布压滤机主要由滤布、滤板、滤框、液压系统和辅助设备等组成。其中,滤布起到过滤和脱水的作用,滤板和滤框则用于支撑滤…

笔试强训week6

day1 Q1 难度⭐⭐ 小红的口罩_牛客小白月赛41 (nowcoder.com) 题目: 疫情来了,小红网购了 n 个口罩。 众所周知,戴口罩是很不舒服的。小红每个口罩戴一天的初始不舒适度为 ai​。 小红有时候会将口罩重复使用(注:…

表空间[MAIN]处于脱机状态

达梦数据库还原后&#xff0c;访问数据库报错&#xff1a;表空间[MAIN]处于脱机状态 解决方法&#xff1a; 1&#xff1a;检查备份文件 DMRMAN 中使用 CHECK 命令对备份集进行校验&#xff0c;校验备份集是否存在及合法。 ##语法&#xff1a;CHECK BACKUPSET <备份集目录…

育菁桌面式数控机床助力教育装备

桌面式数控机床是一种小型化的数控机床&#xff0c;它通常具有紧凑的设计和较小的体积&#xff0c;可以放置在桌面上进行操作。 这种车床结合了数控技术&#xff0c;通过计算机编程来控制机床的运动和加工过程&#xff0c;以实现高精度、高效率的工件加工。 桌面式数控车床是一…

Linux服务器配置ssh证书登录

1、ssh证书登录介绍 Linux服务器ssh登录有密码登录和证书登录两种。如果使用密码登录&#xff0c;容易遭受密码泄露或者暴力破解&#xff0c;我们可以使用ssh证书登录并禁止使用密码登录&#xff0c;ssh证书登录通过公钥和私钥来完成整个连接过程&#xff0c;公钥保存在服务器…

君正X2100 RTOS 修改UVC友好名称

修改UVC友好名称 SDK中默认的名称为"UVC Camera"。 相关文件f_uvc.c&#xff0c;具体内容&#xff1a; static struct usb_string uvc_en_us_strings[] {[UVC_STRING_CONTROL_IDX].s "UVC Camera",[UVC_STRING_STREAMING_IDX].s "Video Streamin…

埃文科技携数据要素产品亮相第七届数字中国建设峰会

第七届数字中国建设峰会&#xff08;以下简称“峰会”&#xff09;于2024年5月24日至25日在福建省福州市举办。此次峰会是国家数据工作体系优化调整后举办的首次数字中国建设峰会。本届峰会由国家发展改革委、国家数据局、国家网信办、科技部、国务院国资委、福建省人民政府共同…

fyne表单布局

fyne表单布局 layout.FormLayout就像一个 2 列网格布局 。 package mainimport ("image/color""fyne.io/fyne/v2/app""fyne.io/fyne/v2/canvas""fyne.io/fyne/v2/container""fyne.io/fyne/v2/layout" )func main() {myApp…

幼儿园老师投稿渠道

幼儿园老师投稿通常指的是教师为了分享自己的教学经验、教育活动设计、儿童发展研究等内容&#xff0c;向专业杂志、教育平台或在线论坛提交文章或资料的过程。以下是一些关于幼儿园老师投稿的步骤和建议&#xff1a; 一、准备工作 选择合适的平台 研究不同平台的读者群体&a…

javaee---IO代码练习

实现一个小程序要求: 扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且要求询问用户是否要删除这个文件 代码示例 public static void main(String[] args) {//1.先让用户指定一个要扫描的目录Scanner scanner new Scanner(System.in);System.out.pri…