用 WiX 制作安装包:设置的 .NET Framework 前置会始终安装,即使目标电脑已经自带或装好

2023-10-23 08:35:25 浏览数 (1)

使用 WiX 的 Burn 引擎制作自定义托管引导程序的 exe 安装包时,你可能会遇到这种情况:明明目标电脑上已经装好了 .NET Framework,但无论如何就是会提示安装,始终不启动自定义的安装界面。

现象

即使是在开发机上(.NET Framework 已经装好),双击制作的 exe 安装包也依然会提示安装 .NET Framework:

如果强行安装,装完也依然不会启动自定义的引导程序。

小提示 这个错误其实非常有误导性! 看起来不断提示要安装 .NET Framework,会让人误以为是 .NET Framework 的安装判断条件写出了问题,然后朝着 Product.wxs 中的 Condition、Bundle.wxs 中的 InstallConditionDetectCondition 去调查。然而这是捆绑包中的判断,与 Product.wxs 无关;我们默认用的是 WixNetFxExtension.dll 中的判断,这很靠谱,也不会出问题,所以也与 InstallConditionDetectCondition 无关。 正确的调查方法是去看错误日志,看真实的错误原因是什么。

当停留在这个“安装 .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”,并不能给我们带来什么有价值的信息。

虽然错误码无法给我们带来有价值的信息,但那句提示至少可以让我们知道问题出在“无法加载托管宿主”这个范围。

这可能是两个范围:

  1. 我们自定义的 BootstrapperApplication 的第一行代码 Run
  2. 我们自定义的 BootstrapperApplication 的第一行代码 Run

这很好区分,在 Run 的第一句加上一个 “Debugger.Launch()”,看看再启动安装包的时候是否会弹出调试器选择框即可。

1 2 3 4

protected override void Run() { Debugger.Launch(); }

我们就这样试一下,可以发现不会弹出调试器选择框。所以以上的两个范围只能是范围 1。

小提示 实际上按目前的日志输出,已经足以确定是范围 1 了,不过这需要一些先验知识,即托管引导程序能捕获 Run 方法中的所有异常。也就是说无论你的代码怎么写,托管引导程序都能把你引导起来,而不会出现此日志中输出的那样“无法加载托管宿主”。前面这个调查模拟没有此先验知识的情况,你可以从中学习到更多的 Burn MBA(Managed Bootstrapper Application)调试技巧。

有哪些东西会在 Run 之前?

  1. BootstrapperCore.config 文件的配置
  2. 程序集元数据

对于 1,如果你能看出来 BootstrapperCore.config 配置出现了哪些问题更好,看不出来的话可以把现成的例子拿出来对比(例如我在入门教程里写的 DEMO 程序,记得要改项目名)。确保里面的 assemblyNamesupportedRuntime 属性赋值正确(可参见我入门教程中写的配置和可用值说明)。

对于 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]) 。

0 人点赞