BCU supports quiet (silent) and unattended uninstallation modes that minimize or eliminate user interaction during the uninstall process. This is essential for automation, scripting, and batch operations where manual intervention is impractical.
Quiet uninstallers can hang without user notification. BCU monitors for stuck processes:
private bool TestUninstallerForStalls(IEnumerable<string> childProcesses){ // Create performance counters for CPU and I/O foreach (var childProcessName in childProcessNames) { var perfCounters = new[] { new PerformanceCounter("Process", "% Processor Time", childProcessName), new PerformanceCounter("Process", "IO Data Bytes/sec", childProcessName) }; _perfCounterBuffer.Add(childProcessName, new PerfCounterEntry(perfCounters)); } // Wait to gather data Thread.Sleep(1100); // Check if any process is active foreach (var perfCounterEntry in _perfCounterBuffer) { var cpuUsage = CalculateCpuUsage(perfCounterEntry); var ioUsage = CalculateIoUsage(perfCounterEntry); // Check if process seems to be doing anything // Use 1% for CPU and 10KB for I/O if (cpuUsage <= 1 && ioUsage <= 10240) { return true; // Process is stalled } } return false;}
Failed quiet uninstalls can retry with interactive mode:
if (error != null){ if (options.RetryFailedQuiet || (UninstallerEntry.UninstallerKind == UninstallerType.Nsis && !options.PreferQuiet)) { retry = true; throw new IOException( Localisation.UninstallError_UninstallerReturnedCode + exitVar); }}if (retry && _canRetry){ CurrentStatus = UninstallStatus.Waiting; _canRetry = false; // Only retry once}else{ Finished = true;}
Retry Behavior:
First attempt: Quiet mode
On failure: Retry with interactive mode
Only retries once per application
Exit Code Handling
BCU interprets uninstaller exit codes:
var exitVar = uninstaller.ExitCode;if (exitVar != 0){ if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec && exitVar == 1602) { // 1602 ERROR_INSTALL_USEREXIT - User cancelled _skipLevel = SkipCurrentLevel.Skip; } else if (UninstallerEntry.UninstallerKind == UninstallerType.Nsis && (exitVar == 1 || exitVar == 2)) { // 1 - Installation aborted by user // 2 - Installation aborted by script _skipLevel = SkipCurrentLevel.Skip; } else if (exitVar == -1073741510) { // 0xC000013A - Terminated by CTRL+C _skipLevel = SkipCurrentLevel.Terminate; } else { switch (exitVar) { case 2: throw new Exception("The system cannot find the file specified."); case 3: throw new Exception("The system cannot find the path specified."); case 5: throw new Exception("Access is denied."); case 9009: throw new Exception("Program is not recognized as an internal or external command."); default: if (options.RetryFailedQuiet) retry = true; throw new IOException( Localisation.UninstallError_UninstallerReturnedCode + exitVar); } }}
Process Chain Tracking
BCU monitors the entire process tree:
var watchedProcesses = new List<Process> { uninstaller };int[] previousWatchedProcessIds = { };while (true){ // Add all child processes foreach (var watchedProcess in watchedProcesses.ToList()) watchedProcesses.AddRange(watchedProcess.GetChildProcesses()); // For MSI, also track msiexec if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { foreach (var msiProcess in Process.GetProcessesByName("msiexec")) watchedProcesses.AddRange(msiProcess.GetChildProcesses()); } watchedProcesses = CleanupDeadProcesses(watchedProcesses).ToList(); // Check if we are done if (watchedProcesses.Count == 0) break; // Continue monitoring...}
For enhanced automation, BCU includes a daemon that can assist with interactive installers:
Experimental Feature: The Quiet Uninstall Daemon attempts to automatically answer common uninstaller dialogs. This feature is experimental and may not work with all uninstallers.
if (IsSilentPossible && UninstallToolsGlobalConfig.UseQuietUninstallDaemon && _canRetry){ // Don't try to automatize CLI programs or our own helpers if (!UninstallerEntry.QuietUninstallerIsCLI() && !UninstallerEntry.QuietUninstallString.Contains( UninstallToolsGlobalConfig.AppLocation, StringComparison.OrdinalIgnoreCase)) { var processIds = SafeGetProcessIds(watchedProcesses).ToArray(); options.Owner.SendProcessesToWatchToDeamon( processIds.Except(previousWatchedProcessIds)); }}
Quiet uninstallers run at lower priority to minimize system impact:
if (options.PreferQuiet && UninstallerEntry.QuietUninstallPossible){ try { uninstaller.PriorityClass = ProcessPriorityClass.BelowNormal; } catch { // Don't care if setting this fails }}
Resource Management: Background uninstallers are deprioritized so you can continue working while the batch operation completes.
# Very silent (no UI at all)unins000.exe /VERYSILENT# Silent with no message boxesunins000.exe /SILENT /SUPPRESSMSGBOXES# Complete silent uninstallunins000.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART