ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VC6.0 MFC计算器完整工程:从消息映射到状态机重构

VC6.0 MFC计算器完整工程:从消息映射到状态机重构 简介基于VC6.0与MFC框架实现的计算器程序完整工程面向C初学者、需要完成Windows课程设计的本科生以及想了解对话框编程的开发者。该工程演示了从创建MFC对话框应用、放置数字与运算符按钮、使用静态文本作为显示区域到定义消息映射、编写点击事件函数、处理CString与double转换、完成加减乘除运算并更新界面的完整流程有助于直观理解MFC的消息驱动机制与窗口对象封装方式正是切入Windows应用开发的高效路径。资源包共31个文件包含6个.h头文件和5个.cpp源文件另有.rc界面资源、.bmp图标、.ico光标、.swf动画素材以及一份MFC说明文档和相关工程配置文件压缩包约106KB结构紧凑下载后可在VC6.0环境中直接打开编译运行。已有383人浏览学习适合作为MFC入门练手、课程设计参考或毕业设计主题的基础工程。通过阅读源码和说明文档可以掌握对话框控件ID与消息处理函数的绑定方法、数值动态显示策略、错误处理思路以及工程组织方式同时能从案例中借鉴调试经验提升独立排错能力。基于此程序可继续扩展括号、平方、历史记录等高级计算功能是熟悉面向对象Windows编程简洁而完整的实操样例。1. 用 VC6.0 写 MFC 计算器一份能直接编译的完整工程比零散教程更省心MFC 计算器是 VC6.0 时代最具代表性的入门项目麻雀虽小却覆盖了对话框框架、控件布局、消息映射和数值处理一条完整链路走下来Windows 桌面程序的底子就基本有了。市面上的计算器代码大多是贴出来的零散片段你能看到消息映射宏却看不到工程怎么组织、资源文件怎么配、环境怎么搭。这份 vc6.0-mfc.rar 的难得之处在于它是一份完整工程jisuan.dsw 工作区、jisuan.rc 对话框模板、Resource.h 资源定义、jisuanDlg.cpp 主窗口实现一应俱全还附带了一个基于 Shockwave Flash 控件的扩展对话框模块兼顾了基础学习和 ActiveX 控件集成两件事。对正在做课程设计的本科生、刚接触 MFC 的初学者或者想找一份能跑通的工程做二次开发的从业者这份资源都有它的用处——先能跑起来再谈改代码。2. 解剖工程文件jisuan.dsw 到 jisuanDlg.cpp七个文件看懂 MFC 对话框程序2.1 jisuan.dsw 与 jisuan.dspVC6.0 的项目入口和编译配置在 VC6.0 里双击 jisuan.dsw 就能打开整个工作区。dsw 原名 Developer Studio Workspace是工作区文件记录了这一工作区包含哪些项目内容非常简短真正承载编译配置的是 dsp 文件即 Developer Studio Project里面写明了源文件列表、预处理器宏、库依赖和 Debug/Release 两套构建参数。所以下次看到“工程文件损坏、无法打开”这类提示先不要慌。凡是 .dsw/.dsp 报错九成是因为路径变化或者 dsp 与 dsw 之间的项目名引用不一致。我自己比较常用的处理是把整个工程目录复制到新位置后删除 .ncb、.opt、.plg 三个缓存文件然后重新打开 dsw。.ncb 是 IntelliSense 用来做自动补全提示的数据库.opt 记录 IDE 的窗口布局和断点.plg 是上一次编译的文本日志这三者全部可以再生删了之后 IDE 从头扫描目录反而能清除旧路径缓存。值得注意的还有 jisuan.aps。它是由 .rc 编译后生成的二进制缓存核心功能是让资源编辑器打开得更快。资源编辑器中每一次保存都会同时更新 .rc 和 .aps如果你手动修改了 .rc而 .aps 还是旧的VC6.0 会弹出“资源脚本已修改是否重新加载”的提示选“是”即可让 .aps 重新生成正常操作不影响工程。2.2 Resource.h 与 jisuan.rc控件 ID 是消息映射的锚点Resource.h 把控件标识符映射到唯一的整数值例如 IDC_BUTTON_ADD 对应整数 1001jisuan.rc 是资源脚本用文本描述了对话框布局、每个控件的位置、大小、标题和 ID。计算器面板上所有按钮都来自这份 .rc 的 DIALOG 模板。按钮能在界面上响应点击靠的是控件 ID 和消息映射表这一对组合。如果你要给计算器新增一个按钮用资源编辑器最稳妥在 IDD_JISUAN_DIALOG 对话框模板上拖一个按钮右键修改 ID新 ID 会被写入 Resource.h然后用 ClassWizard 生成消息映射函数。ClassWizard 会自动把映射关系写进 .cpp、.h 和 jisuan.clw 三个文件。jisuan.clw 是 ClassWizard 数据库专门给 IDE 记录“哪个类响应哪些消息”如果手动改了 .rc 和 Resource.h 却没同步 clwClassWizard 面板里看不到新控件所以别光靠手写几个宏就把后续维护坑了。注意.rc、Resource.h、.clw 三方必须保持一致。最常见的故障是手动编辑 Resource.h 时误删了某个 IDC_ 宏编译报错一大堆恢复起来很头疼。2.3 StdAfx.h 与预编译头MFC 编译提速的核心机制StdAfx.h 和 StdAfx.cpp 是预编译头的两个载体。MFC 的 afxwin.h、afxext.h、afxcmn.h 几个大头文件加起来有几千行如果没有预编译机制每次编译都要重新解析这些头文件单是 include 展开就会耗费大量时间。预编译头把 StdAfx.h 包含的内容编译一次结果存到后缀为 .pch 的文件里后续其他源文件编译时直接复用这批编译产物。// stdafx.h标准系统包含文件 #include afxwin.h // MFC 核心和标准组件 #include afxext.h // MFC 扩展库 #include afxdisp.h // MFC 自动化与 COM 支持 #include afxdtctl.h // MFC 对 IE 公共控件的支持上面是 VC6.0 AppWizard 自动生成的 StdAfx.h 骨架只保留了最基础的四个 MFC 头文件。实际工程需要的其他头文件都放在各个 .cpp 里按需 include避免被全局预编译否则任何头文件的改动都会引发全工程重编。如果你拿到这份工程后改动过 StdAfx.h或者从 Release 切到 Debug 构建时 IDE 没有重新生成 .pch最容易遇见的是编译错误 C1010在查找预编译头文件时遇到意外的文件结尾。解决方式是执行 Rebuild All强制整个工程重新生成 .pch。还有一点容易忽略不要把 .pch 和 .ncb 这类构建产物打包发给别人它们和平台相关到了别人的机器上反而会触发各种奇怪的编译问题。2.4 jisuan.cpp 与 jisuanDlg.cpp从入口到对话框的两层结构jisuan.cpp 定义了一个从 CWinApp 派生的应用程序类以及一个全局唯一的 theApp 实例。MFC 程序在 main 执行之前先构造 theApp进入 WinMain 后调用 InitInstance主对话框在这里被创建并进入消息循环。BOOL CJisuanApp::InitInstance() { AfxEnableControlContainer(); // 允许使用 ActiveX 控件 CJisuanDlg dlg; m_pMainWnd dlg; INT_PTR nResponse dlg.DoModal(); // 模态对话框的消息循环 return FALSE; // 对话框关闭后进程退出 }AfxEnableControlContainer 是为 ActiveX 控件准备的初始化调用工程里的 Flash 控件如果没有这行代码在对话框上创建时很可能会失败。DoModal 是 CDialog 的核心方法它会启动消息循环直到用户点击关闭按钮。这个循环正是 Windows 事件驱动模型的关键程序暂停在 DoModal 内部界面事件排队进来分发到消息映射表直到对话框销毁才返回。懂得这条链路后你再看“程序一闪而过”的问题就能判断出问题大概率发生在 InitInstance 之前还是之后。2.5 flashdlg 与 shockwaveflash工程里藏着的一条 ActiveX 支线工程里的 flashdlg.cpp/h 和 shockwaveflash.cpp/h 是嵌入 Shockwave Flash 控件的扩展模块。shockwaveflash.h 是 Flash ActiveX 控件的 C 包装类flashdlg.cpp 是承载该控件的对话框实现。嵌入 ActiveX 控件是 MFC 中比较进阶的用法步骤是在对话框模板上右键选择 Insert ActiveX Control选中 Shockwave Flash ObjectVC6.0 会自动生成包装类代码shockwaveflash.cpp/h然后在对话框类的 OnInitDialog 里设置 Movie 属性BOOL CFlashDlg::OnInitDialog() { CDialog::OnInitDialog(); // 给 Flash 控件指定要加载的 SWF 文件 m_flashCtrl.SetMovie(_T(calculator.swf)); // 自动播放 m_flashCtrl.SetPlaying(TRUE); // 画质参数0 低1 中2 高 m_flashCtrl.SetQuality(2); return TRUE; }SetMovie 接受的路径可以是与 exe 同目录的文件名也可以是绝对路径SetPlaying 传 FALSE 则只加载不播放SetQuality 的值域是 0 到 2实际用下来建议直接设 2低画质在动画变化时会有明显锯齿。SetMovie 和 SetPlaying 在运行时修改能立即生效所以 Flash 控件常被用来做动态换片展示。把上面这些串起来你就有了一张完整的图景jisuan.rc 管界面长什么样Resource.h 给控件编号jisuanDlg.cpp 管核心交互jisuan.cpp 是进程入口StdAfx.h 提高编译效率Flash 模块承担扩展展示。拿到任何一份 MFC 对话框工程按这个顺序扫一遍文件清单心里基本就有数了。3. 让计算器转起来消息映射、控件取值与数值回显的三段式3.1 打开、编译、运行VC6.0 环境的兼容性设置与链接方式以这份 jisuan.dsw 为例拿到后先不要急着双击把整个目录解压到一个不带空格的路径下比如 D:\jisuan\避免旧版 IDE 在某些系统上对带空格路径处理不当导致编译时找不到中间文件。双击 dsw 打开后按 F7 执行 Build正常情况下十几秒就能在 Debug 目录下生成 jisuan.exe。如果你在 Windows 7/10/11 上运行首要注意的是 VC6.0 本身的兼容性。VC6.0 是 1998 年的产品装好后建议对 MSDEV.EXE 右键设置“以兼容模式运行Windows XP SP2”并勾选“以管理员身份运行”。如果编译时提示 cannot open precompiled header file删掉 Debug 目录下的 .pch 文件再重新 Build 即可。还有一个常见的环境坑机器上同时装了新版 Visual Studio 时环境变量可能被改写导致 VC6.0 偶发找不到 mspdb60.dll处理方式要么在虚拟机里单独跑 VC6.0要么保证命令行环境一致不要混合调用两个版本的编译器。工程默认使用动态链接 MFCUse MFC in a Shared DLL生成的 exe 依赖 MFC42.dll 才能运行。要把成品拿到别的机器上演示在 Project → Settings → General → Microsoft Foundation Classes 里改成 Use MFC in a Static Library重新编译之后 exe 自带 MFC 代码体积大约会增加 1MB 左右但不再需要目标机器安装运行库。做课程设计交作业时这个改动基本能避免“老师电脑上跑不起来”的尴尬。3.2 消息映射ON_BN_CLICKED 把按钮点击变成函数调用对话框程序的交互本质是消息的流转鼠标按下、按键按下、控件焦点变化每个事件都会变成消息进入队列。MFC 的消息映射用一张表把消息 ID 和响应函数绑在一起省去了手写 switch-case 分发的过程。以加法按钮为例头文件里要声明处理函数cpp 文件里要写映射宏// jisuanDlg.h 中的声明 class CJisuanDlg : public CDialog { ... afx_msg void OnBnClickedButtonAdd(); }; // jisuanDlg.cpp 中的消息映射 BEGIN_MESSAGE_MAP(CJisuanDlg, CDialog) ON_BN_CLICKED(IDC_BUTTON_ADD, CJisuanDlg::OnBnClickedButtonAdd) END_MESSAGE_MAP() void CJisuanDlg::OnBnClickedButtonAdd() { // 读取显示区当前字符串 CString strCur; GetDlgItemText(IDC_EDIT_DISPLAY, strCur); // 转成 double 存入成员变量 m_dOperand atof(strCur); // 记录本次运算符 m_nOperator OP_ADD; // 显示区归零等待输入第二个操作数 SetDlgItemText(IDC_EDIT_DISPLAY, _T(0)); }ON_BN_CLICKED 宏的第一个参数是控件 ID第二个参数是响应函数地址。只要这个 ID 与 .rc 中按钮的 ID 一致点击事件就能进入这个函数。函数体做的事可分为三类取GetDlgItemText、算赋值和状态记录、显SetDlgItemText。这里的 m_dOperand 和 m_nOperator 是跨消息保留状态的成员变量它们的存在决定了计算器能否连续运算。要注意的是这里用的是 atof来自 C 标准库。它会忽略字符串开头的空白解析尽可能长的数字片段遇到第一个不能构成数字的字符就停止。也就是说用户输入“12abc”时atof 会解析出 12 而不会报错所以如果你想把输入做严格校验需要在入口处单独判断字符串是否真的是合法数字否则奇怪输入会静默产生错误结果。3.3 数字键与等号键输入处理和运算触发的两处关键代码数字键的处理逻辑是所有数字按钮共通的先取显示区文本判断当前状态决定是替换还是追加。void CJisuanDlg::OnBnClickedButton5() { CString strCur; GetDlgItemText(IDC_EDIT_DISPLAY, strCur); if (strCur _T(0)) // 尚未输入有效数字 strCur _T(5); else strCur _T(5); // 已有数字直接追加 SetDlgItemText(IDC_EDIT_DISPLAY, strCur); }CString 的 操作符直接做字符串追加这是 MFC 中最常用的字符串拼接方式。这段代码隐藏着一个边界问题如果用户按过运算符显示区可能还留着上一个操作数的结果这时再按数字 55 会被追加到结果的末尾而不是作为新输入。常规解法是在运算符处理函数里加一个标志位 m_bNewNumber按运算符时置 TRUE在数字键处理里先判断这个标志为真则直接替换显示内容而不是追加。等号键的处理则汇总了所有中间状态void CJisuanDlg::OnBnClickedButtonEqual() { double dResult 0.0; double dCurrent atof(m_strDisplay); switch (m_nOperator) { case OP_ADD: dResult m_dOperand dCurrent; break; case OP_SUB: dResult m_dOperand - dCurrent; break; case OP_MUL: dResult m_dOperand * dCurrent; break; case OP_DIV: // 除零保护任何数除以 0 都返回 0 并提示 if (dCurrent 0.0) { AfxMessageBox(_T(除数不能为 0)); return; } dResult m_dOperand / dCurrent; break; } CString strOut; strOut.Format(_T(%.8g), dResult); // 8 位有效数字输出 SetDlgItemText(IDC_EDIT_DISPLAY, strOut); m_nOperator OP_NONE; // 复位运算符状态 }等号处理的核心是把成员变量 m_dOperand 里的值和当前显示区数值做运算。m_dOperand 是按下运算符那一刻存下来的值显示区字符串则是第二个操作数。这里最典型的 bug 出现在连续按等号时如果 m_nOperator 没有复位再次按等号会把上一次的结果继续运算。比如 2 3 第一次等号得到 5第二次又把 5 当第二个操作数加了进去结果变成 7这在真正的计算器逻辑里是错的。处理办法就是代码里的 m_nOperator OP_NONE让等号只触发一次有效运算。除以零的边界处理也在这里写清楚了。浮点数除法在 C 语言里不会崩溃但会产生 inf 这样的非有限值显示到界面上就成了一串奇怪的字符。加一个 dCurrent 0.0 的判断弹提示框再返回是计算器程序的基本素养。%.8g 这个格式说明符在输出浮点数时只保留 8 位有效数字并自动去掉末尾多余的零比直接用 %f 干净得多。这三层逻辑——取值、消息分发、结果回显——是 MFC 对话框程序最常用的循环链路。任何按钮功能哪怕只是改个字体、换个颜色都逃不出这套“消息 → 处理 → 更新界面”的框架。4. 避坑手册VC6.0 MFC 计算器最容易翻车的五个场景4.1 现象编译通过运行却一闪而过对话框根本没机会出现按 F5 调试或者直接双击 Debug 目录下的 exe程序闪一下就退出没有任何报错信息。这种情况十有八九不是计算逻辑的问题而是程序在启动早期阶段就失败了。原因Debug 版本的 MFC 程序链接的是 MFC42D.DLL、MSVCRTD.DLL 这组调试版运行库目标机器缺少这些 DLL 时加载器在进程初始化阶段就会终止运行程序连 WinMain 都进不去。另一个常见原因是 VC6.0 生成的程序在 Windows 7 以上的系统里权限或兼容性受限窗口创建失败后默认直接退出。解决先到 Project → Settings → General → Microsoft Foundation Classes 里把 MFC 链接方式改为 Use MFC in a Static Library然后 Rebuild All。静态链接后 exe 不再依赖 MFC42.dll大多数闪退问题就此解决。如果仍然闪退在 InitInstance 的 DoModal 调用前加一句 AfxMessageBox(_T(start))程序能弹框说明已经走到了入口弹不出来则说明问题出在更早的运行库加载阶段按兼容模式运行、管理员权限执行逐个尝试。经验上VC6.0 生成的程序放在非系统盘、非中文目录下运行并给 exe 单独设置“以兼容模式运行Windows XP SP2”稳定性会明显改善。后来我做课程设计辅导时遇到存疑的 exe第一件事就是远程看对方的目录路径和 MFC 链接方式大多数都一样的问题。4.2 现象菜单和按钮上的中文全部变成乱码在中文 Windows 上写的计算器按钮标题是“加、减、乘、除”换到另一台机器上编译运行界面变成一片问号或方框。原因.rc 资源脚本是 ANSI 编码保存的编译器读取时按当前系统的活动代码页来解释字符。中文 Windows 的活动代码页是 GBK如果 .rc 文件被某些编辑器改写成了 UTF-8 或者其他编码编译器按 GBK 去读自然得到乱码。VC6.0 的资源编辑器本身也可能在保存时重新按系统代码页写入导致原有编码发生偏移。解决用记事本打开 jisuan.rc检查右下角编码显示如果不是 ANSI选择“另存为”并选中 ANSI 编码覆盖保存然后再用 VC6.0 打开编译。处理过程中注意不要再经过任何默认 UTF-8 的编辑器二次保存。如果 .rc 已经损坏另一条退路是把界面上的中文按钮标题全部改成英文课程设计一般不会被扣分程序反而在各种机器上更稳定。4.3 现象链接时报 LNK2005 重复定义或 LNK2001 未解析的外部符号编译阶段一个错都没有链接时突然抛出一批 LNK2005 或 LNK2001错误列表里指到的文件还不固定很容易误导人去怀疑工程文件损坏。原因LNK2005 的经典原因是头文件被多个 cpp 文件包含而头文件内部没有加 include guard导致类或全局变量的定义在多个编译单元里各生成一份。LNK2001 则和字符集宏有关成员函数名在 Unicode 构建下会带 W 后缀多字节构建下带 A 后缀如果工程的 Debug 和 Release 两个配置里预处理器定义不一致一个定义了 UNICODE 而另一个没定义链接时符号名对不上就报未解析。解决先给所有自定义头文件补上标准的 #ifndef 保护然后在 Project → Settings → C/C → Preprocessor 里检查 _UNICODE 和 UNICODE 的定义把 Debug 和 Release 配置拉齐。代码里涉及字符串和字符处理的地方尽量用 _T() 包裹让它在多字节和 Unicode 下都能正确编译。这套做完再 BuildLNK 系列错误基本消失。如果还有残留逐一检查 StdAfx.h 里 include 的头文件是否重复。4.4 现象对话框初始化时弹出 ActiveX 控件相关错误或者崩溃运行到某一刻对话框就崩了断点定位在 OnInitDialog 里创建 Flash 控件的那一行提示 Class not registered 或者类似 COM 错误。原因这套工程里的 flashdlg 使用了 Shockwave Flash 控件它是一个 COM 组件需要在系统注册表里有对应的类注册信息。机器上没装过 Flash Player ActiveX控件创建自然失败。如果目标机器恰好删过 Flash 组件或者装了精简版 Flash也会出现同样的问题。解决如果你只想学计算器本身最快的方法是切除 Flash 模块分三步才能做干净第一步在 jisuan.rc 里找到放置 Flash 控件的对话框模板删除整个对话框或删除该控件条目第二步从工程中移除 flashdlg.cpp、flashdlg.h、shockwaveflash.cpp、shockwaveflash.h 四个文件第三步重新 Build。这三步缺一不可因为 .rc、源文件和 ClassWizard 数据库必须保持同步。如果想保留 Flash 功能安装 Flash Player ActiveX 版并确认注册表 HKCR\ShockwaveFlash.ShockwaveFlash 存在。不过考虑到 Flash 早已停止维护我建议直接切除这个组件本身已经成为安全漏洞的常客。4.5 现象连续运算时结果总是多算一次或者漏算操作序列是 12 3 5 期望得到 20实际却出来一个莫名其妙的数比如 155 或者 1235 混合在一起的结果。原因这是计算器程序中最经典的状态管理 bug。数字键的处理函数默认对显示字符串做追加按下第一个运算符后代码如果没有把显示区重置为等待新输入的状态用户输入 5 时上一个结果 15 还在显示区里追加后变成 155。本质是代码没有区分“输入第一个操作数”“按下运算符”“输入第二个操作数”“显示结果”这四个阶段。解决在运算符处理函数中把显示区内容存入 m_dOperand 之后立即把显示区清零并设置一个标志位 m_bNewNumber。数字键处理函数在拼接字符串之前先检查这个标志为真则直接替换显示内容然后置假为假则正常追加。这个改动做完12 3 之后显示区回到 0用户输入 5 时直接显示 5再按等号得到 20。这也更贴近 Windows 自带计算器的交互习惯。调试这类问题时在数字键处理函数入口打印 strCur 的值比在等号按钮处断点更有效因为错误发生在输入阶段而不是计算阶段。5. 把计算器做得更耐打状态机重构与键盘输入扩展这份工程本身能跑通四则运算但我最建议投入时间去做的一件事是把“按钮 → 字符串拼接 → 数值计算”这套充满状态标志的代码改造成状态机。理由就在 4.5 那条坑里计算器本质上一个状态流转系统用布尔标志去表达“当前处于哪个阶段”状态组合一多必然漏路径而状态机从结构上消灭这一类问题。一个够用的状态机只需要四个状态ST_EMPTY 表示初始化后还没输入ST_NUM1 表示正在输入第一个操作数ST_OP 表示已经按下运算符等待第二个操作数ST_NUM2 表示正在输入第二个操作数。事件分为数字键、运算符键、等号键、清除键四类。核心分发函数的写法如下enum CalcState { ST_EMPTY, ST_NUM1, ST_OP, ST_NUM2 }; // 数字键事件的统一入口 void CJisuanDlg::OnDigit(int nDigit) { switch (m_state) { case ST_EMPTY: case ST_OP: // 新输入替换显示区 m_strDisplay.Format(_T(%d), nDigit); m_state (m_state ST_OP) ? ST_NUM2 : ST_NUM1; break; case ST_NUM1: case ST_NUM2: // 连续输入追加数字 if (m_strDisplay _T(0)) m_strDisplay.Format(_T(%d), nDigit); else m_strDisplay.AppendFormat(_T(%d), nDigit); break; } SetDlgItemText(IDC_EDIT_DISPLAY, m_strDisplay); }运算符事件就简化成“记录运算符把状态切到 ST_OP”等号事件则是按当前状态决定是否计算并把结果状态切回 ST_NUM1。状态机的核心价值在于每个按钮的处理函数只需要知道当前状态不需要回溯前几次点击发生了什么。4.5 里那个多算一次的 bug在这种结构下根本没有存在空间。键盘输入扩展是另一个性价比极高的功能。在对话框类里重写虚函数 PreTranslateMessage拦截按键消息并转发到统一入口BOOL CJisuanDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg-message WM_KEYDOWN) { switch (pMsg-wParam) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: OnDigit(pMsg-wParam - 0); return TRUE; case : OnOperator(OP_ADD); return TRUE; case VK_RETURN: OnEqual(); return TRUE; case VK_BACK: OnBackspace(); return TRUE; } } return CDialog::PreTranslateMessage(pMsg); }字符键直接用字符字面量判断数字键通过 wParam - 0 得到实际数值回车映射到等号退格映射到删除。函数返回 TRUE 表示消息已被消费对话框不会发出无按键响应的提示音。做完这个扩展计算器立刻从“只能鼠标点”变成“键盘盲打也流畅”演示时的体验完全不一样。我当时处理这份工程最大的收获是意识到按钮只是输入设备计算逻辑完全可以和界面解耦。如果一个计算逻辑被界面代码裹挟得没法测试那问题一定出在架构上而不是算法上。从那以后我每次写带 UI 的小工具都会强制先画一遍状态图再动手界面越来越薄逻辑反而越来越耐改。这份工程的价值在于你可以从一条消息映射开始亲手走通 MFC 的全链路希望你在复现的时候少踩几个坑希望帮到你。本文还有配套的精品资源点击获取
返回列表