使用 WiX 的 Burn 引擎制作自定义托管引导程序的 exe 安装包时,你可能会遇到这种情况:明明目标电脑上已经装好了 .NET Framework,但无论如何就是会提示安装,始终不启动自定义的安装界面。
现象
即使是在开发机上(.NET Framework 已经装好),双击制作的 exe 安装包也依然会提示安装 .NET Framework:
如果强行安装,装完也依然不会启动自定义的引导程序。
小提示 这个错误其实非常有误导性! 看起来不断提示要安装 .NET Framework,会让人误以为是 .NET Framework 的安装判断条件写出了问题,然后朝着 Product.wxs 中的
Condition
、Bundle.wxs 中的InstallCondition
、DetectCondition
去调查。然而这是捆绑包中的判断,与 Product.wxs 无关;我们默认用的是 WixNetFxExtension.dll 中的判断,这很靠谱,也不会出问题,所以也与InstallCondition
和DetectCondition
无关。 正确的调查方法是去看错误日志,看真实的错误原因是什么。
当停留在这个“安装 .NET Framework”的界面时,查看 Burn 引擎的输出日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 14A4:9F04i001: Burn v3.11.2.4516, Windows v10.0 (Build 22000: Service Pack 0), path: C:UserslvyiAppDataLocalTemp{4310ECA0-6A28-4BE6-922D-570EAA81C0CD}.crWalterlv.Demo.MainApp.exe 14A4:9F04i009: Command Line: '-burn.clean.room=D:WIPDesktopWalterlv.WixInstallerDemoWalterlv.Installer.ExebinDebugWalterlv.Demo.MainApp.exe -burn.filehandle.attached=464 -burn.filehandle.self=460 -l debug.log' 14A4:9F04i000: Setting string variable 'WixBundleOriginalSource' to value 'D:WIPDesktopWalterlv.WixInstallerDemoWalterlv.Installer.ExebinDebugWalterlv.Demo.MainApp.exe' 14A4:9F04i000: Setting string variable 'WixBundleOriginalSourceFolder' to value 'D:WIPDesktopWalterlv.WixInstallerDemoWalterlv.Installer.ExebinDebug' 14A4:9F04i000: Setting string variable 'WixBundleLog' to value 'D:WIPDesktopWalterlv.WixInstallerDemoWalterlv.Installer.ExebinDebugdebug.log' 14A4:9F04i000: Setting string variable 'WixBundleName' to value 'Walterlv.Demo.MainApp' 14A4:9F04i000: Setting string variable 'WixBundleManufacturer' to value 'walterlv' 14A4:9F04i000: Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x80070490. 14A4:A444i000: Setting numeric variable 'WixStdBALanguageId' to value 2052 14A4:A444i000: Setting version variable 'WixBundleFileVersion' to value '1.0.0.0' 14A4:9F04i100: Detect begin, 2 packages 14A4:9F04i000: Setting string variable 'NETFRAMEWORK45' to value '528449' 14A4:9F04i052: Condition 'NETFRAMEWORK45 >= 394802' evaluates to true. 14A4:9F04i101: Detected package: NetFx462Web, state: Present, cached: None 14A4:9F04i101: Detected package: Walterlv.Demo.MainApp.msi, state: Absent, cached: None 14A4:9F04i199: Detect complete, result: 0x0 |
---|
调查
可以注意到唯一的一行错误:
1 | Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x80070490. |
---|
加载安装前置的引导程序,因为托管宿主无法被加载,错误代码 0x80070490
。所以导致弹出 .NET Framework 安装界面的原因是引导程序无法加载我们的自定义界面,误认为前置没有装好,所以弹出了前置安装界面。
查询一下错误码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ❯ err 80070490 # No results found for hex 0x4c5c75a / decimal 80070490 # for hex 0x80070490 / decimal -2147023728 PEER_E_NOT_FOUND p2p.h E_PROP_ID_UNSUPPORTED vfwmsgs.h # The specified property ID is not supported for the # specified property set.%0 WER_E_NOT_FOUND werapi.h DRM_E_NOT_FOUND windowsplayready.h # as an HRESULT: Severity: FAILURE (1), FACILITY_WIN32 (0x7), Code 0x490 # for hex 0x490 / decimal 1168 ERROR_NOT_FOUND winerror.h # Element not found. # 5 matches found for "80070490" |
---|
“Not Found”,并不能给我们带来什么有价值的信息。
虽然错误码无法给我们带来有价值的信息,但那句提示至少可以让我们知道问题出在“无法加载托管宿主”这个范围。
这可能是两个范围:
- 我们自定义的
BootstrapperApplication
的第一行代码Run
之前 - 我们自定义的
BootstrapperApplication
的第一行代码Run
之后
这很好区分,在 Run
的第一句加上一个 “Debugger.Launch()”,看看再启动安装包的时候是否会弹出调试器选择框即可。
1 2 3 4 | protected override void Run() { Debugger.Launch(); } |
---|
我们就这样试一下,可以发现不会弹出调试器选择框。所以以上的两个范围只能是范围 1。
小提示 实际上按目前的日志输出,已经足以确定是范围 1 了,不过这需要一些先验知识,即托管引导程序能捕获
Run
方法中的所有异常。也就是说无论你的代码怎么写,托管引导程序都能把你引导起来,而不会出现此日志中输出的那样“无法加载托管宿主”。前面这个调查模拟没有此先验知识的情况,你可以从中学习到更多的 Burn MBA(Managed Bootstrapper Application)调试技巧。
有哪些东西会在 Run
之前?
- BootstrapperCore.config 文件的配置
- 程序集元数据
对于 1,如果你能看出来 BootstrapperCore.config 配置出现了哪些问题更好,看不出来的话可以把现成的例子拿出来对比(例如我在入门教程里写的 DEMO 程序,记得要改项目名)。确保里面的 assemblyName
和 supportedRuntime
属性赋值正确(可参见我入门教程中写的配置和可用值说明)。
对于 2,通过实验可以得知程序集元数据出现错误时的错误码不是这个(参见:0x80131508
错误)。
于是我们也能得出问题出在 BootstrapperCore.config 配置里。
解决
可按下面的配置作为参考,将你的配置改到正确(参见我的 WiX 入门教程):
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version="1.0" encoding="utf-8" ?><br><configuration><br> <configSections><br> <sectionGroup name="wix.bootstrapper" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore"><br> <section name="host" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore" /><br> </sectionGroup><br> </configSections><br> <wix.bootstrapper><br> <host assemblyName="Walterlv.InstallerUI"><br> <supportedFramework version="v4Full" /><br> </host><br> </wix.bootstrapper><br></configuration> |
---|
另外多说一句,官方文档对 BootstrapperCore.config 的描述非常具有误导性:
- How To: Install the .NET Framework Using Burn
- 奇葩史的奇葩事 - [译]:WiX Toolset使用技巧——使用Burn引擎安装.NET Framework
官方文档示例的注释中要大家改 host/@assemblyName
,但实际上按官方文档的改法改好了就会出现本文所述的错误。
参考资料
- installation - Wix ExePackage always installs regardless of DetectCondition, InstallCondition, on install, or uninstall - Stack Overflow
本文会经常更新,请阅读原文: https://blog.walterlv.com/post/wix-burn-always-install-netfx-even-if-already-installed.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。