打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
使用WiX Toolset创建.NET程序发布Bootstrapper(安装策略管理)(二)——自定义安装
userphoto

2023.04.19 加拿大

关注

自定义产品卸载方式

        继续从上一次的基础上前进,现在我们已经知道了最简单的bootstrapper打包方法,现在我们对其中的每个节点深入自定义,争取可以达到我们需要的效果。先把最后全部的XML贴出来。

  1. <?xml version='1.0' encoding='UTF-8'?>
  2. <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'
  3. xmlns:util='http://schemas.microsoft.com/wix/UtilExtension' >
  4. <Bundle Name='CamCard' Version='4.0.0.0' Manufacturer='IntSig Information Co., Ltd.' AboutUrl='http://www.intsig.net' IconSourceFile='icon_256.ico'
  5. UpgradeCode='1EB9EC76-9E5F-4471-B522-314A62518A80' DisableRemove='no' DisableModify='yes' DisableRepair='yes'>
  6. <BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.RtfLicense'>
  7. <bal:WixStandardBootstrapperApplication LicenseFile='license.rtf' ThemeFile='MyTheme.xml' LocalizationFile='MyLocalize.wxl' LogoFile='logo.png' />
  8. </BootstrapperApplicationRef>
  9. <Chain>
  10. <ExePackage Id='Netfx4Full'
  11. Cache='no'
  12. Compressed='no'
  13. PerMachine='yes'
  14. Permanent='yes'
  15. Vital='yes' InstallCommand=' /q /norestart'
  16. SourceFile='dotNetFx40_Full_x86_x64.exe'
  17. DownloadUrl='http://go.microsoft.com/fwlink/?LinkId=164193'
  18. DetectCondition='DotNetFramework40FullInstallRegValue=1' />
  19. <MsiPackage Compressed='no' SourceFile='SSCERuntime-CHS.msi' Vital='yes' DisplayInternalUI='no' Permanent='yes' ForcePerMachine='yes'
  20. DownloadUrl='http://go.microsoft.com/fwlink/?LinkID=166085'
  21. InstallCondition='VersionNT = v5.1' />
  22. <MsiPackage Compressed='no' SourceFile='IntSig.CamCard.Installer.msi' Vital='yes' DisplayInternalUI='no' Permanent='no' ForcePerMachine='yes'>
  23. <MsiProperty Name='TARGETDIR' Value='[InstallFolder]'/>
  24. </MsiPackage>
  25. </Chain>
  26. <util:RegistrySearch Id='FindDotNet40FullInstallRegValue' Root='HKLM'
  27. Key='SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' Value='Install'
  28. Variable='DotNetFramework40FullInstallRegValue' />
  29. </Bundle>
  30. </Wix>

        Bundle节点前面几个属性我们都已经知道了,IconSourceFile就是打包后exe的图标设置,DisableRemove、DisableModify这两个属性比较有讲究,他们分别设置了在“添加/删除程序”列表中,选中安装包后鼠标右击,是否会出现“卸载”和“修改”这两个选项。如果这两个选项都同时为yes,那这个产品安装后根本就不会出现在“添加/删除程序”列表中,只能通过再次双击bootstrapper安装exe进行卸载。

