简介:InstallShield for VC++ 6.0 是专为 Microsoft Visual C++ 6 定制的安装包制作工具,面向使用 VC++6.0 开发 Windows 桌面程序、需要将 Release 版 EXE 打包发布的开发者。它可直接从 Visual C++6 的 Tools 菜单调用 InstallShield Wizard,省去单独配置工程与脚本的麻烦,适合刚接触安装包制作、希望快速生成标准安装向导的初中级程序员。资源包共 27 个文件,约 10.04MB,以 cab 压缩数据、exe 可执行程序、ini 配置、htm 说明文档、gif/bmp/jpg 界面素材为主,另含 dll、dat、ins 脚本等运行与安装所需组件,结构完整可直接使用。目前已有 1175 人学习下载。借助该工具,读者可掌握从编译 Release 程序到生成安装向导的完整流程,理解安装脚本、界面资源与压缩包的组织方式,为后续发布自研软件打下基础。
1. 为什么今天还有人翻出 InstallShield 给 VC++ 6.0 项目打包
如果你手上维护着一个用 VC++ 6.0 编译的老系统,客户或产线那边突然要求出一个能双击安装、带开始菜单快捷方式、能写注册表、能注册 OCX 控件的安装包,你大概率会经历一段相当难受的选型过程。现代打包工具对 6.0 时代的产物兼容性参差不齐,有的不认老式依赖,有的对 16 位遗留组件直接罢工,还有的装完在目标机上跑不起来。这时候很多人会回头去找 InstallShield——尤其是那个能和 VC++ 6.0 无缝配合的版本。它不是什么新东西,但在“老工程 + 老编译链 + 老运行库”这个组合里,它反而是最省心的那条路。这篇笔记拆的就是这套安装包制作工具,讲清楚它到底能干什么、怎么把 VC++ 6.0 的产物塞进去、参数在哪改、以及我踩过的那些坑。适合正在维护遗留系统、需要交付可安装产物的同学。
2. InstallShield 与 VC++ 6.0 的配合逻辑:先搞懂它替你做了什么
2.1 安装包的本质:把一堆散件变成可回滚的部署动作
VC++ 6.0 编译出来的东西,通常不是一个 exe 就完事。一个典型的老项目会包含:主程序 exe、若干自研 dll、MFC 运行库(mfc42.dll、msvcrt.dll 之类)、可能还有第三方 OCX 或 ActiveX 控件、配置文件 ini、数据库连接串、图标资源。你手动把这些文件拷到目标机,再双击注册控件,再改注册表,这套动作做一次还行,做一百次一定出错。InstallShield 干的事,就是把这些动作固化成一份脚本加一份文件清单,让安装程序按顺序执行,并且支持卸载时回滚。
它和 VC++ 6.0 的配合点主要在三个地方:第一,它能识别 VC++ 6.0 生成的工程输出目录结构,你不需要手动去猜哪个 dll 该放哪;第二,它内置了对 MFC 运行库的合并模块支持,能自动把依赖的运行库带上;第三,它生成的安装脚本可以用类似 C 的语法写条件判断,比如判断目标机是否已装某个运行库,没装才释放。这三点加起来,就是它比“手动拷贝 + 批处理”强的地方。
2.2 为什么不是别的打包工具:选型时的几个硬指标
我试过用别的工具给 VC++ 6.0 项目打包,翻车点集中在几个地方。一个是依赖扫描,现代工具喜欢用 API 钩子去抓运行时依赖,但 VC++ 6.0 的程序很多是静态链接 MFC 的,扫描结果要么漏要么多;另一个是注册表写入,老程序习惯把配置写在 HKLM\Software 下面,有些新工具默认只写 HKCU,装完程序读不到配置直接崩;还有一个是 OCX 注册,新工具对 32 位 OCX 的注册路径处理不干净,导致控件加载失败。
InstallShield 在这几个点上都是按老规矩来的:依赖靠工程文件里显式列出的文件清单,注册表可以精确指定根键和子键,OCX 注册走的是 regsvr32 的等价调用。所以选它的理由不是它功能多强,而是它“不瞎猜”。对于遗留系统,不瞎猜比功能多重要得多。
2.3 工程结构:安装脚本、文件清单、组件三者的关系
一个 InstallShield 工程里,你主要跟三个东西打交道。第一个是安装脚本,通常是一个 .rul 文件,里面用 InstallScript 写安装逻辑,比如“如果目标机没有 mfc42.dll,就从压缩包里释放一个”。第二个是文件清单,它决定了哪些文件被打进安装包、装到哪个目录、是否注册。第三个是组件(Component),一个组件可以包含多个文件,组件可以设置成“必须安装”或“可选安装”,也可以设置安装条件。
这三者的关系是:脚本控制流程,文件清单控制内容,组件控制粒度。新手容易犯的错是把所有文件塞进一个组件,结果卸载时要么全删要么全留,没法做增量。我一般会按功能拆组件,比如“主程序”“运行库”“示例配置”各一个组件,这样后续维护和排错都清楚。
3. 从 VC++ 6.0 工程到可安装包:手把手走一遍完整流程
3.1 准备阶段:把 VC++ 6.0 的输出整理成可打包的目录
在打开 InstallShield 之前,先把 VC++ 6.0 的 Release 输出整理干净。我习惯在工程目录下建一个 deploy 文件夹,把要打包的东西按目标机的目录结构放好。比如:
deploy/ ├── bin/ │ ├── main.exe │ ├── core.dll │ └── ui.dll ├── lib/ │ └── mfc42.dll ├── config/ │ └── app.ini └── ocx/ └── chart.ocx这样做的好处是,InstallShield 里配置目标路径时可以直接按这个结构映射,不容易乱。注意 mfc42.dll 这类运行库,如果你的程序是动态链接 MFC 的,必须带上;如果是静态链接的,可以不带,但目标机如果缺其他依赖还是可能出问题。判断方法是用 Dependency Walker 打开 exe,看它实际引用了哪些 dll,只带必要的。
3.2 新建工程:选对模板,别在第一步就埋雷
打开 InstallShield,新建工程时选 “Visual C++ 6.0” 相关的模板。不同版本模板名字略有差异,常见的是 “InstallShield for Visual C++ 6.0” 或者 “Legacy Visual C++ Project”。选错模板的后果是,后面配置依赖和运行库时选项对不上,你会花很多时间在找选项上。
新建完成后,工程向导会问你几个问题:应用程序名称、公司名称、默认安装目录、是否包含卸载程序。这里注意两点:默认安装目录不要用 Program Files 的硬编码路径,用 [ProgramFilesDir] 这样的变量,否则在非英文系统上会出问题;卸载程序一定要勾上,否则用户装完没法干净卸载,后面维护成本很高。
3.3 文件与组件配置:把 deploy 目录映射进安装包
在工程视图里找到 “Files” 或 “Components” 面板,把 deploy 目录下的文件按类别加进去。我一般会建三个组件:
| 组件名 | 包含内容 | 安装条件 |
|---|---|---|
| AppCore | bin 下的 exe 和 dll | 始终安装 |
| Runtime | lib 下的 mfc42.dll 等 | 目标机缺失时安装 |
| OCXReg | ocx 下的控件 | 始终安装并注册 |
配置时,每个文件的 “Destination” 要写清楚,比如 bin 下的文件目标路径设为 [INSTALLDIR]\bin。OCX 文件要勾选 “Self-Registered” 或手动在脚本里调 regsvr32。这里有个细节:如果 OCX 依赖其他 dll,要把依赖也放进同一个组件,否则注册时会失败。
3.4 脚本编写:用 InstallScript 处理条件安装和注册表
InstallShield 的脚本是 InstallScript,语法类似 C。下面这段是我常用的一个片段,作用是:检查目标机是否已有 mfc42.dll,没有才释放;然后注册 OCX;最后写一个注册表项。
// 检查 mfc42.dll 是否存在,不存在则从安装包释放 if (FileExists("C:\\Windows\\System32\\mfc42.dll") == FALSE) then // 从安装包中释放运行库文件到系统目录 CopyFile("mfc42.dll", "C:\\Windows\\System32\\mfc42.dll"); // 注册该 dll,确保 COM 组件可用 RegisterDLL("C:\\Windows\\System32\\mfc42.dll"); endif; // 注册 OCX 控件,注意路径要用目标机实际路径 RegisterDLL("[INSTALLDIR]\\ocx\\chart.ocx"); // 写入注册表,配置程序启动参数 RegistrySet("HKEY_LOCAL_MACHINE\\Software\\MyApp", "InstallPath", "[INSTALLDIR]", REG_SZ);逻辑说明:FileExists 判断文件是否存在,CopyFile 负责释放,RegisterDLL 完成注册。参数上,路径要用双反斜杠转义,[INSTALLDIR] 是安装目录变量。RegistrySet 的第三个参数是值,第四个是类型,REG_SZ 表示字符串。注意注册表根键要根据程序实际读取的位置来写,写错位置程序读不到。
3.5 编译与测试:在干净虚拟机上验证安装包
脚本和文件配置完后,点编译生成 setup.exe。但别急着交付,先在干净虚拟机上测。我一般会准备两个虚拟机:一个全新系统,一个装了部分运行库的系统。测试步骤:
- 在全新系统上运行 setup.exe,看是否报缺 dll。
- 安装完成后,检查开始菜单快捷方式是否生成、注册表项是否写入。
- 运行主程序,看是否能正常启动、OCX 控件是否加载。
- 走一遍卸载,看文件是否删干净、注册表项是否清除。
如果全新系统上装完能跑,部分运行库的系统上也能跑,基本就稳了。测试时记得关掉虚拟机的快照回滚,否则测不出真实状态。
4. 避坑与排查:那些让我加班到凌晨的安装包问题
4.1 现象:安装过程提示“无法注册 OCX”,但手动 regsvr32 能成功
原因:InstallShield 在注册 OCX 时,默认的工作目录可能不是 OCX 所在目录,导致控件依赖的相对路径 dll 找不到。手动注册时你在当前目录下执行,所以能成功。
解决:在脚本里注册前先切换工作目录,用 ChangeDirectory 或直接在 RegisterDLL 里写绝对路径。另外确认 OCX 依赖的 dll 也在同一目录或系统目录下。
4.2 现象:装完后程序启动报“找不到 mfc42.dll”,但安装包里明明带了
原因:释放路径写错了。32 位系统下 mfc42.dll 应该释放到 System32,64 位系统下 32 位程序需要释放到 SysWOW64。如果只写死 System32,在 64 位系统上就会找不到。
解决:用系统变量判断,或者统一释放到 [WindowsFolder]\System32 并额外处理 SysWOW64。InstallShield 有 Is64Bit 之类的判断函数,可以按位数走不同分支。
4.3 现象:卸载后注册表项残留,重装时读到旧配置
原因:安装时写了注册表,但卸载脚本里没有对应的删除动作。InstallShield 不会自动帮你清理所有写入项,尤其是自定义的 HKLM 项。
解决:在卸载脚本里显式调用 RegistryDelete 删除写入的项。如果项下面有子键,要递归删。测试时专门走一遍“装-卸-再装”,看配置是否被旧值污染。
4.4 现象:安装包在中文系统上正常,在英文系统上乱码或路径错误
原因:脚本里用了硬编码的中文路径或字符串,或者安装目录变量在非中文系统上解析结果不同。
解决:所有路径用系统变量,所有提示字符串用资源文件管理,不要硬编码。测试时至少覆盖中文和英文两种系统语言。
4.5 现象:编译出的 setup.exe 体积异常大,或者安装时解压很慢
原因:把调试符号、pdb 文件、临时文件也打进去了。VC++ 6.0 的 Release 目录里有时会混入这些。
解决:打包前清理 deploy 目录,只保留运行时需要的文件。可以用脚本自动过滤,比如只拷贝 .exe、.dll、.ocx、.ini 这几类。
5. 进阶技巧:让老安装包也能有“后悔药”和可验证性
5.1 用响应文件做静默安装,方便批量部署
InstallShield 支持用响应文件(.iss)做静默安装。生成方法是先手动走一遍安装,在临时目录里找到 setup.iss,然后把它和 setup.exe 放一起,用setup.exe /s /f1"setup.iss"执行。这个技巧在产线批量装机时特别有用,不用每台机器点下一步。
# 静默安装,/s 表示 silent,/f1 指定响应文件路径 setup.exe /s /f1"C:\deploy\setup.iss"参数说明:/s 是静默模式,/f1 后面跟响应文件的绝对路径。注意响应文件里的路径也要用变量,否则换一台机器就失效。
5.2 给安装包加版本校验,避免装错版本
在脚本开头加一段版本检查,读取目标机已装版本的注册表项,如果比当前包新就提示用户先卸载。这样可以避免覆盖安装导致的 dll 版本混乱。
// 读取已安装版本号,与当前包版本比较 if (RegistryGet("HKEY_LOCAL_MACHINE\\Software\\MyApp", "Version", szVersion) == 0) then if (szVersion > PACKAGE_VERSION) then MessageBox("已安装更新版本,请先卸载。", SEVERE); abort; endif; endif;逻辑说明:RegistryGet 返回 0 表示读取成功,szVersion 是读到的值,PACKAGE_VERSION 是安装包自身版本。比较后如果已装版本更高,弹提示并中止安装。
5.3 验证安装包完整性的一个笨办法
我习惯在安装包编译完后,算一下 setup.exe 的 MD5,和 deploy 目录的压缩包 MD5 一起记在交付文档里。目标机装完后,用同样的方法算一下安装目录下关键文件的 MD5,对比是否一致。这个方法笨,但能快速判断是安装过程丢了文件还是源文件本身有问题。从那以后我每次交付安装包都强制走一遍 MD5 比对,省得来回扯皮。希望帮到你。
本文还有配套的精品资源,点击获取