当前位置: 首页 > news >正文

技术演进中的开发沉思-41 MFC系列:定制 AppWizard

MFC开发,最为重要的无非就是用“MFC AppWizard” 对话框做开发了,第一次使用感觉像拆收音机的孩子 —— 左边是项目类型选择,右边是一堆打勾的选项,点完 “完成”,屏幕上就冒出了能直接编译运行的窗口程序。那时还不知道,这个像 “自动包饺子机” 的工具,后来会被我们改成适配团队需求的 “专属模具”。

一、Custom AppWizard 基本操作

刚开始用 AppWizard 时,每次新建项目都要重复选 “单文档”“支持数据库”“带状态栏”,像每次包饺子都要重新调馅料比例。有次团队赶一个环境监测软件的项目,一周内要新建三个类似的子模块,光是配置项目选项就耗了小半天。也就是从那时候起,我开始琢磨怎么把这些重复步骤 “固化” 成新模板,Custom AppWizard 就成了最佳选择。

打开 VC++5.0,从 “File” 里选 “New”,在 “Projects” 标签里找到 “Custom AppWizard”,给它起个名字 —— 比如我们当时给团队做的 “科研数据工具模板”。这里有个小细节,名字最好带上团队或项目特征,后来我们建了好几个模板,“水质监测专用”“大气数据采集” 这样的命名能让大家一眼就找到对应模板。

下一步会让你选基于哪个原始模板(就像选基础面团),MFC 提供了单文档、多文档、对话框这几种基础类型。我们当时做的监测软件大多是单文档带数据表格的,就选了 “Single Document” 作为基础。接着弹出的配置页和普通 AppWizard 一模一样,从 “Database Support” 里勾选 “Database View Without File Support”,到 “User Interface Features” 里把 “Status Bar” 和 “Tool Bar” 都打上勾,每一个选项都对应着后续开发的基础需求。

配置完成后,VC++ 会生成一个新的模板项目,里面包含了模板的配置信息和生成逻辑。我们需要把这个模板编译一下,生成的.dll 文件会被自动放到 VC 的模板目录里。等下次同事新建项目时,在 “Projects” 列表里就能看到我们做的 “科研数据工具模板”,一点击,之前配置的选项全都是默认勾选状态,原本要花 5 分钟的配置过程,现在 10 秒钟就能搞定。

这一步就像给自动售货机装固定货道 —— 你提前把大家常买的饮料摆好,别人按一下就能拿到,不用再从一堆选项里翻找。而且这个货道还能随时调整,后来我们需要给软件加打印功能,就在模板里把 “Printing and Print Preview” 勾选上,所有基于这个模板新建的项目,自动就有了打印相关的基础代码。

二、AppWizard Components 剖析

后来团队需要在每个程序里加日志模块,普通模板满足不了。这时候就得拆开 AppWizard 的 “五脏六腑”,看看它是怎么生成代码的。就像想在饺子里加新馅料,得先知道饺子机的馅料输送管道在哪。

AppWizard 的核心是两个部分:配置对话框代码生成脚本。配置对话框就是我们选选项的那些窗口,它由一系列的对话框资源和对应的处理代码组成。这些对话框就像点餐时勾选 “加辣”“少冰” 的菜单,每个勾选框背后都对应着一个变量,当我们点击 “下一步” 时,这些变量的值就会被传递给代码生成脚本。

代码生成脚本则是根据勾选结果 “炒菜” 的菜谱,它用一种叫 “.awx” 的格式存储。.awx 文件其实是一种特殊的资源文件,里面包含了代码模板、条件判断语句和资源模板。我当时用资源编辑器打开.awx 文件,看到里面像搭积木一样排列着各种代码片段 —— 有窗口类的定义、消息映射的实现,还有菜单和工具栏的资源描述。

比如它判断你选了 “状态栏”,就会在 MainFrame 类里加m_wndStatusBar的定义,同时在OnCreate函数里加入状态栏的创建代码:


if (!m_wndStatusBar.Create(this) ||!m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT))){TRACE0("Failed to create status bar\n");return -1; // fail to create}

而如果选了 “数据库支持”,它就会引入 ODBC 相关的头文件,在文档类里加入CDatabase和CRecordset的对象定义。

这些组件就像装修时的 “水电预装模块”—— 你选了 “装空调”,工人就会预留管线;选了 “智能家居”,就会提前布好网线。理解了这些,就知道该改哪里能加入我们自己的模块。比如我们想加日志模块,就得找到代码生成脚本里初始化程序的部分,在那里加入日志初始化的代码;同时在配置对话框里加一个勾选框,让用户可以选择是否启用日志功能。

当时为了搞清楚这些组件的工作流程,我特意找了个简单的模板,每勾选一个选项就去看生成的代码有什么变化,像侦探破案一样一点点梳理出选项和代码之间的对应关系。这个过程虽然花了两天时间,但搞明白之后,后面修改模板就得心应手了。

三、动手修改 AppWizard

早期在开发项目时有这样的需求,我们需要每个程序都带 “数据加密” 功能。因为监测数据涉及到环境敏感信息,必须在程序启动时初始化加密模块,防止数据泄露。如果让每个开发人员手动加这段代码,不仅麻烦还容易出错,所以我决定修改之前的 Custom AppWizard 模板,把加密初始化的代码集成进去。

首先找到模板的.awx 文件(通常在 VC 安装目录的 Template 文件夹里,路径大概是 “Microsoft Visual Studio\VC98\Template”),用 VC 打开这个模板项目。在工作区里能看到 “Resource Files” 下面有个 “Custom.awx” 文件,这就是我们要修改的代码生成脚本。

双击打开.awx 文件,里面是按不同代码模块分类的脚本。我要找的是应用程序初始化的部分,对应着 CWinApp 派生类的 InitInstance 函数。在脚本里找到相关的代码块后,加入了一段判断和加密初始化代码(简化版):


// 在生成CWinApp派生类时添加加密初始化if (m_bNeedEncrypt) // 我们新增的配置项{AddCode("BOOL CMyApp::InitInstance()");AddCode("{");AddCode(" // 初始化加密模块");AddCode(" EncryptInit();"); // 我们的加密函数AddCode(" // 原有初始化代码");AddCode(" CWinApp::InitInstance();");AddCode("}");}

这段脚本的意思是,如果用户勾选了 “启用数据加密”,生成的程序就会在初始化时调用 EncryptInit 函数。

光有脚本还不够,得让用户能选择是否启用这个功能。所以我又在配置对话框里加了个 “启用数据加密” 的复选框。在模板项目里找到对话框资源(通常是 IDD_STEP1 或者类似的 ID),用对话框编辑器拖了一个复选框进去,设置 ID 为 IDC_ENCRYPT,然后在对应的对话框类里添加了一个成员变量 m_bNeedEncrypt,用来存储用户的选择。这样当用户在配置时勾选这个复选框,m_bNeedEncrypt 就会被设为 TRUE,上面的脚本就会生效。

改完这些后,我编译了模板并测试了一下。新建项目时,配置页面果然出现了 “启用数据加密” 的复选框,勾选后生成的代码里也有了 EncryptInit 的调用。但运行程序时却崩溃了,调试后发现是没在代码里引入加密模块的头文件 “Encrypt.h”。就像包饺子时忘了放酵母,面发不起来,代码缺少必要的头文件,自然无法正常运行。于是我又回到脚本里,在添加加密初始化代码的地方,加了一行#include "Encrypt.h",问题才得以解决。

这种 “踩坑” 的经历反而让我明白:工具再智能,也需要人懂它的 “脾气”。每个看似简单的功能背后,都有一连串的细节需要考虑,而这些细节正是从 “会用工具” 到 “驾驭工具” 的关键。

最后小结:

现在回头看,Custom AppWizard 就像 MFC 时代的 “技术裁缝”。它不只是省时间的工具,更藏着早期程序员对 “复用” 的理解 —— 我们不重复写代码,也不重复做配置,把精力留给真正需要创新的地方。

后来到了 Web 时代,脚手架工具代替了 AppWizard,但道理没变:好的工具永远是 “可定制的”。就像当年我们在机房里改模板时想的:技术会变,但 “让工具适应人” 的思路,大概是所有程序员的共同默契。而那些在修改模板时踩过的坑、总结的经验,也成了后来面对新工具时的底气 —— 无论工具怎么变,拆解它、理解它、改造它的逻辑,从来都没有变过。未完待续.........

http://www.dtcms.com/a/290279.html

相关文章:

  • 【爬虫】06 - 自动化爬虫selenium
  • Zabbix 企业级分布式监控系统深度解析
  • 计算机发展史:人工智能时代的智能变革与无限可能
  • Laravel 后台登录 403 Forbidden 错误深度解决方案-优雅草卓伊凡|泡泡龙
  • NVM的安装使用:nvm管理多个 Node.js 版本的工具
  • gRPC深度解析:原理、实践与性能优化指南
  • 将 RustFS 用作 GitLab 对象存储后端
  • uniapp使用uni-ui怎么修改默认的css样式比如多选框及样式覆盖小程序/安卓/ios兼容问题
  • 测量误差溯源:系统误差与随机误差的数学建模与分离方法
  • 大模型——Prompt 优化还是模型微调
  • 【PTA数据结构 | C语言版】求单源最短路的Dijkstra算法
  • AI学习--本地部署ollama
  • 6.String、StringBuffer、StringBuilder区别及使用场景
  • 第3章通用的服务可用性治理手段——3.1 微服务架构与网络调用
  • Tomcat的部署、单体架构、session会话、spring
  • ARC学习(6)arc 编译器overlap 地址重叠方式使用
  • stm32mp157f-dk2安装镜像并且部署qt全流程
  • 基于uniapp的餐厅在线选餐小程序的设计与实现
  • 信息整合注意力IIA,通过双方向的轻量级注意力机制强化目标关键特征并抑制噪声,提升特征融合的有效性和空间位置信息的保留能力。
  • Qt的QAbstractTableModel
  • 基于大数据的旅游推荐系统 Python+Django+Hive+Vue.js
  • 三大工厂设计模式
  • 电商项目_秒杀_初步分析
  • Django视图与路由系统
  • Jetpack ViewModel LiveData:现代Android架构组件的核心力量
  • echarts图铺满父元素
  • 在翻译语义相似度和会议摘要相似度评估任务中 ,分类任务 回归任务 生成任务区别
  • k8s查看某个pod的svc
  • Zookeeper 注册中心垂直介入
  • ZooKeeper学习专栏(四):单机模式部署与基础操作详解