ARTICLE DETAIL

资讯详情

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

AutoUpgrader Pro在Delphi 12.3中的安装配置与自动升级实战指南

AutoUpgrader Pro在Delphi 12.3中的安装配置与自动升级实战指南 简介面向 Delphi 12.3 开发者的 AutoUpgrader Pro v5.02 自动升级控件包专为桌面应用提供在线更新检测、版本比对与升级下载能力适合需要快速构建软件自动更新机制的中高级 Delphi 程序员。该控件包共 158 个文件压缩后约 3.25MB包含 pas 源码、dcu 已编译单元、dpk/dproj 工程定义、dfm 窗体布局以及 res/ico 等资源文件同时提供帮助文档与演示工程便于查看调用方式和完成本地集成。已有 95 人学习下载具备一定参考热度。整套资源交付完整源码与工程骨架可灵活定制升级流程、扩展网络下载策略及界面反馈逻辑AutoUpgraderProDemo 示例与辅助工具单元能帮助开发者快速验证从请求到安装更新的全链路缩短基于 Delphi 12 的自动升级功能上线周期。1. 装上能用的自动升级控件比写业务代码更考验工程判断拿到「Delphi 12.3控件之AutoUpgrader Pro v5.02 to D12.7z」这个包名第一反应是又是个控件压缩包解压、装进 IDE、拖到窗体上完事。但真做过桌面软件自动升级的人都知道升级功能里最花时间的从来不是控件本身而是「版本号怎么比、文件被占用怎么替换、更新失败怎么回滚」。AutoUpgrader Pro 是一个专门给 Delphi 写的自动升级组件v5.02 这个版本明确针对 Delphi 12.xD12做了适配压缩包后缀 7z 说明文件用了高压缩率打包。适合谁适合还在维护 VCL/FireMonkey 桌面客户端、想给老程序补上自动更新能力、又不打算从零写一套升级协议的团队。这篇就按我实际装组件、配服务端、嵌代码的顺序把能复现的步骤和踩过的坑一次讲完。2. 解压与安装把 v5.02 组件包挂进 Delphi 12.3 的三个动作2.1 先核对压缩包结构与 IDE 位数再决定装哪个包解压之前先确认两件事Delphi 12.3 的安装版本是 Community 还是 Professional以及 IDE 跑的是 32 位还是 64 位。Delphi 12.x 的 IDE 默认是 32 位但支持的编译器有 Win32/Win64 之分控件的设计时包design-time package和运行时包runtime package要分别编译。打开压缩包后一般会看到 Bin、Lib、Source、Demos 这几个目录还有一些 .dpk 和 .bdsgroup 文件。AutoUpgrader Pro v5.02 to D12 这种命名通常意味着作者已经按 Delphi 12 的版本重新编译过一组包文件最常见的是AutoUpgraderPro_D12.dpk这类文件名。先把 7z 解压到一个不含中文和空格的路径比如D:\Components\AUP501_D12因为 Delphi 的 Library 路径对中文字符的兼容性并不好后面编译报奇怪的错多半从这里开始。解压完成后不要急着点 .dpk先打开 Delphi 12.3在 Tools Options Language Delphi Library 里把解压目录下的 Source 和 Lib 路径加进 Library path。这一步的作用是让 IDE 在编译任何项目时都能找到控件的源文件和预编译单元。注意 Win32 和 Win64 两个平台要分别加一次不然切到 64 位编译时照样找不到单元。路径加好后打开 Package 文件这时 IDE 会弹出 Package 编辑器点 Compile 再点 Install控件就能注册到 IDE 的组件面板里。2.2 用 IDE 的 Open Package 把控件装进组件面板打开 .dpk 的标准路径是 File Open 选择AutoUpgraderPro_D12.dpk这类包文件。如果压缩包里同时有 Design 和 Runtime 两个包先编译 Runtime 包通常是不需要 Install 的只有 Compile再编译 Design 包。Design 包是真正向 IDE 注册控件的那个编译后点 Install会弹窗提示安装到哪个组件页。我一般会新建一个叫 AUP 的组件页把所有相关控件归拢到一起方便后续找。如果 Install 按钮是灰的说明当前打开的包不是设计时包回到压缩包里找带 Design 字样的 .dpk。安装过程中常见的一个提示是「Cant load package ... 找不到指定的模块」。这个坑的根源通常是依赖的运行时包没有先编译或者 IDE 的 Library 路径没配上。解决办法是先把 Source 目录里所有 .pas 文件所在路径全部加进 Library path再把压缩包里自带的 .bpl 文件路径也加进去。如果依然报错打开 Windows 的事件查看器看 Application 日志里的详细错误码能定位到是缺哪个 DLL 或 BPL。编译时若提示需要特定版本的System.Win.ComObj或Winapi.Windows那是 Delphi 12 自带的单元路径没配错就不会出问题。2.3 通过命令行 bpl 方式做批量安装的替代方案如果团队里多人都在装这个控件或者你需要在 CI 机器上静默安装可以用命令行方式编译包。Delphi 12.3 自带的rsvars.bat先把环境变量加载好然后调用msbuild来编译项目组文件。典型命令是call C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\rsvars.bat msbuild AutoUpgraderPro_D12.dproj /t:Build /p:ConfigRelease /p:PlatformWin32这条命令用rsvars.bat把编译器和库路径加载到当前终端再用msbuild按 Release 配置编译 Win32 平台的包。参数说明/t:Build指定执行构建目标/p:ConfigRelease选择 Release 配置避免 Debug 包的断言代码拖慢更新逻辑/p:PlatformWin32精确指定目标平台。如果需要 Win64 的运行时包把 Platform 换成 Win64 再执行一次。编译成功的标志是输出目录里生成了对应平台的 .bpl 文件之后在新电脑上只需要把 .bpl 和 .dcp 文件拷贝到 Delphi 的默认搜索路径或者在 Library path 里指过去IDE 下次启动就能加载。但注意命令行编译只生成运行时包控件要出现在组件面板上仍然需要手动打开设计时包点 Install——这一步没有官方静默方案只能写个小脚本模拟 IDE 的注册表导入。3. 让升级流程先跑通version 文件、升级包与服务端目录规划3.1 升级流程里三个核心角色客户端、Updater、服务端AutoUpgrader Pro 这类组件的典型工作方式是把升级过程拆成两个程序主程序你的业务 exe和更新器Updater。主程序启动时向服务端请求版本信息比对发现新版本后把升级包下载到本地临时目录然后主程序退出由 Updater 进程完成「覆盖旧 exe 重新启动主程序」这一串动作。这样设计的原因很直接Windows 不允许正在运行的 exe 被覆盖所以替换文件必须发生在主程序退出之后、且由另一个进程来做。服务端不需要额外的数据库一个静态文件服务器只要能把version.json或version.ini和升级包按 URL 暴露出去就行。理解了这三个角色再看 AutoUpgrader Pro v5.02 的配置项就有方向了。控件上需要填的基本是四类信息升级源 URL指向版本信息文件的完整地址、当前程序版本号从资源文件或代码里读、下载目录默认在%TEMP%下、以及 Updater 程序的路径。v5.02 对 Delphi 12 的适配主要体现在字符串处理和 HTTP 库上旧版本控件在 Delphi 12 下有可能会因为System.SysUtils中字符串编码的调整而出现中文路径乱码新版本把这块修掉了。首次配置时建议先用一个固定 URL 的版本文件把流程跑通再接入动态版本号。3.2 version 文件与升级包的命名规则版本号怎么比对才不会误报版本信息文件是整个升级链路的判据格式没有统一标准但常见做法是写成这样[Version] Major5 Minor0 Build12 Revision0 DownloadPathhttp://your-server.com/updates/app_v5.0.12.zip UpdaterPathhttp://your-server.com/updates/updater.exe这个配置的含义是服务端上的程序版本是 5.0.12升级包和更新器分别存放在两个 URL 上。控件读取后会拿这里的5.0.12与客户端当前版本比对判定规则一般是「优先比 Major其次 Minor、Build、Revision任何一个大就提示更新」。DownloadPath和UpdaterPath分开写的意义在于版本升级时可以只换程序包更新器本身有独立的小版本号只有当更新器也必须更新时才会更换那个 URL。这里有个细节容易埋坑升级包的命名不要用app_setup_最新版本.exe这种含中文或带空格的名称HTTP 服务对 URL 编码的处理各不相同老一点的文件服务器直接返回 404。统一用app_v5.0.12.zip这种纯 ASCII 命名压缩包内主程序名保持和客户端 exe 一致。后端在做版本比对时如果自己解析 version 文件注意字符串比较和数值比较的差别——5.0.9和5.0.10按字符串比5.0.9反而大于5.0.10这会直接导致用户永远收不到新版本提示。换成数值逐段比较这个问题就不会出现。3.3 服务端目录与权限静态文件服务器也能当升级源服务端不建议用需要登录鉴权才能下载文件的路径因为客户端是无人值守地检查更新每次都要输入口令不现实。要么用公开读权限的静态目录要么用带短期签名 URL 的对象存储。目录结构按「版本号分目录」是最稳妥的/updates/ v5.0.10/ app_v5.0.10.zip version.ini v5.0.12/ app_v5.0.12.zip version.ini每个版本一个目录version.ini 里只写当前目录所属的版本。升级 URL 固定指向http://your-server.com/updates/v5.0.12/version.ini客户端要发新版时就改这个跳转或重定向。这个方式的好处是回滚方便——如果 5.0.12 出了严重问题把 URL 指回 v5.0.10 的目录所有客户端都会「降级」到上一个稳定版本。注意这里的降级要在客户端代码里做判断很多升级控件默认只允许升不允许降需要配置一个 AllowDowngrade 类似开关v5.02 里对应的选项在控件属性面板的 UpdatePolicy 分类下。另一件事是服务端的 HTTP 响应头。压缩包下载时某些小型 HTTP 服务器不返回Content-Length客户端拿不到文件总大小进度条一直是 0%看起来像卡死。静态文件服务一般没问题但如果你用的是自研的 HTTP 服务务必在响应里带上Content-Length。还有断点续传的问题AutoUpgrader Pro v5.02 是否支持取决于底层 HTTP 客户端实现Delphi 12 自带的TNetHTTPClient支持 Range 请求但控件封装后不一定把透传。稳妥做法是升级包控制在 20MB 以内内网环境下秒级下载不需要纠结续传外网大包就要在控件的事件里自己处理断点逻辑这个我在第 5 章会展开说。4. 把控件嵌进业务程序检查更新、下载进度与退出替换的代码骨架4.1 初始化控件与版本读取从 ini 到注册表的几种常见做法把 AutoUpgrader Pro 放到主窗体上之后第一步是初始化。初始化要做的核心事情是告诉控件当前程序版本号。读取版本号有几种常见来源一是从 exe 的文件版本信息里取这种方式在 Delphi 里要调GetFileVersionInfo二是程序自己把版本号写进注册表或同目录的config.ini三是把版本号作为编译期常量直接写在代码里。我一般用第一种因为从 exe 资源里拿到的版本号和你在项目设置里填的 VersionInfo 保持一致后续发版少一重维护。初始化代码大致这样procedure TMainForm.FormCreate(Sender: TObject); begin AutoUpgrader1.CurrentVersion : GetFileVersion(Application.ExeName); AutoUpgrader1.URL : http://your-server.com/updates/v5.0.12/version.ini; AutoUpgrader1.DownloadDir : GetTempDirectory AppUpdates\; AutoUpgrader1.UpdaterFile : GetTempDirectory AppUpdates\updater.exe; end;给这段代码做点说明GetFileVersion是封装了GetFileVersionInfoAPI 的函数返回一个四位点分格式的字符串例如5.0.12.0。DownloadDir指定下载目录UpdaterFile指定更新器 exe 的本地路径。这里的UpdaterFile不一定存在控件会在下载阶段从服务端的UpdaterPath一起拉下来。三个属性里最容易出错的是URL必须精确到 version.ini 文件本身而不是只写到目录。有些版本还要求 URL 末尾不能带斜杠否则拼接相对路径时会出现双斜杠服务端静态目录通常能容忍但自研服务就不一定。4.2 检查更新与下载进度的最小 Pascal 代码控件安装到窗体后一般会暴露一组公开方法。v5.02 版本核心动作通常分为检查更新CheckForUpdate和下载更新DownloadUpdate两步分开而不是合并成一步是为了给 UI 层更多控制点——比如用户在弹窗提示后点了「稍后再说」你就只需要关掉弹窗不用取消整个下载。下面的代码演示了从检查到启动下载的最小流程procedure TMainForm.btnCheckClick(Sender: TObject); begin AutoUpgrader1.OnCheckComplete : HandleCheckComplete; AutoUpgrader1.CheckForUpdate; end; procedure TMainForm.HandleCheckComplete(Sender: TObject; AUpdateAvailable: Boolean); begin if AUpdateAvailable then begin AutoUpgrader1.OnDownloadProgress : HandleDownloadProgress; AutoUpgrader1.DownloadUpdate; end else StatusBar1.Panels[0].Text : 当前已是最新版本; end; procedure TMainForm.HandleDownloadProgress(Sender: TObject; AProgress: Integer); begin ProgressBar1.Position : AProgress; end;这个流程里的关键点OnCheckComplete事件返回的AUpdateAvailable是控件内部完成版本比对后的结论不要在业务侧重复实现一遍版本比较逻辑那会增加一处出错的可能。DownloadUpdate调用后进度事件会多次触发AProgress是 0~100 的整数直接赋给TProgressBar.Position就行。注意下载完成后控件一般会触发OnDownloadComplete事件在这个事件里你需要调用ExecuteUpdate之类的动作来退出主程序并拉起更新器。有些版本把退出动作封装在ExecuteUpdate里有些则要求你手动调Application.Terminate后再由更新器接管。4.3 主程序退出后的替换动作先备份再替换的顺序更新器准备接管之前主程序要做一件很容易被忽略的事把当前运行的 exe 复制一份作为备份。常见做法是更新器把新文件先放到临时目录等主程序退出后再从临时目录覆盖到原路径。完整的替换顺序建议是下载新版本 exe 到临时目录校验大小和哈希主程序退出前把旧 exe 复制成app.exe.bak更新器把临时目录的新 exe 移动到原 exe 路径更新器启动新的主程序结束自己的进程。这个顺序比直接覆盖多了一步备份成本很低但收益极大——新版本启动即崩溃时用户至少还有app.exe.bak可以手动恢复。AutoUpgrader Pro 的更新器是否支持自动备份取决于版本设置里面有没有 BackUpBeforeInstall 选项如果你手里的 v5.02 没这个开关就在主程序退出前的OnBeforeShutdown事件里自己写一行CopyFile。代码位置如下procedure TMainForm.OnBeforeShutdown(Sender: TObject); begin CopyFile(PChar(Application.ExeName), PChar(ChangeFileExt(Application.ExeName, .exe.bak)), False); end;这行代码的作用是强制把当前正在运行的 exe 复制为.bak文件CopyFile第三个参数False表示目标文件已存在时直接覆盖。执行成功后更新器即便后续覆盖失败现场也保留了一份可用的旧版本至少不用重装整个客户端。备份文件不要放在安装目录里Windows 对 Program Files 根目录写操作默认受限更新器如果跑在高权限下还好普通权限下会写失败。建议备份到%LOCALAPPDATA%\YourApp\Backup\。5. 避坑AutoUpgrader Pro v5.02 在 Delphi 12.3 下常见的 5 个翻车现场5.1 装完组件面板里没有图标编译却正常现象在 Package 编辑器里点 Install 提示成功但打开窗体设计器找遍组件面板也看不到 AutoUpgrader 的图标。此时新建一个空白项目手动把单元名写进 uses 子句代码能编译说明控件已经装进 IDE 了只是没注册到可视面板。原因Design 包编译时RegisterComponents过程注册的组件页名和当前 IDE 的主题不兼容或者组件页被 IDE 隐藏了。解决在窗体设计器里右键组件面板选择「组件列表」搜索 AutoUpgrader找到后把它显式加进当前面板。如果找不到检查安装的是不是 Design 包——只有 Runtime 包被 Install 不会有任何组件出现。我一般把 Runtime 包和 Design 包放在两个不同的目录避免同名文件干扰安装时用「包含设计时包」的 .dpk 文件。5.2 永远提示「已是最新版本」旧程序不更新现象服务端明显已经上传了新版本version.ini 里的 Build 号也改了但客户端每次检查都返回「已是最新版本」。原因版本号比对逻辑中控件读的是客户端 exe 的 ProductVersion 资源段而你只改了服务端 version.ini没重新编译客户端——客户端自己的FileVersion还是旧值两边自然相等。另一个常见原因是 HTTP 缓存客户端请求 version.ini 时服务器返回了带Cache-Control: max-age的响应客户端本地缓存了旧文件。Delphi 12 自带的TNetHTTPClient默认会遵守 HTTP 缓存头Indy 的老组件则完全不理会。解决先用浏览器直接访问 version.ini 确认内容是最新的然后在服务端把响应头加上Cache-Control: no-cache。同时注意AUpdateAvailable返回值只能说明「服务端有更新」不能说明「本地版本读取正确」。可以在控件的OnCheckComplete里弹一个调试对话框把AutoUpgrader1.CurrentVersion和从服务端解析到的版本号都打出来一眼就知道是哪边不对。5.3 下载完成但 exe 替换失败提示文件被占用现象进度条走到 100%下载目录里也确实存在新版本的 zip但一次性退出后弹窗提示「无法覆盖文件」旧程序还能继续运行。原因主程序退出时某些子模块的进程没有完全结束。最常见的是托盘图标程序——主窗口关了但TrayIcon的窗口句柄没释放exe 进程实际上还活着。另一个常见场景是杀毒软件正在扫描 exe 文件短时间内给文件加了排他锁。解决先打开任务管理器确认你的程序.exe进程是否真的退出退出不了就在OnBeforeShutdown里调用Application.MainFormOnTaskBar : True再结束所有子窗体还不行就显式调用TerminateProcess强制结束。针对杀毒软件的问题更新器里加一个重试循环每 500 毫秒尝试一次复制最多重试 5 次。很多更新器所谓的「卡死在替换步骤」其实就是一次性重试都没做。把替换逻辑改成循环后这个场景基本消失。5.4 FireMonkey 项目里控件运行时抛异常现象同一个控件在 VCL 项目里一切正常换成 Delphi 12.3 的 FireMonkeyFMX项目后运行时在创建控件或调用更新时抛EDAVComError或「类不支持自动化」之类的异常。原因AutoUpgrader Pro v5.02 的安装包名虽然写了 to D12但组件主体很可能是 VCL 实现内部直接调用了TWinControl、TForm这些 VCL 专属类FMX 里没有这些类型控件被拖到 fmx 窗体上后 IDE 只给了一个「占位」运行时才真正崩溃。解决确认压缩包里是否有FMX或Platform目录的源文件。没有就放弃在 FMX 项目里直接放控件改用子进程方案把检查更新这块逻辑做成一个独立的 VCL exe比如update_worker.exe主 FMX 程序通过启动这个 exe 并读取它的退出码来得知是否要重启。这样 VCL 控件待在自己熟悉的环境里FMX 主程序只管进程通信。这个拆法看起来绕了一圈但比硬把 VCL 控件塞进 FMX 项目省心得多。5.5 编译程序报内存错误集中在关闭更新器时现象Delphi 12.3 里编译整个项目没问题但运行退出时偶尔弹「Access violation at address...」坐标每次都不一样关掉更新器窗口时尤其明显。原因升级控件的内部对象生命周期和主窗体的释放顺序冲突。常见是控件在FormDestroy里访问了一个已经被释放的消息队列句柄特别是下载线程还没完全结束时直接关窗体——线程对象释放了但 Windows 消息还在投递。解决在主窗体的FormCloseQuery里先主动调用控件的取消方法。AutoUpgrader Pro 有没有暴露 Cancel 取决于版本没有的话就下载一个TThread.Sleep(200)等待下载线程退场再继续。另一个稳妥做法是把控件的 Owner 设成一个专门的中介对象而不是主窗体让它和主窗体的释放顺序错开AutoUpgrader1 : TAutoUpgrader.Create(nil); AutoUpgrader1.Parent : Self; // 仅用于获取句柄不参与释放这段代码的意思是让控件独立于窗体的生命周期避免窗体释放时控件内部线程还在回调。设置Parent : Self是为了让它在 VCL 里能接收窗口消息但销毁不再跟随主窗体。注意这个写法只适用于 VCLFMX 下控件没有 Parent 概念还是走第 5.4 节的子进程方案最稳。内存错误这问题属于典型的「运行时随机复现、调试时永远不出现」建议直接在发布版本里做异常捕获在更新器的入口处包一层try...except把错误写进日志文件。6. 进阶用本地 HTTP 服务验证整条升级链路并给更新留后悔药6.1 用 IdHttpServer 搭一个最小升级源整套升级流程搭好后最难验证的是服务端不在手边的问题。我习惯在开发机本地起一个极简 HTTP 服务用 Delphi 自带的IdHTTPServer就能实现。这个服务只做两件事返回version.ini内容和输出升级包文件。把第 3 章里的 version.ini 固定成一个常量字符串升级包用一个本地小 zip 替代客户端里的 URL 指到http://127.0.0.1:8080/version.ini就能完整地走一遍「检查 → 下载 → 替换 → 重启」的全流程。跑通后再把 URL 换成生产地址步骤不变。procedure TDevServer.Execute(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); begin if ARequestInfo.Document /version.ini then begin AResponseInfo.ContentText : [Version]#13#10 Major5#13#10 Minor0#13#10 Build99#13#10 Revision0#13#10 DownloadPathhttp://127.0.0.1:8080/app_v5.0.99.zip; AResponseInfo.ContentType : text/plain; AResponseInfo.ResponseNo : 200; end else if ARequestInfo.Document /app_v5.0.99.zip then AResponseInfo.ServeFile(AContext, D:\Dev\Builds\app_v5.0.99.zip) else AResponseInfo.ResponseNo : 404; end;这段代码的意图很直白当请求的是/version.ini时返回伪造的动态版本信息其中 Build99 保证一定大于当前客户端的版本号当请求的是升级包时直接输出本地文件。AResponseInfo.ResponseNo手动指定状态码避免默认值引起歧义。验证完成后把Build改成旧值客户端就会回归「已是最新版本」两个方向都验到整个黑匣子才算真正打开。6.2 备份与回滚把「更新失败」变成「更新可回退」验证完成不代表万事大吉真正让团队敢上线自动更新的是回滚通道。每次更新前把旧 exe 备份到%LOCALAPPDATA%\YourApp\Backup\同时在本地写一个last_good_version.txt。下个版本出问题时用户不用重装只需双击一个rollback.bat内容就是把备份目录里的旧文件复制回安装目录。这个方案比在服务端挂「旧版本下载链接」更直接因为用户手里可能已经没有安装包了但备份就在本机。具体到 AutoUpgrader Pro v5.02 的集成上我的习惯是在主程序的版本信息资源里同时写入CompanyName和ProductVersion前者用来拼备份目录路径后者用来做前缀区分。这样每个版本的备份文件形如5.0.12_app.exe.bak不会互相覆盖。更新完成后的第一次启动如果程序能在 30 秒内正常进入主界面就把这个备份文件删掉——留下最后一个已知好用的版本比留一堆中间版本更符合直觉。回滚这件事做到这个程度就够了不要试图做一个自动回滚机制。自动回滚看起来很智能但回滚判断本身就是新的出错点——判断错了会把好版本回滚到坏版本。把回滚做成「手动但简单」的操作才是适合生产环境的成熟度和成本平衡点。我在这类项目上吃的亏不少最典型的一次就是「升级成功但新版启动崩溃」当时没做备份只能让用户手动重装。后来强制规定所有集成 AutoUpgrader 的项目必须带备份和回滚脚本再没出过需要现场救火的事。希望这个习惯能帮你在自己的项目里少踩一次坑。本文还有配套的精品资源点击获取
返回列表