I have a WiX v4 managed BA that has many redistributables as prerequisites. The installation completes and everything is installed successfully, but frequently the 3 bootstrapper processes never terminate even after the UI is closed and Engine.Quit(0) is called. The redistributable installers exit successfully and there are no issues in their logs, so I assume the issue is coming from my BA.
In my bundle I have the redistributables included as follows:
<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" />
And my BA class is:
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);
}
}
}
From the logs it almost looks like the BA is hanging before it is able to execute the cleanup after Engine.Quit.
The bundle log ends with:
[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
Whereas the expected lines afterwords about
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
Never appear.
Often (not always) if I install the NET Desktop Runtime redistributable myself BEFORE running the BA, the process terminates as expected and the cleanup is executed.
This happens on a fresh, completely new install. There are no pending reboot or file moves I can see. The BA is not encountering any obvious exceptions or giving any event viewer information.
Killing the process myself after Engine.Quit(0) using something like Process.GetCurrentProcess().Kill(); works, but is skipping any potential cleanup, shutdown events, and just feels gross in general.
This was not a problem with the same logic in v3, so either something with the upgrade to v4 is incorrect or v4 itself had a behavior change (some new asynchronous stuff?) regarding this that I cannot track down.
I can provide any additional log or code information upon request.
Any help or even left-field suggestions would be appreciated :)