自定义产品安装界面

  1. <BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.RtfLicense'>
  2. <bal:WixStandardBootstrapperApplication LicenseFile='license.rtf' ThemeFile='MyTheme.xml' LocalizationFile='MyLocalize.wxl' LogoFile='logo.png' />
  3. </BootstrapperApplicationRef>

        BootstrapperApplicationRef这个节点上一篇已经讲解过,但是我们使用的是最原始的默认界面,事实上可以通过在该节点中加入WixStandardBootstrapperApplication来自定义安装界面。LicenseFile就是要显示在用户安装协议中的RTF文件名称。ThemeFile是自定义主题xml文件,该文件详细定义了安装界面中的每个按钮和控件的位置。LocalizationFile是本地化配置文件,这个版本的Burn框架还不支持运行时自动根据安装语言环境自动切换。如果你需要采用本地化安装策略,比较靠谱的方法就是在bootstrapper之前再执行另一个exe,用来判断语言环境并自动执行不同的bootstrapper进行安装。LogoFile是安装界面左上角那个图标,注意XP环境下好像是不能使用ICON文件的。

        ThemeFile='MyTheme.xml' 这个文件可以在WiX源代码中找到,我贴在这篇博客里面,方便大家直接复制粘贴,这文件和后面的本地化文件的确不好找。

  1. <?xml version='1.0' encoding='utf-8'?>
  2. <Theme xmlns='http://wixtoolset.org/schemas/thmutil/2010'>
  3. <Window Width='485' Height='300' HexStyle='100a0000' FontId='0'>#(loc.Caption)</Window>
  4. <Font Id='0' Height='-12' Weight='500' Foreground='000000' Background='FFFFFF'>Segoe UI</Font>
  5. <Font Id='1' Height='-24' Weight='500' Foreground='000000'>Segoe UI</Font>
  6. <Font Id='2' Height='-22' Weight='500' Foreground='666666'>Segoe UI</Font>
  7. <Font Id='3' Height='-12' Weight='500' Foreground='000000' Background='FFFFFF'>Segoe UI</Font>
  8. <Font Id='4' Height='-12' Weight='500' Foreground='ff0000' Background='FFFFFF' Underline='yes'>Segoe UI</Font>
  9. <Image X='11' Y='11' Width='64' Height='64' ImageFile='logo.png' Visible='yes'/>
  10. <Text X='80' Y='11' Width='-11' Height='64' FontId='1' Visible='yes' DisablePrefix='yes'>#(loc.Title)</Text>
  11. <Page Name='Help'>
  12. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.HelpHeader)</Text>
  13. <Text X='11' Y='112' Width='-11' Height='-35' FontId='3' DisablePrefix='yes'>#(loc.HelpText)</Text>
  14. <Button Name='HelpCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.HelpCloseButton)</Button>
  15. </Page>
  16. <Page Name='Install'>
  17. <Richedit Name='EulaRichedit' X='11' Y='80' Width='-11' Height='-70' TabStop='yes' FontId='0' HexStyle='0x800000' />
  18. <Checkbox Name='EulaAcceptCheckbox' X='-11' Y='-41' Width='260' Height='17' TabStop='yes' FontId='3' HideWhenDisabled='yes'>#(loc.InstallAcceptCheckbox)</Checkbox>
  19. <Button Name='OptionsButton' X='-171' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0' HideWhenDisabled='yes'>#(loc.InstallOptionsButton)</Button>
  20. <Button Name='InstallButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.InstallInstallButton)</Button>
  21. <Button Name='WelcomeCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.InstallCloseButton)</Button>
  22. </Page>
  23. <Page Name='Options'>
  24. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.OptionsHeader)</Text>
  25. <Text X='11' Y='121' Width='-11' Height='17' FontId='3' DisablePrefix='yes'>#(loc.OptionsLocationLabel)</Text>
  26. <Editbox Name='FolderEditbox' X='11' Y='143' Width='-91' Height='21' TabStop='yes' FontId='3' FileSystemAutoComplete='yes' />
  27. <Button Name='BrowseButton' X='-11' Y='142' Width='75' Height='23' TabStop='yes' FontId='3'>#(loc.OptionsBrowseButton)</Button>
  28. <Button Name='OptionsOkButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.OptionsOkButton)</Button>
  29. <Button Name='OptionsCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.OptionsCancelButton)</Button>
  30. </Page>
  31. <Page Name='Progress'>
  32. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.ProgressHeader)</Text>
  33. <Text X='11' Y='121' Width='70' Height='17' FontId='3' DisablePrefix='yes'>#(loc.ProgressLabel)</Text>
  34. <Text Name='OverallProgressPackageText' X='85' Y='121' Width='-11' Height='17' FontId='3' DisablePrefix='yes'>#(loc.OverallProgressPackageText)</Text>
  35. <Progressbar Name='OverallCalculatedProgressbar' X='11' Y='143' Width='-11' Height='15' />
  36. <Button Name='ProgressCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.ProgressCancelButton)</Button>
  37. </Page>
  38. <Page Name='Modify'>
  39. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.ModifyHeader)</Text>
  40. <Button Name='RepairButton' X='-171' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0' HideWhenDisabled='yes'>#(loc.ModifyRepairButton)</Button>
  41. <Button Name='UninstallButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.ModifyUninstallButton)</Button>
  42. <Button Name='ModifyCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.ModifyCloseButton)</Button>
  43. </Page>
  44. <Page Name='Success'>
  45. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.SuccessHeader)</Text>
  46. <Button Name='LaunchButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0' HideWhenDisabled='yes'>#(loc.SuccessLaunchButton)</Button>
  47. <Text Name='SuccessRestartText' X='-11' Y='-51' Width='400' Height='34' FontId='3' HideWhenDisabled='yes' DisablePrefix='yes'>#(loc.SuccessRestartText)</Text>
  48. <Button Name='SuccessRestartButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0' HideWhenDisabled='yes'>#(loc.SuccessRestartButton)</Button>
  49. <Button Name='SuccessCancelButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.SuccessCloseButton)</Button>
  50. </Page>
  51. <Page Name='Failure'>
  52. <Text X='11' Y='80' Width='-11' Height='30' FontId='2' DisablePrefix='yes'>#(loc.FailureHeader)</Text>
  53. <Hypertext Name='FailureLogFileLink' X='11' Y='121' Width='-11' Height='42' FontId='3' TabStop='yes' HideWhenDisabled='yes'>#(loc.FailureHyperlinkLogText)</Hypertext>
  54. <Hypertext Name='FailureMessageText' X='22' Y='163' Width='-11' Height='51' FontId='3' TabStop='yes' HideWhenDisabled='yes' />
  55. <Text Name='FailureRestartText' X='-11' Y='-51' Width='400' Height='34' FontId='3' HideWhenDisabled='yes' DisablePrefix='yes'>#(loc.FailureRestartText)</Text>
  56. <Button Name='FailureRestartButton' X='-91' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0' HideWhenDisabled='yes'>#(loc.FailureRestartButton)</Button>
  57. <Button Name='FailureCloseButton' X='-11' Y='-11' Width='75' Height='23' TabStop='yes' FontId='0'>#(loc.FailureCloseButton)</Button>
  58. </Page>
  59. </Theme>

        可以看到这个XML文件可以通过自定义的方法来进行界面修改,其中#(loc.Caption)之类的是WiX使用变量的语法,这些变量都是已经内嵌在框架中了,我们还可以通过在bundle.wxs文件中自行增加变量的方法来达到增加变量的目的。MyLocalize.wxl文件如下:
  1. <?xml version='1.0' encoding='utf-8'?>
  2. <WixLocalization xmlns='http://schemas.microsoft.com/wix/2006/localization'>
  3. <String Id='Caption'>Setup [WixBundleName]</String>
  4. <String Id='Title'>[WixBundleName]</String>
  5. <String Id='ConfirmCancelMessage'>Are you sure you want to cancel?</String>
  6. <String Id='HelpHeader'>Setup Help</String>
  7. <String Id='HelpText'>
  8. /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or
  9. creates a complete local copy of the bundle in directory. Install is the default.
  10. /passive | /quiet - displays minimal UI with no prompts or displays no UI and
  11. no prompts. By default UI and all prompts are displayed.
  12. /norestart - suppress any attempts to restart. By default UI will prompt before restart.
  13. /log log.txt - logs to a specific file. By default a log file is created in %TEMP%.
  14. </String>
  15. <String Id='HelpCloseButton'>&Close</String>
  16. <String Id='InstallAcceptCheckbox'>I &agree to the license terms and conditions</String>
  17. <String Id='InstallOptionsButton'>&Options</String>
  18. <String Id='InstallInstallButton'>&Install</String>
  19. <String Id='InstallCloseButton'>&Close</String>
  20. <String Id='OptionsHeader'>Setup Options</String>
  21. <String Id='OptionsLocationLabel'>Install location (disk root path is not recommanded):</String>
  22. <String Id='OptionsBrowseButton'>&Browse</String>
  23. <String Id='OptionsOkButton'>&OK</String>
  24. <String Id='OptionsCancelButton'>&Cancel</String>
  25. <String Id='ProgressHeader'>Setup Progress</String>
  26. <String Id='ProgressLabel'>Processing:</String>
  27. <String Id='OverallProgressPackageText'>Initializing...</String>
  28. <String Id='ProgressCancelButton'>&Cancel</String>
  29. <String Id='ModifyHeader'>Modify Setup</String>
  30. <String Id='ModifyRepairButton'>&Repair</String>
  31. <String Id='ModifyUninstallButton'>&Uninstall</String>
  32. <String Id='ModifyCloseButton'>&Close</String>
  33. <String Id='SuccessHeader'>Setup Successful</String>
  34. <String Id='SuccessLaunchButton'>&Launch</String>
  35. <String Id='SuccessRestartText'>You must restart your computer before you can use the software.</String>
  36. <String Id='SuccessRestartButton'>&Restart</String>
  37. <String Id='SuccessCloseButton'>&Close</String>
  38. <String Id='FailureHeader'>Setup Failed</String>
  39. <String Id='FailureHyperlinkLogText'>One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href='#'>log file</a>.</String>
  40. <String Id='FailureRestartText'>You must restart your computer to complete the rollback of the software.</String>
  41. <String Id='FailureRestartButton'>&Restart</String>
  42. <String Id='FailureCloseButton'>&Close</String>
  43. </WixLocalization>

        我们只需要修改每个String节点中的内容,就可以达到本地化安装内容的目的。

自定义安装序列

        上面我们已经将安装界面全部自定义完毕,脸面上的事情总算是做完了,我们要解决的根本问题还在眼前,如何自定义调整安装顺序?答案就是通过Chain节点来完成。Chain节点定义了打包安装的顺序,默认是从上到下逐个完成,当然你也可以在子节点中通过After属性来调整安装顺序。

        首先是判断并安装.NET Framework 4.0环境。

  1. <ExePackage Id='Netfx4Full'
  2. Cache='no'
  3. Compressed='no'
  4. PerMachine='yes'
  5. Permanent='yes'
  6. Vital='yes' InstallCommand=' /q /norestart'
  7. SourceFile='dotNetFx40_Full_x86_x64.exe'
  8. DownloadUrl='http://go.microsoft.com/fwlink/?LinkId=164193'
  9. DetectCondition='DotNetFramework40FullInstallRegValue=1' />

        我们知道,从网上下载得到的.NET 4.0是一个exe安装包,因此我们要用ExePackage进行声明。

        Compressed表示是否要将该安装包打包在bootstrapper.exe文件中,我们回忆一下上一讲,上一讲我们的Setup1.msi文件因为没有带上该属性,就自动将安装包打包在bootstrapper中了,因此我们只得到了一个安装文件。如果Compressed='no',那就会在bootstrapper.exe边上出现另外一个单独的安装文件,这样可以减小我们发布的尺寸。

        PerMachine是表示是否要采用UAC权限进行安装,因为有些安装文件需要以管理员身份运行才能安装成功,而我们现在采取的是静默安装策略,因此需要在定义安装的时候就表明是否需要UAC权限。

        Permanent表示卸载时是否需要同时卸载该安装包。yes表示卸载产品时该安装包不需要同时卸载,将会永远留在客户的计算机里面。

        Vital表示是否该安装时必须的,如果该安装是必须的,那如果该安装没有成功,后续的所有安装都不会进行。

        InstallCommand是在安装时需要跟上的参数,我们知道.NET的静默安装需要带上“ /q /norestart”参数,这样我们用bootstrapper托管下就可以让.NET静默安装完毕了。

        SourceFile表明安装文件的名称是什么,它和DownloadUrl是任选的,也就是说如果安装时发现该文件包不存在的话,需要从什么URL进行自动下载并安装。

        DetectCondition是bootstrapper的精髓,它用一个表达式来判断一台计算机上该包是否已经安装过,DotNetFramework40FullInstallRegValue和文档最后的util:RegistrySearch节点ID是一致的,util:RegistrySearch代表查看注册表中的某个表项的值的变量,然后匹配该注册表项值满足条件,则说明已经安装,如果不满足则说明需要安装。

  1. <MsiPackage Compressed='no' SourceFile='SSCERuntime-CHS.msi' Vital='yes' DisplayInternalUI='no' Permanent='yes' ForcePerMachine='yes'
  2. DownloadUrl='http://go.microsoft.com/fwlink/?LinkID=166085'
  3. InstallCondition='VersionNT = v5.1' />
        MsiPackage节点上一讲已经使用过,如果要安装MSI文件必须用这个节点,它的属性和ExePackage大同小异,DisplayInternalUI表示是否要静默安装,ForcePerMachine代表需要UAC权限。InstallCondition代表需要安装的判定条件,请注意,这个和上面的DetectCondition是反的,上面是如果判定为false则安装,这个是判定为true则安装。(看到这儿实在觉得这是不是两个人写的框架啊,属性名称也不统一,连判定安装条件也要反一反,很是反人类啊!)InstallCondition='VersionNT = v5.1'表示如果是XP则进行安装,VersionNT是框架自带的变量,可以在WiX的网站中查询并得到其他的变量使用方法。
  1. <MsiPackage Compressed='no' SourceFile='IntSig.CamCard.Installer.msi' Vital='yes' DisplayInternalUI='no' Permanent='no' ForcePerMachine='yes'>
  2. <MsiProperty Name='TARGETDIR' Value='[InstallFolder]'/>
  3. </MsiPackage>

        最后一个MSI就是我们自己定义的MSI了,前面是把环境安装包给安装掉,最后就是安装我们自己的产品包。重点需要解释的是<MsiProperty Name='TARGETDIR' Value='[InstallFolder]'/>节点,这个其实就是相当于我们使用msiexec.exe系统命令进行MSI安装,如果我们要把MSI静默安装,又必须指定要装到某个目录下,需要使用msiexec.exe ***.msi TARGETDIR='C:\AA\' 这样的命令格式,其实上面的 MsiProperty属性就是把之前在bootstrapper启动界面选项中选中的安装目录路径通过变量传送了过来。

        完成上面的这些工作,再把所需要的安装包,配置文件都放到工程里面后,经过项目生成,我们就拿到很完美的bootstrapper了。现在的结果是,最极限情况下我们只需要发布一个bootstrapper,后续所有的软件依赖就能下载并安装,极大得减少了用户下载安装的负担。并且因为可以动态判断依赖包,所以用户的安装速度也得到极大的提升。最最重要的是,只需要在安装第一个包的时候进行一次UAC询问,后续就不再会有类似的恼人确认对话框了。至此,我们之前所有提出的VS打包项目的不足就全部解决了。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
使用WiX Toolset创建.NET程序发布Bootstrapper(安装策略管理)(二)
漂亮的日志模板 - 敏儿的日志 - 网易博客
window.open参数表
C#之条形码代码-程序开发-红黑联盟
根据文本内容创建图片并返回图片路径
外文书上的毛衣图片
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服