我有一个 WiX v4 托管 BA,它有许多可再发行组件作为先决条件。安装完成并且所有内容都已成功安装,但即使在关闭 UI 并调用 Engine.Quit(0) 后,3 个引导程序进程也常常不会终止。可再发行安装程序成功退出,并且其日志中没有任何问题,因此我认为问题来自我的 BA。
在我的捆绑包中,我包含如下可再发行组件:
<ExePackage
bal:PrereqPackage="yes"
Id="NetFramework48"
PerMachine="yes"
DetectCondition="NETFRAMEWORK45 >= 528040"
Vital="yes"
Permanent="yes"
Protocol="netfx4"
LogPathVariable="NetFxInstallLog"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\ndp48-x86-x64-allos-enu.exe"
Name="redist\ndp48-x86-x64-allos-enu.exe"
InstallArguments="/q /norestart /ChainingPackage "[WixBundleName]" /log "[NetFxInstallLog].html""
RepairArguments="/q /norestart /repair /ChainingPackage "[WixBundleName]" /log "[NetFxInstallLog].html""
UninstallArguments="/uninstall /q /norestart /ChainingPackage "[WixBundleName]" /log "[NetFxInstallLog].html"" />
<ExePackage
Id="DotNET6DesktopRuntimeX86"
PerMachine="yes"
DetectCondition="DotNET6DesktopRuntimex86Installed"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET6\windowsdesktop-runtime-6.0.11-win-x86.exe"
Name="redist\windowsdesktop-runtime-6.0.11-win-x86.exe"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart">
<ExitCode Behavior="success" Value="0" />
<!-- Error Code 0x666 or 1638 means a newer version was detected -->
<ExitCode Behavior="success" Value="1638" />
</ExePackage>
<ExePackage
Id="DotNET6DesktopRuntimeX64"
PerMachine="yes"
DetectCondition="DotNET6DesktopRuntimex64Installed"
Vital="yes" Permanent="yes" Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET6\windowsdesktop-runtime-6.0.11-win-x64.exe"
Name="redist\windowsdesktop-runtime-6.0.11-win-x64.exe"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart">
<ExitCode Behavior="success" Value="0" />
<!-- Error Code 0x666 or 1638 means a newer version was detected -->
<ExitCode Behavior="success" Value="1638" />
</ExePackage>
<ExePackage
Id="DotNET6AspCoreRuntimeX86"
PerMachine="yes"
DetectCondition="DotNET6ASPCoreRuntimex86Installed"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET6\aspnetcore-runtime-6.0.11-win-x86.exe"
Name="redist\aspnetcore-runtime-6.0.11-win-x86.exe"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart" />
<ExePackage
Id="DotNET8DesktopRuntimeX86"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart"
PerMachine="yes"
DetectCondition="DotNET8DesktopRuntimex86Installed"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET8\windowsdesktop-runtime-8.0.7-win-x86.exe"
Name="redist\windowsdesktop-runtime-8.0.7-win-x86.exe">
<ExitCode Behavior="success" Value="0"/>
<!-- Error Code 0x666 or 1638 means a newer version was detected -->
<ExitCode Behavior="success" Value="1638"/>
</ExePackage>
<ExePackage
Id="DotNET8DesktopRuntimeX64"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart"
PerMachine="yes"
DetectCondition="DotNET8DesktopRuntimex64Installed"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET8\windowsdesktop-runtime-8.0.7-win-x64.exe"
Name="redist\windowsdesktop-runtime-8.0.7-win-x64.exe">
<ExitCode Behavior="success" Value="0"/>
<!-- Error Code 0x666 or 1638 means a newer version was detected -->
<ExitCode Behavior="success" Value="1638"/>
</ExePackage>
<ExePackage
Id="DotNET8AspCoreRuntimeX86"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart"
PerMachine="yes"
DetectCondition="DotNET8ASPCoreRuntimex86Installed"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\DotNET8\aspnetcore-runtime-8.0.7-win-x86.exe"
Name="redist\aspnetcore-runtime-8.0.7-win-x86.exe"/>
<ExePackage
Id="VisualCPlusPlus14Runtime"
PerMachine="yes"
DetectCondition="VC14RuntimeInstalled > v0.0.0.0"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\Visual C++\VC++2015-2022_x86.exe"
Name="redist\VC++2015-2022_x86.exe"
InstallArguments="/install /quiet /norestart"
RepairArguments="/repair /quiet /norestart"
UninstallArguments="/uninstall /quiet /norestart" />
<ExePackage
Id="DirectX9C"
PerMachine="yes"
DetectCondition="DirectX9cInstalled"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\Directx90c\DXSETUP.exe"
Name="redist\Directx90c\DXSETUP.exe"
InstallArguments="/silent">
<PayloadGroupRef Id="DirectX9cPayloadGroup" />
</ExePackage>
<ExePackage
Id="MicrosoftAccess2010DatabaseEngine"
PerMachine="yes"
DetectCondition="MSAccess2010DbEngineInstalled"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\AccessDatabaseEngine.exe"
Name="redist\AccessDatabaseEngine.exe"
InstallArguments="/quiet /norestart" />
<MsiPackage
Id="Powershell"
InstallCondition="NOT PowershellInstalled"
Vital="yes"
Permanent="yes"
Compressed="no"
SourceFile="$(var.SolutionDir)Resources\Redistributables\Powershell\PowerShell-7.3.11-win-x86.msi"
Name="redist\PowerShell-7.3.11-win-x86.msi" />
我的学士学位课程是:
using System.Windows.Threading;
using WixToolset.Mba.Core;
namespace Bootstrapper
{
/// <summary>
/// The custom bootstrapper application.
/// </summary>
public class CustomBA : BootstrapperApplication, ICustomBA
{
#nullable enable
public CustomBA(IEngine engine, IBootstrapperCommand command) : base(engine)
{
Engine = engine;
Command = command;
}
/// <inheritdoc/>
public IEngine Engine { get; set; }
/// <inheritdoc/>
public IBootstrapperCommand Command { get; set; }
/// <summary>
/// The global dispatcher.
/// </summary>
static public Dispatcher? BootstrapperDispatcher { get; private set; }
/// <summary>
/// The entry point for the custom UI.
/// </summary>
protected override void Run()
{
this.Engine.Log(LogLevel.Standard, "Launching custom bootstrapper UX");
BootstrapperDispatcher = Dispatcher.CurrentDispatcher;
MainWindowViewModel viewModel = new MainWindowViewModel(this);
MainWindow view = new MainWindow();
view.DataContext = viewModel;
view.Closed += (sender, e) => BootstrapperDispatcher.InvokeShutdown();
if (!viewModel.silent)
{
view.Show();
}
Dispatcher.Run();
this.Engine.Quit(0);
}
}
}
从日志来看,BA 几乎在 Engine.Quit 之后执行清理之前就挂起了。
捆绑日志结尾为:
[098C:18D8][2024-07-31T13:40:42]i399: Apply complete, result: 0x0, restart: None, ba requested restart: No
[098C:0B88][2024-07-31T13:45:23]i000: Dispatch complete, quitting engine.
[098C:18D8][2024-07-31T13:45:23]i500: Shutting down, exit code: 0x0
而预期的台词后记是关于
Cleanup begin.
Cleanup not required due to running Apply.
Cleanup complete, result: 0x0
All the variable ending values blah blah
Exit code: 0x0, restarting: No
永远不要出现。
通常(并非总是)如果我在运行 BA 之前自行安装 NET 桌面运行时可再发行组件,进程会按预期终止并执行清理。
这种情况发生在全新安装时。 我可以看到没有待处理的重新启动或文件移动。 BA 没有遇到任何明显的异常或提供任何事件查看器信息。
在 Engine.Quit(0) 之后使用类似 Process.GetCurrentProcess().Kill(); 的方法自行终止进程;有效,但跳过了任何潜在的清理、关闭事件,总体感觉很恶心。
这不是 v3 中相同逻辑的问题,因此升级到 v4 的某些内容不正确,或者 v4 本身发生了行为更改(一些新的异步内容?),对此我无法追踪。
我可以根据要求提供任何其他日志或代码信息。
任何帮助,甚至左翼建议,将不胜感激:)
该问题是由于向 Engine.Apply 传递虚拟句柄而引起的。
private void OnPlanComplete(object sender, PlanCompleteEventArgs e)
{
Bootstrapper.Engine.Log(LogLevel.Standard, $"Plan complete with status: {e.Status}");
if (e.Status >= 0)
{
// Pass in a dummy handle since as of WiX v4, Apply no longer supports a handle of zero.
// This handle is used for parenting Burn child UIs to the BA window, not needed for us.
Bootstrapper.Engine.Apply(new System.Windows.Forms.Form().Handle);
}
}
如果没有真正的手柄,引擎(有时??????)在退出时似乎无法自行清理,即使安装顺利完成。
使用
new WindowInteropHelper(this).EnsureHandle();
从 View 获取实际句柄并将其传递给 Apply,如下所示:
private void OnPlanComplete(object sender, PlanCompleteEventArgs e)
{
Bootstrapper.Engine.Log(LogLevel.Standard, $"Plan complete with status: {e.Status}");
if (e.Status >= 0)
{
// Use real window handle from the view.
Bootstrapper.Engine.Log(LogLevel.Standard, $"Applying with window handle: {Bootstrapper.Handle}");
Bootstrapper.Engine.Apply(Bootstrapper.Handle);
}
}
为我解决了问题。