首页 / 资讯中心 / 文章详情

ArcGIS Engine应用打包交付:运行时依赖、安装包制作与干净机验证

ArcGIS Engine应用打包交付:运行时依赖、安装包制作与干净机验证 ★ FEATURED ARTICLE
简介面向 ArcGIS Engine 开发者的应用程序打包专题文档聚焦 AE 桌面程序在部署阶段的运行环境准备、安装包制作与常见问题处理适合已具备 .NET 与 Visual Studio 基础、需要将地图功能交付给最终用户的中级开发者参考。压缩包内仅含 1 个 docx 文件约 2.24MB以图文步骤组织内容围绕 .NET Framework 3.5 SP1 与 ArcGIS Engine Runtime 的单独安装、联合打包、快捷方式配置及打包排错等环节展开。文档从实际项目发布流程出发梳理 Visual Studio 安装项目与 InstallShield 两种方式的操作思路并提示 Esri 依赖库排除、授权运行时安装等易错点便于读者对照检查安装包结构、减少交付环境差异带来的故障。已有 657 人学习可作为 AE 程序部署打包的速查材料。1. ArcGIS Engine 应用程序打包先搞清你交付的是 exe 还是整条运行时链有人把编译输出目录连同十几个运行时程序集压成 zip 发给客户客户双击后弹出找不到程序集的报错电话直接打回来这种返工在 AE 项目交付里太常见。ArcGIS Engine 应用程序打包真正要交付的是三层东西.NET Framework 运行时、带有效授权的 ArcGIS Engine Runtime、以及你自己的主输出。AE 的二次开发产物本身很轻重的是它脚下那套运行时和授权所以打包的核心动作不是复制文件而是决定这三层谁先装、谁由安装包负责、谁交给客户自己准备。下面按两条路线拆用 Visual Studio 2008 自带安装项目做分离式打包以及用 InstallShield 把框架与 Runtime 一起装进单个安装包适合正在交付桌面 GIS 系统的 AE 开发者。2. 依赖链拆解AE Runtime、.NET 3.5 SP1 与那堆程序集到底谁管谁打包翻车的人九成是在这一步偷懒没把依赖关系画清楚就开始拖文件。AE 应用在开发机上跑得好好的是因为开发机既有框架又有运行时还有一份调试期导入的授权所有缺失都被开发环境掩盖了。换到一台刚装完系统的客户机缺哪一层就报哪一层的错而错误信息往往指向你的 exe让人误以为是自己的代码问题。所以动手之前先把交付物分层后面无论走 VS 安装项目还是 InstallShield思路都是同一套。2.1 三层交付物框架、运行时、应用本体第一层是 .NET Framework 3.5 SP1它提供 CLR 和基础类库AE 的托管封装跑在它上面。第二层是 ArcGIS Engine Runtime这一层最容易被低估它不只是几个 dll还包含原生组件、COM 注册信息、桌面运行时所需的共享资源以及授权机制。第三层才是你的主输出exe 加自有业务 dll加上配置文件、图标、帮助文档、初始数据。层级典型内容建议由谁安装检测方式.NET Framework 3.5 SP1CLR、BCL安装包先决条件或系统功能注册表 NDP\v3.5 下的 Install 与 SP 值AE Runtime原生组件、托管程序集、COM 注册、授权独立安装包或作为先决条件串在应用之前产品注册键、运行时服务、授权状态应用本体exe、业务 dll、配置、数据你的 Setup安装目录与快捷方式分层的意义在于责任边界。第一层和第二层属于环境第三层属于你。把环境层的东西塞进应用安装包短期看省事长期看就是给后面的升级和排错埋雷。我一般的做法是应用安装包里只放第三层外加一段明确的检测逻辑环境不满足就给出可读的提示而不是让程序在启动瞬间崩溃。2.2 为什么运行时的程序集不能跟着 exe 一起发Visual Studio 默认会把引用的程序集以 Copy Local 的方式复制到输出目录于是 bin\Release 里躺着一堆运行时 dll。很多人顺手就把整个目录打进安装包觉得「反正都带上了肯定不缺」。这个做法在 AE 场景下会出三类问题。第一类是版本漂移。客户机上可能已经装了另一个小版本的 Runtime加载时 .NET 的程序集探测顺序会优先命中你安装目录里的本地副本而原生组件的加载走的是系统里注册的那份托管层和原生层版本对不上表现就是 TypeInitializationException 或者 0x8007007E 这类看不出所以然的错误。这种问题排查起来相当玄学因为开发机上永远复现不了。第二类是注册与授权。运行时的部分组件需要在系统里注册 COM 信息、写入共享目录单纯复制 dll 不会完成这些动作。授权更是如此它绑定的是已安装的运行时环境不是你能随手复制的文件。第三类是权限与升级。往 Program Files 下写文件在启用 UAC 的机器上会被虚拟化重定向装完之后程序读到的和写进去的不是同一个位置。而 Runtime 后续打补丁时你带过去的那份副本又不会被更新机器上就同时存在两套版本。提示判断一个程序集该不该进安装包看它是不是随运行时一起发布。属于运行时的一律排除属于你自己业务或第三方通用库的正常打包。2.3 目标机基线检测三条命令把环境摸清与其装完再猜不如装前先测。我习惯在部署脚本里先跑一段检测把框架、运行时产品键、运行时服务三项摸清楚输出成一行行可读结果出问题时直接把这段输出发给对方比来回问「你装了吗」高效得多。# 目标机基线检测框架 / 运行时产品键 / 运行时服务 $ErrorActionPreference SilentlyContinue # 1) .NET Framework 3.5Install1 表示已安装SP1 表示已打 SP1 $ndp HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5 $p Get-ItemProperty $ndp NET35 Install{0} SP{1} Version{2} -f $p.Install, $p.SP, $p.Version # 2) 运行时产品键桌面端多为 32 位优先看 WOW6432Node $roots HKLM:\SOFTWARE\WOW6432Node, HKLM:\SOFTWARE foreach ($r in $roots) { Get-ChildItem $r | Where-Object { $_.PSChildName -like ArcGIS* } | ForEach-Object { Runtime key: $($_.Name) } } # 3) 运行时相关服务是否在跑服务缺失通常意味着装得不完整 Get-Service | Where-Object { $_.Name -like ArcGIS* } | Select-Object Name, Status这段脚本的参数含义值得说清楚。NDP\v3.5是框架在注册表中的固定位置Install为 1 才算真的装上SP为 1 才代表 SP1两个值缺一不可只看到 Version 有值并不保险。第二段同时扫WOW6432Node和原生分支是因为 AE 桌面应用绝大多数编译为 32 位产品键落在重定向视图里只查 64 位分支会误判成「没装」。第三段用服务做交叉验证产品键存在但服务缺失说明安装过程被中断过这种情况直接重装比修复快。检测通过再谈打包。如果目标机是全新的还要额外确认系统是否已经内置了框架的按需启用能力某些系统版本上 .NET 3.5 属于可选功能而不是独立安装包这两种情况的处理方式完全不同后面避坑章节会专门说。3. 分离式打包用 VS 2008 安装项目产出干净的 Setup.msi分离式打包的意思是Runtime 和框架由你或客户单独准备安装包只负责应用本体外加一层环境检测。这条路线的优点是产物小、构建快、排错简单缺点是客户要多点几次安装。对于内部系统或者有统一软件分发平台的场景我更倾向于这一种因为环境由平台保证应用包只需要管好自己的事。3.1 建安装项目与主输出从解决方案到应用程序文件夹在包含 AE 应用的解决方案里新增一个安装和部署项目模板选安装项目命名成 Setup。这里有个细节安装项目和应用程序放在同一个解决方案里是为了让安装项目能直接引用主输出的项目引用构建时自动带上最新的编译结果不用手工拷 dll。操作顺序是这样打开安装项目的文件系统编辑器在「应用程序文件夹」上右键选择添加项目输出项目选你的 AE 应用输出选主输出。做完这一步中间的窗口里会一次性出现主输出、依赖程序集、以及一大批运行时程序集。这些运行时程序集就是接下来要清理的对象。我一般会在这一步先做一次减法把明显属于运行时的条目挑出来记下名字再去源项目里核对这些引用是从哪来的。有些是业务代码直接用了运行时的类型删不掉得保留引用但设成本地复制关闭有些是间接依赖压根没用到直接断开引用最干净。3.2 排除运行时程序集引用Copy Local 与安装项目引用两处都要改排除要改两个地方只改一处等于没改。第一处是源应用项目里把运行时程序集的「复制到本地」设为 False让输出目录不再产生这些文件第二处是安装项目的「检测到的依赖」节点右键把运行时程序集排除避免它被自动扫描进来。第二处经常被忽略。安装项目会自动分析主输出的引用链即使你把本地复制关了它仍可能从 GAC 或开发机的运行时目录里把程序集拉进安装包。所以构建前必须看一眼检测到的依赖列表。打包前先扫一遍输出目录确认没有漏网的运行时程序集被带出来# 打包前自检列出输出目录中属于运行时的程序集 $runtimePrefix esri # 运行时程序集的前缀按实际命名替换 $bin .\bin\Release Get-ChildItem $bin -Filter *.dll | Where-Object { $_.Name -match ^$runtimePrefix } | Select-Object Name, {nKB; e{[math]::Round($_.Length/1KB,1)}} | Sort-Object Name # 同时看看配置文件里有没有绑定重定向残留 Get-ChildItem $bin -Filter *.config | ForEach-Object { Config: $($_.FullName) }这段脚本的作用是给你一份清单出现条目就说明减法没做干净。匹配用的是前缀而不是完整文件名因为运行时程序集数量多、命名有规律逐个列举反而容易漏。配置文件那一段是顺手检查早期项目如果做过程序集绑定重定向升级运行时之后这些重定向会变成新的故障源打包前清掉更稳妥。至于前缀怎么定展开开发机上的运行时安装目录看一眼命名习惯就知道了别照抄别人的规则。3.3 快捷方式落到桌面与开始菜单安装包里没有快捷方式用户装完会以为没装上。做法是在应用程序文件夹里选中主输出右键创建快捷方式重命名成产品名然后把它拖到左侧的「用户桌面」和「用户的程序菜单」两个目录。想同时给所有用户用就放到「所有用户桌面」但那需要在安装时提权安装项目里对应设置一下安装权限即可。快捷方式的属性里有个容易踩的点目标默认是相对路径指向应用程序文件夹里的 exe这是对的不要改成绝对路径。图标可以在属性里单独指定用 exe 自带的图标或者额外放一个 ico 文件进应用程序文件夹再引用。3.4 生成产物与首轮安装验证生成安装项目输出目录里会出现 msi 和 setup.exe 两个文件。msi 是真正的安装数据库setup.exe 是个引导程序负责检查框架之类的先决条件再调 msi。如果只分发 msi在缺框架的机器上会直接失败所以两个一起发。验证不要在自己开发机上做开发机什么都有测不出问题。找一台干净的虚拟机装完系统后先手动装框架和 Runtime再跑你的 Setup确认三件事安装目录里的文件对不对、快捷方式有没有出现在该出现的位置、卸载之后目录是否清干净。这三件事都过了分离式打包这条路才算走通。REM 带日志安装出问题时看日志比看弹窗有用 msiexec /i Setup.msi /l*v %TEMP%\ae_setup.log /qb REM 卸载同样留日志验证残留 msiexec /x Setup.msi /l*v %TEMP%\ae_uninstall.log /qb/l*v是详细日志/qb是带进度条的基本界面比完全静默的/qn更适合首轮验证能看到卡在哪一步。日志默认在临时目录装完把文件留下来对比两次安装的差异很多时候「上次好好的这次不行」的答案就在日志的 FileCopy 段落里。4. 一体化打包InstallShield 把框架和 Runtime 塞进同一个安装包一体化打包适合面向外部客户的场景对方不一定有软件分发平台也不一定愿意按你的说明书一步步装环境。目标是把框架、Runtime、应用本体、快捷方式全部装进一个安装包用户点一次就完事。代价是包体变大、构建复杂、排错路径变长所以这一节我会把每一步的意图讲清楚别只照着点。4.1 Project Assistant 与 Installation Designer 该用哪个InstallShield 新建工程后会进 Project Assistant这是一个按「上一步下一步」组织常用操作的向导填应用信息、选安装架构、加文件、配快捷方式几步就能出一个能跑的安装包。适合做验证性打包或者内部工具。但向导的能力有上限。比如要在一个安装包里按顺序执行多个安装源、要控制临时目录的生命周期、要在特定时机执行自定义动作向导就做不到了得切到 Installation Designer。我一般的用法是先用 Project Assistant 把工程骨架和信息填完拿到一个能跑的最小包再切到 Installation Designer 补文件结构和安装顺序。这样既省了建工程的时间又不会被向导限制住。4.2 Files and Folders源机目录拖到目标机temp 目录挂安装源切到 Installation Designer左侧找到 Files and Folders 双击界面分成两栏左边是源计算机的目录右边是目标计算机的目录。操作分两步。第一步把应用输出目录整个从左侧拖到右侧的 Application Target Folder 下这是用户机器上的安装根目录。第二步在 Application Target Folder 上右键新建一个 temp 目录把运行时安装源整个目录拖进这个 temp 里。这里拖进去的是安装源不是安装结果包含运行时的安装程序和授权相关内容。为什么要单独开 temp 目录而不是直接堆在根目录下因为安装源是中间产物装完就该清掉和应用文件混在一起既难看又容易在卸载时被误删或误留。用独立目录后面在脚本里按路径清理边界清晰。注意temp 目录下放的安装源在安装过程中会被调用清理动作必须放在所有安装步骤之后提前清理会导致后续步骤找不到安装程序。4.3 先决条件与安装顺序为什么 temp 目录要最后清理顺序错了整个包就白做。正确顺序是框架 → 运行时 → 应用本体 → 快捷方式与注册 → 清理 temp。框架放最前面因为运行时的安装程序本身可能依赖它运行时在应用之前因为应用启动时要加载运行时组件清理放最后理由上一节说过。如果不想自己写安装序列可以用 InstallShield 的先决条件Prerequisites功能把框架和运行时的安装包挂成先决条件由安装引擎在正式安装前按顺序调用。这种方式的好处是支持「已安装则跳过」的判断客户机上已经有框架时不会重复装一遍。缺点是可定制性差一些复杂的判断条件还是得靠自定义动作。我倾向混合框架和运行时用先决条件挂上去让引擎处理检测和跳过应用本体走标准的文件安装temp 清理写成一个在安装完成后执行的自定义动作。这样每一段都用最合适的机制不用为了统一而硬凑。如果你们的部署环境允许脚本用批处理串安装序列更直观也方便在每一步之间插入检测echo off setlocal set SRC%~dp0 REM 1) 框架/q 静默/norestart 抑制自动重启装完由安装包统一提示 start /wait %SRC%payload\dotnetfx35sp1.exe /q /norestart if %ERRORLEVEL% NEQ 0 echo [WARN] 框架返回码 %ERRORLEVEL% REM 2) 运行时msi 用 /qn 静默/norestart 同样抑制重启 start /wait msiexec /i %SRC%payload\runtime\setup.msi /qn /norestart if %ERRORLEVEL% NEQ 0 echo [WARN] 运行时返回码 %ERRORLEVEL% REM 3) 应用本体最后装保证启动时环境已就绪 start /wait msiexec /i %SRC%Setup.msi /qn /norestart if %ERRORLEVEL% NEQ 0 echo [WARN] 应用返回码 %ERRORLEVEL% echo 安装序列执行完毕 endlocalstart /wait是关键没有它三条命令会并发跑框架还没装完就去调运行时必然失败。每个步骤后面接返回码判断非零值不一定代表致命错误比如框架已存在时某些安装包会返回一个非零的「已安装」码所以这里只告警不中断让人工能从头到尾看完再决定重试哪一步。%~dp0取脚本所在目录保证从任意路径调用都能找到 payload 子目录。4.4 快捷方式、发布产物与自定义动作的边界快捷方式在 Installation Designer 的 Shortcuts 节点里配。回到 Project Assistant 那一侧也可以建新建时选中主 exe系统会给一个默认名字改成产品名然后在 Designer 里进一步设置目标位置桌面、开始菜单、或者两者都要。开始菜单里我一般再建一个子文件夹把主程序和帮助文档放进去卸载项交给安装引擎自动生成。自定义动作能做的事很多但我给自己划了条线只做文件和环境层面的操作不做业务逻辑。见过有人在安装包里调数据库初始化脚本结果客户机上数据库客户端版本不对装到一半失败回滚也不干净。安装包该保证的是「文件到位、环境就绪、能启动」首次运行的数据初始化交给应用自己判断失败了还能重试。生成产物之前把 Release 配置和 Debug 配置各构建一次对比。有些安装工程会引用调试符号或者调试版依赖Release 包里混进这些东西包体大不说还可能引入额外的运行时依赖。5. 打包避坑五个让安装现场返工的错误下面五条都是我在实际交付里踩过或者帮别人收拾过的每条按现象、原因、解决写。看的时候对照一下自己的安装包能提前避开一大半现场返工。5.1 启动即报程序集加载失败现象安装顺利双击图标后弹出一个 .NET 的异常框提示无法加载某个程序集或者直接 0x8007007E。开发机上完全正常。原因安装包里混进了运行时程序集与目标机已安装的运行时版本不一致。托管层加载了本地副本原生组件走系统注册那份两层版本对不上。解决回到 3.2 节做减法把运行时程序集从输出目录和安装项目依赖里都排除如果业务代码确实需要引用运行时类型保持引用但关闭本地复制。改完在干净虚拟机上重测别在开发机上验证。5.2 目标机缺 .NET 3.5安装包不吭声现象安装过程没报错装完启动直接失败或者安装程序闪一下就退了。原因只分发了 msi没带引导程序或者先决条件配置了但没有正确检测某些系统上框架属于系统可选功能安装包里的独立安装程序无法直接装上。解决msi 和 setup.exe 一起分发先决条件里同时配置检测和安装两条路径对框架属于可选功能的系统安装前先提示用户启用该功能或者用系统命令按需启用后再继续。5.3 快捷方式只出现在管理员桌面现象管理员账户装完能看到桌面图标普通用户登录后桌面空空但程序其实装上了。原因快捷方式放到了「所有用户桌面」而安装时的权限上下文与普通用户的桌面路径不是同一个或者安装时用了提升权限写入位置被重定向。解决明确目标用户范围。只给当前用户用就放用户桌面安装时不需要提权要给所有用户就放所有用户桌面并确保安装包以足够的权限运行。两种方式不要混用混用就会出现一部分人看得到一部分人看不到。5.4 装完能启动但授权初始化失败现象程序界面能出来一操作就提示授权相关的错误或者初始化组件时直接退出。原因运行时装了但没有有效授权或者授权是在安装应用之后才导入的应用启动时读不到。也有一种情况是安装顺序颠倒应用先于运行时安装首次启动时环境不完整缓存了失败状态。解决把运行时和授权的准备放在应用安装之前安装包里明确提示授权需要在运行时安装完成后导入应用侧做一次启动自检检测不到可用环境时给出可操作的提示而不是直接崩。顺序问题在 4.3 节已经强调过一体化打包尤其容易在这里翻车。5.5 升级安装把旧文件留在原地现象覆盖安装新版本后程序行为还是旧的或者启动报配置冲突。原因安装包的升级逻辑没处理好新旧版本的文件标识不同旧文件既没被替换也没被删除配置文件被当成用户数据保留了下来新版本读到旧格式。解决给安装包配置明确的升级路径主版本升级走删除旧版本再装新版本配置文件在安装时判断版本号旧格式的做一次迁移或者备份后替换。卸载验证也要做装完再卸看目录里还剩什么剩下的就是下次升级的隐患。6. 干净机验证与静默部署把安装包跑成一次通过安装包做完最后一道关是验证而验证环境必须是干净的。我习惯用一个装完系统就打了快照的虚拟机每次测试前回滚到快照这样能保证上一轮的残留不会影响下一轮。开发机上测出来的「通过」没有意义那里什么依赖都有。验证清单我固定按下面这张表走每一项都要有明确的观察结果不能凭感觉。验证项检查方法期望结果环境检测在干净机上直接运行安装包缺框架时给出可读提示不闪退文件落位安装后查看安装目录只有应用本体无运行时程序集快捷方式分别用管理员与普通用户登录目标用户能看到对应图标首次启动双击启动观察加载过程无程序集或授权相关异常静默安装命令行带日志安装返回码为 0日志无 error 级记录卸载残留卸载后检查安装目录与注册表目录清空无遗留产品键静默安装这条要单独跑因为它和界面安装走的路径不完全一样有些自定义动作在界面模式下有时序保护静默模式下会暴露出来。我的做法是带详细日志静默装一遍然后直接在日志里搜错误级别REM 静默安装并留详细日志装完直接在日志里搜错误 msiexec /i Setup.msi /qn /norestart /l*v %TEMP%\ae_silent.log REM 日志里常见的三类问题文件复制失败、自定义动作超时、返回值 1603 findstr /i error 1603 return value 3 %TEMP%\ae_silent.logfindstr那一行是经验之谈1603 是安装过程中最常见的通用失败码本身不说明原因但它出现的位置附近的上下文往往就是根因return value 3是自定义动作失败的典型标记。日志里搜不到这两类基本可以认为静默路径是通的。再往上一层是批量部署。如果客户环境有软件分发平台可以把安装包和依赖串成一个命令序列由平台推送装完回传返回码。这时候回报信息的设计比安装本身更重要返回码要能区分「环境不满足」和「安装失败」前者应该重试或者转人工后者要看日志。我给这类脚本定过一个规矩任何非零返回都必须附带一行人类可读的原因否则运维只看到一个数字什么问题都定位不了。还有一个容易忽略的点是版本回退。新版本装上去了客户说要退回旧版本这时候安装包能不能干净卸载就成了关键。我的习惯是每次发版前在快照机上先装旧版、再装新版、再卸新版、再装回旧版走完这一圈文件、快捷方式、注册项都对得上才敢发出去。说个自己的教训。早年做一个 AE 桌面系统交付图省事把整个输出目录连同运行时程序集打了个包在自己机器上装了七八遍都没事到客户现场第一台就起不来远程排查了两个多小时才发现是本地程序集抢了系统运行时的加载。从那以后我每次出安装包之前都强制走一遍干净机快照验证输出目录的运行时程序集扫描脚本也固定在构建流程里宁可多花二十分钟也不在客户面前返工。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站