简介:这份压缩包提供一套基于VC++的简易程序安装向导源代码,面向刚接触MFC、希望理解Windows安装流程与对话框组织方式的新手开发者。它围绕分步引导安装这一常见软件环节展开,演示了向导界面搭建、页面切换及TAB控件使用等过程,适合用作课程练习、毕业设计参考或小型工具安装界面的改造基础。包内共26个文件,以h头文件与cpp源文件为主,辅以rc资源脚本、ico图标、dsp和dsw工程文件以及avi演示素材,整体约48KB,工程结构完整,可直接用VC++打开研读。已有213人学习该资源,说明其对入门者具有一定参考价值。读者可从中梳理安装向导从工程配置、资源定义到各对话框类协作的实现脉络,理解TAB控件在多页面切换中的消息处理与布局技巧,并参考其中的资源脚本、图标和动画素材组织方式,为自行编写安装程序或改造传统界面提供可复用的代码骨架与调试思路。
1. 解压出 .dsw 就双击:这套安装向导源码最先卡住的不是编译
拿到一个叫「一个简单的 VC 程序安装向导源代码」的压缩包,多数人的第一反应是解压、双击里面那个 .dsw,等着 IDE 自己把环境铺好。现实常常相反:机器上要么根本没装 VC6.0,要么装了 VS2019 却漏勾 MFC 组件,双击之后弹「无法打开工程」,或者一编译就被 LNK 错误刷屏。这套源码的价值在于它把「取安装源目录 → 建目标目录 → 复制文件 → 写注册表 → 生成卸载入口」这条链路完整走了一遍,属于 VC 源码里文件操作最典型的一类练习,环境搭建本身不复杂,但版本和字符集两件事极度敏感,选错一步就是满屏乱码加链接失败,后面所有调试都白做。下面按我实际装过一遍的顺序,从环境选型一路写到装完怎么验证。
2. 装编译环境:VC6.0 与 VS2019 两条路线的组件勾选
先说结论:这套安装向导源码有两条能走通的路,一条是原汁原味的 VC6.0,一条是把工程升级到 VS2019/2022。选哪条不取决于哪条更先进,取决于你要干什么——只想读代码、改改文件操作逻辑、把自己的程序塞进去打个包,走 VS 路线二十分钟能跑起来;要严格复现当年的编译产物、避免升级向导改动工程配置,那就得准备一台装 VC6.0 的环境。我两种都装过,VC6.0 那套折腾的时间反而更长。
2.1 VC6.0 路线:安装目录、组件勾选和补丁顺序
VC6.0 的安装程序年代久远,在现在的系统上直接双击,最常见的现象是进度条停在「更新系统注册表」那一格不动,等十分钟也没反应。原因不是卡死,是安装程序在写注册表时被系统的权限和兼容性策略挡住了。常见做法是右键 setup.exe,在兼容性里设成 Windows XP 兼容模式并勾上以管理员身份运行,再重新装一遍,基本都能过。
安装路径这一项值得单独说。默认路径带Program Files这种含空格的目录,老版本工具链在处理带空格路径时偶尔会出问题,我一般直接改成D:\VC6,短、纯 ASCII、没空格,省掉后面一堆玄学。安装类型选 Custom,组件列表里这几项必须勾上:
| 组件 | 是否必选 | 作用 |
|---|---|---|
| Visual C++ 6.0 | 必选 | 编译器 cl.exe、链接器 link.exe |
| MFC 及共享库 | 必选 | 安装向导的界面基本都建立在 MFC 上 |
| ATL | 建议 | 部分工程会引用 ATL 的注册表封装 |
| ActiveX 控件 | 可选 | 只有工程里拖了控件才需要 |
| 在线帮助文档 | 可选 | 装不装不影响编译,占空间大 |
装完主程序别急着开 IDE,先打 SP6 补丁,顺序不能反。VC6.0 原始版本的 STL 和部分头文件在编译现代写法的模板代码时会直接报内部错误,这类错误看起来像源码写错了,实际上是编译器版本太老。补丁打完,打开 IDE 看「帮助 → 关于」,版本号里带 Service Pack 6 才算落地。
还有一个细节:VC6.0 默认把工程文件里的路径写成短文件名格式,比如C:\PROGRA~1\...。换台机器、换个安装盘符,这个路径就失效,编译时报「无法打开包含文件」。装完环境后第一件事是确认工程里的附加包含目录写的是相对路径,不是这种机器相关的绝对短路径。
2.2 VS2019/2022 路线:桌面 C++ 工作负载加 MFC 单组件
VS 路线的坑不在安装本身,在组件勾选。安装器首页只让你选「工作负载」,那个「使用 C++ 的桌面开发」勾上之后,默认并不包含 MFC。安装向导源码里大量CDialog、CFileDialog、AfxMessageBox,缺了 MFC 就是一片「无法打开包括文件 afxwin.h」。
正确做法是进安装器后切到「单个组件」标签页,把下面这几项补齐:
- 「适用于最新 v14x 生成工具的 C++ MFC(x86 和 x64)」
- 「适用于最新 v14x 生成工具的 C++ ATL(x86 和 x64)」
- 「Windows 10 SDK」(版本挑一个已安装的就够,不用全勾)
- 「MSVC v142/v143 生成工具」(跟随默认即可)
MFC 组件有个容易忽略的点:它必须和生成工具版本对得上。装了两个版本的 MSVC 但只给其中一个勾了 MFC,切换平台工具集之后照样报缺头文件。我一般只保留一个生成工具集,减少这种错配。
安装位置也建议改一下,默认塞进系统盘,一个完整工作负载加 MFC 之后占用不小。改到非系统盘时注意安装器会拆成「安装目录」和「共享组件目录」两处,两个都要改,漏一个会出现组件装到 C 盘的情况。
2.3 工具链自检:cl.exe 能不能在命令行里跑通
IDE 能打开不代表工具链就绪。装完之后做一个不依赖 IDE 的自检,能提前暴露环境变量的问题,比等到编译时才报错省事得多。
VS 路线打开开始菜单里的「x86 Native Tools 命令提示符」,VC6.0 路线则运行安装目录下VC98\Bin\VCVARS32.BAT,然后在同一个窗口里执行:
:: 确认编译器能被找到;找不到说明 PATH 里没有工具链目录 where cl :: 直接跑一次编译器,正常会打印版本号和用法摘要 cl :: 打印生成工具根目录,用来核对 MFC 是否装在同一个版本下 echo %VCToolsInstallDir%where cl有输出、cl能打印出版本行,两条都满足才算环境就绪。如果where cl空着,说明当前窗口不是开发者命令提示符,普通 cmd 里敲 cl 是永远找不到的,别去手改系统 PATH,那样容易把别的工具链搅乱。VC6.0 那边对应的自检是cl打印出Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00,版本号是 12 开头就对了。
3. 工程导入:把安装向导源码摊到本地并让它认得出资源
环境装好只是第一步,安装向导这类工程比普通控制台程序多了资源脚本、图标、对话框模板,任何一个路径对不上,编译期都会以「找不到文件」的形式炸出来。导入之前先把目录整理规矩,比事后一条条改路径快得多。
3.1 目录整理:保留 .rc 与 res 的相对位置
解压目标路径这一项就劝退过不少人。压缩包里的文件名带中文或者解压工具自动改了编码,会导致 .rc 里引用的图标名对不上,编译时报 RC1015「无法打开包含文件」。我一般解压到一个纯 ASCII、无空格的短路径,比如D:\work\setupwiz,解压工具选 7-Zip 这类不擅自改名的,别用系统自带的解压。
目录摊开之后大概长这样,各类文件的角色要分清楚:
| 文件/目录 | 角色 | 能不能动 |
|---|---|---|
| .dsw / .sln | 工作区或解决方案入口 | 保留,别改名 |
| .dsp / .vcxproj | 工程配置,编译器参数都在里面 | 保留,改动走 IDE |
| .rc | 资源脚本,对话框、菜单、字符串表 | 不能删,路径敏感 |
| resource.h | 资源 ID 定义,.rc 和代码共用 | 保留,别手改 ID |
| res/ | 图标、位图等资源实体 | 必须和 .rc 保持相对位置 |
| .cpp / .h | 文件操作和向导流程的实现 | 主要改这里的代码 |
| .def | 导出表,安装向导一般用不到 | 可留可删 |
.res 和 res 目录的相对位置一旦被破坏(比如有人只把 .cpp 拷出来单独建工程),.rc 编译就会失败。这类问题的报错信息通常只给一个文件名,不给完整路径,看着很莫名其妙,实际就是资源目录没跟着走。
3.2 工程文件识别:.dsp 打开方式与字符集这一项必须先定
.dsp是 VC6.0 的工程文件,.dsw是它上一级的工作区。用 VS2019 打开 .dsw 时会弹「升级向导」,确认后它会生成 .vcxproj,并把原文件备份成 .dsp.bak 之类。这一步本身不可逆但可回退,备份文件别删。
升级完必须马上检查一个设置:字符集。VC6.0 时代的工程默认走多字节字符集(_MBCS),字符串全是char;升级到新工具链后,工程属性里的「字符集」有可能被设成「使用 Unicode 字符集」,这时源码里的LPSTR、sprintf、strcpy和 MFC 的CString转换会对不上,编译报一堆「无法从 const char* 转换为 LPCWSTR」。安装向导弹出来的第一句提示就变成乱码或者问号,看着像编码坏了,实际是字符集配置被改了。
处理方式二选一,别混着来:
- 保持原工程风格:项目属性 → 配置属性 → 高级 → 字符集,改成「使用多字节字符集」,源码一行不动。
- 彻底转宽字符:把所有
char缓冲改成TCHAR,sprintf改_stprintf,字符串字面量加_T()。工作量大,除非要长期维护,不建议为了跑通做这个。
3.3 头文件与附加库目录:写死绝对路径的工程怎么救
老工程里写死路径是常态。打开项目属性 → C/C++ → 常规 → 附加包含目录,如果看到C:\PROGRA~1\...或者别人机器上的D:\xxx\include,全部换成基于工程目录的宏:
$(ProjectDir)include $(ProjectDir)..\common$(ProjectDir)在编译时展开成 .vcxproj 所在目录,带结尾反斜杠,跟着工程走,换机器不用改。链接器那边的附加库目录同理,用$(ProjectDir)lib。这一步做完,工程才算真正「可迁移」,否则它只在原作者那台机器上能编译。
还有一类报错来自平台工具集。老工程升级后默认可能指向一个没装的工具集版本,报 MSB8020。项目属性 → 常规 → 平台工具集,改成下拉里实际存在的那个(比如 v143),改完重新生成。
4. 文件操作配置:安装向导把文件搬到目标机的三类参数
这套 VC 源码的核心就是文件操作,安装向导干的事说白了就三件:知道自己从哪来、知道要往哪去、把文件按规则搬过去并留下卸载痕迹。这三件事对应三组参数,任何一组配错,装出来的东西要么缺文件,要么卸载不干净。
4.1 路径拼接:GetModuleFileName 拿到的源目录怎么用
安装向导启动后第一件事是确定「安装包自己在哪个目录」,因为要复制的源文件就在它旁边。标准做法是GetModuleFileName拿自身路径,再截掉文件名部分。
// 取当前 exe 的完整路径,再砍掉文件名只留目录 TCHAR szSrcDir[MAX_PATH] = {0}; ::GetModuleFileName(NULL, szSrcDir, MAX_PATH); TCHAR* pSlash = _tcsrchr(szSrcDir, _T('\\')); if (pSlash != NULL) *(pSlash + 1) = _T('\0'); // 保留结尾反斜杠,后面拼路径直接用 // 拼接目标文件路径。VC6 的 _sntprintf 在截断时不保证补 0,必须手动兜底 TCHAR szDstFile[MAX_PATH] = {0}; _sntprintf(szDstFile, MAX_PATH - 1, _T("%sapp\\%s"), szTargetRoot, _T("main.exe")); szDstFile[MAX_PATH - 1] = _T('\0');几个参数值得说明。第一个参数传NULL表示取当前进程的模块句柄,安装向导里就是主 exe 自己;第二个参数是缓冲区;第三个是缓冲区长度,传MAX_PATH就够,因为 Windows 下大部分路径 API 的上限就是 260 个字符。_tcsrchr从右往左找最后一个反斜杠,找到后把它后面那位写成\0,得到的就是带结尾分隔符的目录。
这里有个 VC 源码里常见的隐蔽 bug:_sntprintf在 VC6 的 CRT 实现里,如果拼出来的字符串长度正好等于或超过缓冲区,结尾的那个 0 可能不会被写进去,后面的CopyFile读到一个没有终止符的字符串,行为不可预期,表现为「有时候能装有时候装一半」。手动补一次szDstFile[MAX_PATH - 1] = _T('\0')就能规避,代价只有一行。
另一个坑是路径里的空格。目标目录如果带空格,拼出来的完整路径在某些外部命令调用(比如ShellExecute调 regsvr32 注册 DLL)里会被截断。常见做法是在拼命令字符串时给路径加引号,或者用GetShortPathName转成 8.3 短路径再传。
4.2 复制与覆盖:CopyFile 的返回值、只读属性和占用文件
复制文件用CopyFile最直接,但安装场景下它有两个必须处理的前置条件:目标文件可能是只读的,目标文件可能正被别的进程占用。
// 目标已存在且是只读的,CopyFile 会直接失败,先清属性 DWORD dwAttr = ::GetFileAttributes(szDstFile); if (dwAttr != INVALID_FILE_ATTRIBUTES && (dwAttr & FILE_ATTRIBUTE_READONLY)) ::SetFileAttributes(szDstFile, dwAttr & ~FILE_ATTRIBUTE_READONLY); // 第三个参数 FALSE 表示目标存在就覆盖 if (!::CopyFile(szSrcFile, szDstFile, FALSE)) { DWORD dwErr = ::GetLastError(); if (dwErr == ERROR_SHARING_VIOLATION || dwErr == ERROR_ACCESS_DENIED) { // 文件被占用:登记为重启后替换,源和目标都必须是绝对路径 ::MoveFileEx(szSrcFile, szDstFile, MOVEFILE_DELAY_UNTIL_REBOOT); } }CopyFile第三个参数bFailIfExists,传TRUE是「目标存在就失败」,传FALSE才是覆盖。安装向导里几乎都是覆盖语义,看到源码里传TRUE又抱怨「装第二遍装不上」的,问题就在这。GetLastError返回 32 是ERROR_SHARING_VIOLATION,意思是目标文件被别的进程打开着,典型场景是用户没关掉旧版本的主程序就开始装新版。
MoveFileEx加MOVEFILE_DELAY_UNTIL_REBOOT是处理占用文件的标准招数,系统在下次启动时替你完成替换。两个前提:调用进程要有管理员权限,并且源路径和目标路径都得是完整绝对路径,传相对路径这个标志会静默失败。用完这个标志记得在安装完成页提示用户重启,否则用户以为装完了,其实新文件还没生效。
需要显示进度条的话换成CopyFileEx,多一个回调函数参数,回调里返回PROGRESS_CANCEL就能中断复制。安装向导如果文件多、体积大,这个体验差别很明显。
4.3 提权与卸载信息:写注册表前的清单和编译期开关
安装向导要往HKEY_LOCAL_MACHINE下写东西,没提权就是一路ERROR_ACCESS_DENIED。提权不是靠右键「以管理员身份运行」,得在程序里声明,否则用户双击时根本不会有 UAC 提示,程序自己在后台默默失败。
做法是给工程加一个 manifest 文件,内容里指定请求的执行级别:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <!-- requireAdministrator 表示启动即申请管理员,缺这行写 HKLM 必失败 --> <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> </assembly>manifest 加进工程后,链接器会自动嵌入。验证方式是编译出的 exe 图标上带个小盾牌,双击弹 UAC。如果没弹,检查是不是被「asInvoker」覆盖了,或者程序是被别的进程以普通权限拉起来的——后一种情况子进程继承父进程权限,manifest 也救不了。
注册表写入本身不复杂,RegCreateKeyEx建键,RegSetValueEx写值,重点在返回值判断:
HKEY hKey = NULL; // 第四个参数 0 表示 REG_OPTION_NON_VOLATILE,重启后键还在 LONG lRet = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\DemoVendor\\DemoSetup"), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); if (lRet != ERROR_SUCCESS) { // 5 是权限不足,多数情况是 manifest 没生效 ::AfxMessageBox(_T("写入安装信息失败,请以管理员身份重新运行。")); return FALSE; } ::RegSetValueEx(hKey, _T("InstallPath"), 0, REG_SZ, (const BYTE*)szTargetRoot, (DWORD)((_tcslen(szTargetRoot) + 1) * sizeof(TCHAR))); ::RegCloseKey(hKey);RegSetValueEx写字符串时长度参数要算上结尾 0,(_tcslen + 1) * sizeof(TCHAR),少算一个字节写进去的字符串就会缺尾巴,卸载时读出来路径不全,卸载程序找不到文件,留下一堆残留。这类问题在文件操作里极其常见,属于不看注册表根本发现不了的隐性故障。
编译期开关方面,安装向导一般留了调试输出。#ifdef _DEBUG包裹的日志写文件代码,发布版要确认走的是 Release 配置并且这个宏没被手动打开,否则装完在用户目录里凭空多出几个 log 文件,卸载又不删,很容易被当成残留垃圾。
5. 踩坑排查:链接、字符集、运行库和权限的五次翻车
前面几章是把环境按正确姿势装好,这一章记录的是没装对时的样子。下面五条都是我在复现这套安装向导源码时真实撞过的,每条按现象、原因、解决写,对着症状找能省不少时间。
5.1 链接报 LNK2001 / LNK1120:子系统与入口点对不上
现象:所有 .cpp 都编译通过,链接阶段报LNK2001: 无法解析的外部符号 _WinMain@16或者_main,有时候还配一个LNK1120: 1 个无法解析的外部命令。
原因:工程的子系统配置和源码里的入口函数不匹配。安装向导是带界面的 MFC 程序,入口应该是WinMain,但工程属性里的「链接器 → 系统 → 子系统」被设成了「控制台」,链接器转头去找main,自然找不到。
解决:项目属性 → 链接器 → 系统 → 子系统,改成「Windows (/SUBSYSTEM:WINDOWS)」;同时确认「高级 → 入口点」是空的(让链接器自己按子系统推导),如果被手填成了mainCRTStartup要清掉。VC6.0 里对应的位置是 Project → Settings → Link 标签页的 Project Options,把/subsystem:console改成/subsystem:windows。
5.2 编译通过但界面乱码:字符集被升级成 Unicode
现象:程序能跑,但对话框标题、按钮文字、提示框里全是问号或者方块,源码里明明是正常中文。
原因:工程从 VC6.0 升级到 VS 后字符集被切成了 Unicode,源码里的字符串字面量是窄字符char,MFC 的CString在 Unicode 配置下是宽字符,赋值时发生了错误的隐式转换,中文直接丢失。
解决:优先把字符集改回「使用多字节字符集」,源码零改动,这是复现老工程最快的路。如果确实要 Unicode,就得系统性替换:char缓冲改TCHAR,sprintf改_stprintf,字面量全部套_T(),并检查所有第三方接口是否接受宽字符。半改半不改是最糟的状态,会同时出现两种编译错误。
5.3 换机就缺 VCRUNTIME140.dll:CRT 的链接方式
现象:在自己机器上编译运行一切正常,拷到一台干净的测试机上双击,弹「无法启动此程序,因为计算机中丢失 VCRUNTIME140.dll」。
原因:工程用的是动态链接 CRT(/MD),运行库不在系统里就起不来。安装向导这类工具偏偏最常在干净机器上跑,这个坑几乎是必踩。
解决:项目属性 → C/C++ → 代码生成 → 运行库,Debug 和 Release 都改成「多线程 (/MT)」,静态链接,exe 体积大一点但不再依赖外部运行库。代价是不能跨模块共享堆内存,安装向导这种单体程序不受影响。如果因为别的依赖必须用 /MD,那就得把对应版本的运行库安装包一起分发,别指望目标机自带。
5.4 写注册表返回 ERROR_ACCESS_DENIED:缺管理员清单
现象:安装流程走到写注册表那一步就中断,日志里RegCreateKeyEx返回 5,或者CopyFile往Program Files下复制时返回 5。
原因:进程以普通用户权限运行,HKEY_LOCAL_MACHINE和系统目录都需要管理员权限。没有 manifest 声明时,用户双击 exe 不会有任何 UAC 提示,程序就在低权限下默默失败。
解决:按 4.3 加requireAdministrator的 manifest,重新编译,确认 exe 带盾牌图标。已经加了还失败,检查是不是从某个安装器或者脚本里拉起来的,父进程权限会传染给子进程。测试时也别用管理员账户直接登录来「验证通过」,那种环境下根本测不出权限问题。
5.5 CopyFile 返回 32:文件被占用和只读属性
现象:覆盖安装时进度条卡在某个文件,CopyFile失败,GetLastError是 32;或者错误码 5,但进程明明是管理员。
原因:32 是文件被别的进程占用,通常是旧版本的程序没退出;5 在管理员下出现,多半是目标文件带只读属性,CopyFile覆盖只读文件会直接拒绝。
解决:先用GetFileAttributes判断只读并SetFileAttributes清掉,再复制;占用的情况用MoveFileEx加MOVEFILE_DELAY_UNTIL_REBOOT登记重启替换,并在界面上明确告诉用户「部分文件将在重启后完成更新」。别用「先删再拷」绕过,删完复制失败就是文件彻底丢失,比装不上更严重。
6. 装完怎么验证:一次静默安装加卸载回滚的检查清单
编译出 exe 只是开始,安装向导这种东西的价值全在「装完之后对不对」。我的做法是准备一台干净环境(虚拟机最省事),按一遍完整流程跑:安装、检查落地、卸载、检查残留。少任何一步,问题都会留到用户那边才暴露。
先跑安装。如果源码支持命令行静默参数(多数安装向导源码会留/S之类的开关,具体看源码里对lpCmdLine的解析),用批处理把整个流程串起来:
:: 静默安装到指定目录;/S 表示静默,/D 指定目标路径(按源码实际参数改) start /wait SetupDemo.exe /S /D=C:\Temp\DemoInstall :: 检查主程序是否落地,落地了才算装成功 if exist "C:\Temp\DemoInstall\main.exe" (echo [OK] 主程序已落地) else (echo [FAIL] 文件没复制过去) :: 再调卸载程序,验证回滚;卸载程序名以源码实际生成的为准 start /wait "C:\Temp\DemoInstall\uninstall.exe" /S :: 卸载后目录应该空了,还剩东西就是卸载逻辑漏删 if exist "C:\Temp\DemoInstall\main.exe" (echo [FAIL] 卸载有残留) else (echo [OK] 卸载已清理)这段批处理的关键在start /wait,不加的话脚本不会等安装程序结束就去检查文件,永远得到 FAIL,白白怀疑代码。/S和/D这两个参数名是我按常见实现写的,落到你手上这份源码,去InitInstance或者命令行解析那段看实际拼写,别照抄。
然后是检查清单。下面这几项我每次都会过一遍,覆盖的是最容易出问题的边界:
| 检查项 | 检查方法 | 通过标准 |
|---|---|---|
| 文件完整性 | 对比安装目录和源目录的文件数、大小 | 数量一致,主程序能启动 |
| 目标路径含中文/空格 | 装到C:\测试 目录\下 | 不报错,文件落地正确 |
| 非管理员账户 | 用标准用户账户跑一次安装 | 正常弹 UAC,或给出明确提示 |
| 写注册表 | 用注册表编辑器看产品键下的 InstallPath | 值和实际安装目录完全一致 |
| 卸载清理 | 卸载后看目录和注册表项 | 目录清空、注册表项删除、无残留 |
| 重复安装 | 同一目录连装两次 | 第二次不报错,文件被正确覆盖 |
「目标路径含中文和空格」这一项单独列出来,是因为它在开发机上几乎永远测不出来。开发时习惯装到D:\test,用户那边装到C:\Program Files\某某软件,路径里的空格和中文会把 4.1 里那些拼接问题全部引爆。我吃过这个亏,一个只在中文路径下出现的「安装完成但程序起不来」,查了两个小时才发现是拼路径时少了一层引号。
还有一个验证技巧:把安装过程的GetLastError全部打到日志文件里,Release 版也保留。安装向导最怕的不是失败,是失败了还不告诉用户失败在哪。有这个日志,用户在别人机器上出问题,让他把 log 发过来,基本能定位到具体某一行 API。
从那以后我每次拿到这类安装向导源码,都会先在干净虚拟机里完整跑一遍安装加卸载,再去看代码怎么改,而不是改完直接在自己机器上点下一步。这套习惯帮我挡掉过好几次「开发机正常、用户机全崩」的事故。希望帮到你。
本文还有配套的精品资源,点击获取