技术演进中的开发沉思-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.pswp.cn/bicheng/90140.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90140.shtml

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

相关文章

Libevent(3)之使用教程(2)创建事件

Libevent(3)之使用教程(2)创建事件 Author: Once Day Date: 2025年6月29日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 本文档翻译于:Fast portable non-bl…

Kotlin 作用域函数 let 的实现原理

Kotlin 中的 let 是一个 标准库扩展函数,它广泛用于作用域函数(Scope Functions)中,尤其适用于对可空对象(nullable)做非空判断并执行代码块的场景。 示例代码 val name: String? "123" name?…

从FDTD仿真到光学神经网络:机器学习在光子器件设计中的前沿应用工坊

FDTD仿真与光学神经网络的基础概念 FDTD(时域有限差分)是一种数值方法,用于求解麦克斯韦方程组,广泛应用于光子器件设计。光学神经网络通过光波导、衍射元件等物理结构实现矩阵运算,具有低能耗、高并行的优势。 机器学…

在Ubutu22系统上面离线安装Go语言环境【教程】

0.引言 Go语言(又称Golang)是Google开发的一种静态强类型、编译型、并发型编程语言,由Robert Griesemer、Rob Pike和Ken Thompson于2007年开始设计,2009年正式发布。 1.到官网下载压缩包 2.从win10系统离线上传压缩包给ubuntu22…

CMake实践:CMake3.30版本之前和之后链接boost的方式差异

目录 1.背景 2.boost引入CMake时机 3.CMake 3.30 之前(含 3.29)链接 Boost 的方式 4.CMake 3.30 及之后链接 Boost 的方式 5.CMake3.30后引入Boost的步骤 6.迁移建议(3.30 之前 → 3.30 之后) 7.CMake 3.30 移除FindBoost的…

告别挂马风险!PBootCMS完美替代方案BadouCMS

开发企业网站时一直比较喜欢用pbootcms,标签套用很简单,使用也方便。 但是pbootcms一直有被挂马的问题,官方好像也不怎么更新了!换过好几个cms,比如eyoucms、dedecms、帝国等等,感觉都不怎么能用得习惯,还…

开发者如何集成AI绘画?智创聚合API简化Midjourney接入

在 AI 绘画领域,Midjourney 的大名如雷贯耳,其强大的图像生成能力,能将我们脑海中的奇思妙想,迅速转化为精美的视觉画面,深受设计师、艺术家以及广大创意爱好者的青睐。然而,使用 Midjourney 的过程中&…

pycharm回车、删除、方向键和快捷键等不能使用原因

解决方法 :菜单栏中的Tools取消勾选Vim Emulator 原因 :新版的pycharm安装中,默认安装了vim扩展,一旦安装了pycharm在编写代码时会默认使用Vim编辑器

修复ffmpeg.dll丢失错误|6种解决ffmpeg.dll方法详细教程

看到电脑提示“ffmpeg.dll丢失”,很多人会懵。ffmpeg.dll 是个处理视频、音频的关键文件。它要是没了或坏了,软件就打不开或直接闪退。常见原因是软件安装不全、文件被删、或者中病毒。下面说说它是干嘛的,再给解决办法。一.ffmpeg.dll 到底是…

OkHttp 与 Stetho 结合使用:打造强大的 Android 网络调试工具链

前言在 Android 应用开发过程中,网络请求的调试一直是一个重要但具有挑战性的环节。Facebook 开发的 Stetho 是一个强大的调试工具,当它与 OkHttp 结合使用时,可以为我们提供前所未有的网络请求洞察能力。本文将详细介绍如何将这两者结合使用…

LangGraph教程10:LangGraph ReAct应用

文章目录 ReAct 预构建的代理 向 ReAct 代理添加记忆 向 ReAct 代理添加系统提示 向 ReAct 代理添加人机交互 ReAct 官方文档地址:https://langchain-ai.github.io/langgraph/how-tos/#prebuilt-react-agent 中文文档地址:https://www.aidoczh.com/langgraph/how-tos/#react…

安卓第一个项目

测试所有摄像头 安卓CameraX:https://developer.android.com/media/grow/spatial-audio?hlzh-cn 1、MainActivity.java // 定义包名 package com.mms.densenapplication;// 引入 AppCompatActivity,支持兼容性更强的 Activity import androidx.appcompa…

Google Gemini 体验

文章中代码仓库 gemini 谷歌推出的 AI 只能模型 Gemini官网Gemini ChatGemini开发者文档Gemini SDK 所有模型 模型变体输入输出优化目标Gemini 2.5 Pro gemini-2.5-pro音频、图片、视频、文本和 PDF文本增强的思考和推理能力、多模态理解能力、高级编码能力等Gemini 2.5 Fla…

Trae安装指定版本的插件

前情 Trae是属于国产的跟 Cursor类似的AI编程IDE,我也是第一时间体验Trae的,虽然相比Cursor弱了一些,但是也绝对胜任了,前端因为排队问题我转战了Cursor,等到Trae出收费模式前,我已经办了Cursor会员了&…

【技术追踪】用于医学图像合成和分割的噪声一致孪生扩散模型(CVPR-2025)

孪生扩散模型,生成息肉图像用于提升分割性能! 论文:Noise-Consistent Siamese-Diffusion for Medical Image Synthesis and Segmentation 代码:https://github.com/Qiukunpeng/Siamese-Diffusion 0、摘要 深度学习已彻底革新医学影…

Crontab详解

crontab是Unix/Linux系统中用于设置周期性任务的工具,通过编辑配置文件实现定时执行命令或脚本。以下是其语法规则和核心要点: 一、基本格式 * * * * * command - - - - - | | | | | | | | | ----…

中国1km逐月潜在蒸散发数据集 - matlab按shp批量裁剪

中国1km逐月潜在蒸散发数据集 - matlab按shp批量裁剪 1. 数据概述 2 利用掩膜文件对数据进行裁剪 3 完整代码 4 结语 本篇继续处理气象数据,中国1km逐月潜在蒸散发数据集同前节介绍的中国1km降水数据集一样,都可以从国家青藏高原科学数据中心获得,数据具有同样的空间分辨率(…

Node.js链接MySql

前言: 在现代 Web 开发和后端服务中,Node.js 因其高性能和异步特性被广泛使用。MySQL 作为流行的关系型数据库之一,提供了稳定高效的数据存储和管理能力。将 Node.js 与 MySQL 结合,可以构建强大的数据驱动型应用。 一、环境准备…

Charles 的 Windows proxy 对爬取瑞数6 网站接口数据的作用分析

其实本文还是源于上个月的这篇文章 ➡️▼ 耗时两天半,利用 DrissionPage绕过瑞数6,爬取某药*局数据经历~ 不同点是,当时爬取的是列表页(已爬完),后面爬取的是详情页!懂的都懂,差别还…

PHP 测验

PHP 测验 引言 PHP 作为一种流行的开源服务器端脚本语言,被广泛应用于网页开发、服务器端编程等领域。为了帮助大家更好地理解和掌握 PHP,我们特此推出本 PHP 测验。通过以下问题,您可以检验自己的 PHP 知识水平,同时也能了解自己在哪些方面需要加强。 测验内容 问题一…