public void SkipWaiting(bool terminate) { lock (_operationLock) { if (Finished) { return; } if (!IsRunning && CurrentStatus == UninstallStatus.Waiting) { CurrentStatus = UninstallStatus.Skipped; } // Do not allow skipping of Msiexec uninstallers because they will hold up the rest of Msiexec uninstallers in the task if (CurrentStatus == UninstallStatus.Uninstalling && UninstallerEntry.UninstallerKind == UninstallerType.Msiexec && !terminate) { return; } _skipLevel = terminate ? SkipCurrentLevel.Terminate : SkipCurrentLevel.Skip; } }
private void UninstallThread(object parameters) { var options = parameters as RunUninstallerOptions; Debug.Assert(options != null, "options != null"); Exception error = null; var retry = false; try { var processSnapshot = Process.GetProcesses().Select(x => x.Id).ToArray(); using (var uninstaller = UninstallerEntry.RunUninstaller(options.PreferQuiet, options.Simulate, _canRetry)) { // Can be null during simulation if (uninstaller == null) { return; } if (options.PreferQuiet && UninstallerEntry.QuietUninstallPossible) { try { uninstaller.PriorityClass = ProcessPriorityClass.BelowNormal; } catch { // Don't care if setting this fails } } var checkCounters = options.PreferQuiet && options.AutoKillStuckQuiet && UninstallerEntry.QuietUninstallPossible; var watchedProcesses = new List <Process> { uninstaller }; int[] previousWatchedProcessIds = { }; var idleCounter = 0; while (true) { if (_skipLevel == SkipCurrentLevel.Skip) { break; } foreach (var watchedProcess in watchedProcesses.ToList()) { watchedProcesses.AddRange(watchedProcess.GetChildProcesses()); } if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { foreach (var watchedProcess in Process.GetProcessesByName("msiexec")) { watchedProcesses.AddRange(watchedProcess.GetChildProcesses()); } } watchedProcesses = CleanupDeadProcesses(watchedProcesses, processSnapshot).ToList(); // Check if we are done, or if there are some proceses left that we missed. // We are done when the entry process and all of its spawns exit. if (watchedProcesses.Count == 0) { if (string.IsNullOrEmpty(UninstallerEntry.InstallLocation)) { break; } FindAndAddProcessesToWatch(watchedProcesses, processSnapshot); if (watchedProcesses.Count == 0) { break; } } // Only try to automate first try. If it fails, don't try to automate // the rerun in case user or app itself can resolve the issue. if (IsSilentPossible && UninstallToolsGlobalConfig.UseQuietUninstallDaemon && _canRetry) { var processIds = SafeGetProcessIds(watchedProcesses).ToArray(); options.Owner.SendProcessesToWatchToDeamon(processIds.Except(previousWatchedProcessIds)); previousWatchedProcessIds = processIds; } // Check for deadlocks during silent uninstall. Prevents the task from getting stuck // idefinitely on stuck uninstallers and unrelated processes spawned by uninstallers. if (checkCounters) { var processNames = SafeGetProcessNames(watchedProcesses); if (TestUninstallerForStalls(processNames)) { idleCounter++; } else { idleCounter = 0; } // Kill the uninstaller (and children) if they were idle/stalled for too long if (idleCounter > 30) { KillProcesses(watchedProcesses); throw new IOException(Localisation.UninstallError_UninstallerTimedOut); } } else { Thread.Sleep(1000); } // Kill the uninstaller (and children) if user told us to or if it was idle for too long if (_skipLevel == SkipCurrentLevel.Terminate) { if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { watchedProcesses.AddRange(Process.GetProcessesByName("Msiexec")); } KillProcesses(watchedProcesses); break; } } if (_skipLevel == SkipCurrentLevel.None) { var exitVar = uninstaller.ExitCode; if (exitVar != 0) { if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec && exitVar == 1602) { // 1602 ERROR_INSTALL_USEREXIT - The user has cancelled the installation. _skipLevel = SkipCurrentLevel.Skip; } else if (UninstallerEntry.UninstallerKind == UninstallerType.Nsis && (exitVar == 1 || exitVar == 2)) { // 1 - Installation aborted by user (cancel button) // 2 - Installation aborted by script (often after user clicks cancel) _skipLevel = SkipCurrentLevel.Skip; } else if (UninstallerEntry.UninstallerKind == UninstallerType.SimpleDelete && (exitVar == 1)) { // 1 - Installation aborted by user (cancel button) _skipLevel = SkipCurrentLevel.Skip; } else if (exitVar == -1073741510) { /* 3221225786 / 0xC000013A / -1073741510 * The application terminated as a result of a CTRL+C. * Indicates that the application has been terminated either by user's * keyboard input CTRL+C or CTRL+Break or closing command prompt window. */ _skipLevel = SkipCurrentLevel.Terminate; } else { switch (exitVar) { case 2: throw new Exception("The system cannot find the file specified. Indicates that the file can not be found in specified location."); case 3: throw new Exception("The system cannot find the path specified. Indicates that the specified path can not be found."); case 5: throw new Exception("Access is denied. Indicates that user has no access right to specified resource."); case 9009: throw new Exception("Program is not recognized as an internal or external command, operable program or batch file."); case -2147024846: throw new Exception("0x80070032 - This app is part of Windows and cannot be uninstalled on a per-user basis."); default: if (options.RetryFailedQuiet || (UninstallerEntry.UninstallerKind == UninstallerType.Nsis && !options.PreferQuiet)) { retry = true; } throw new IOException(Localisation.UninstallError_UninstallerReturnedCode + exitVar); } } } } } } catch (Exception ex) { error = ex; } finally { try { _perfCounterBuffer.ForEach(x => x.Value.Dispose()); } catch { // Ignore any errors to make sure rest of this code runs } _perfCounterBuffer.Clear(); // Take care of the aftermath if (_skipLevel != SkipCurrentLevel.None) { _skipLevel = SkipCurrentLevel.None; CurrentStatus = UninstallStatus.Skipped; CurrentError = new OperationCanceledException(Localisation.ManagerError_Skipped); } else if (error != null) { //Localisation.ManagerError_PrematureWorkerStop is unused CurrentStatus = UninstallStatus.Failed; CurrentError = error; } else { CurrentStatus = UninstallStatus.Completed; } if (retry && _canRetry) { CurrentStatus = UninstallStatus.Waiting; _canRetry = false; } else { Finished = true; } } }
private void UninstallThread(object parameters) { var options = parameters as RunUninstallerOptions; Debug.Assert(options != null, "options != null"); Exception error = null; var retry = false; try { using (var uninstaller = UninstallerEntry.RunUninstaller(options.PreferQuiet, options.Simulate)) { // Can be null during simulation if (uninstaller != null) { if (options.PreferQuiet && UninstallerEntry.QuietUninstallPossible) { uninstaller.PriorityClass = ProcessPriorityClass.BelowNormal; } var checkCounters = options.PreferQuiet && options.AutoKillStuckQuiet && UninstallerEntry.QuietUninstallPossible; List <Process> childProcesses; var idleCounter = 0; do { if (_skipLevel == SkipCurrentLevel.Skip) { break; } childProcesses = uninstaller.GetChildProcesses().Where( p => !p.ProcessName.Contains("explorer", StringComparison.InvariantCultureIgnoreCase)) .ToList(); if (!uninstaller.HasExited) { childProcesses.Add(uninstaller); } if (checkCounters) { if (TestUninstallerForStalls(childProcesses)) { idleCounter++; } // Kill the uninstaller (and children) if they were idle/stalled for too long if (idleCounter > 40) { uninstaller.Kill(true); throw new IOException(Localisation.UninstallError_UninstallerTimedOut); } } else { Thread.Sleep(1000); } // Kill the uninstaller (and children) if user told us to or if it was idle for too long if (_skipLevel == SkipCurrentLevel.Terminate) { uninstaller.Kill(true); if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { foreach (var process in Process.GetProcessesByName("Msiexec")) { try { process.Kill(); } catch (InvalidOperationException) { } catch (Win32Exception) { } catch (NotSupportedException) { } } } break; } } while (!uninstaller.HasExited || childProcesses.Any(p => !p.HasExited)); if (_skipLevel == SkipCurrentLevel.None) { var exitVar = uninstaller.ExitCode; if (exitVar != 0) { if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec && exitVar == 1602) { // 1602 ERROR_INSTALL_USEREXIT - The user has cancelled the installation. _skipLevel = SkipCurrentLevel.Skip; } else if (exitVar == -1073741510) { /* 3221225786 / 0xC000013A / -1073741510 * The application terminated as a result of a CTRL+C. * Indicates that the application has been terminated either by user's * keyboard input CTRL+C or CTRL+Break or closing command prompt window. */ _skipLevel = SkipCurrentLevel.Terminate; } else { switch (exitVar) { // The system cannot find the file specified. Indicates that the file can not be found in specified location. case 2: // The system cannot find the path specified. Indicates that the specified path can not be found. case 3: // Access is denied. Indicates that user has no access right to specified resource. case 5: // Program is not recognized as an internal or external command, operable program or batch file. case 9009: break; default: if (options.RetryFailedQuiet) { retry = true; } break; } throw new IOException(Localisation.UninstallError_UninstallerReturnedCode + exitVar); } } } } } } catch (Exception ex) { error = ex; } // Take care of the aftermath if (_skipLevel != SkipCurrentLevel.None) { _skipLevel = SkipCurrentLevel.None; CurrentStatus = UninstallStatus.Skipped; CurrentError = new OperationCanceledException(Localisation.ManagerError_Skipped); } else if (error != null) { //Localisation.ManagerError_PrematureWorkerStop is unused CurrentStatus = UninstallStatus.Failed; CurrentError = error; } else { CurrentStatus = UninstallStatus.Completed; } if (retry && _canRetry) { CurrentStatus = UninstallStatus.Waiting; _canRetry = false; } else { Finished = true; } IsRunning = false; }
private void UninstallThread(object parameters) { var options = parameters as RunUninstallerOptions; Debug.Assert(options != null, "options != null"); Exception error = null; var retry = false; try { var processSnapshot = Process.GetProcesses().Select(x => x.Id).ToArray(); using (var uninstaller = UninstallerEntry.RunUninstaller(options.PreferQuiet, options.Simulate)) { // Can be null during simulation if (uninstaller == null) { return; } if (options.PreferQuiet && UninstallerEntry.QuietUninstallPossible) { try { uninstaller.PriorityClass = ProcessPriorityClass.BelowNormal; } catch { // Don't care if setting this fails } } var checkCounters = options.PreferQuiet && options.AutoKillStuckQuiet && UninstallerEntry.QuietUninstallPossible; var watchedProcesses = new List <Process> { uninstaller }; var idleCounter = 0; while (true) { if (_skipLevel == SkipCurrentLevel.Skip) { break; } foreach (var watchedProcess in watchedProcesses.ToList()) { watchedProcesses.AddRange(watchedProcess.GetChildProcesses()); } // Msiexec service can start processes, but we don't want to watch the service if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { foreach (var watchedProcess in Process.GetProcessesByName("msiexec")) { watchedProcesses.AddRange(watchedProcess.GetChildProcesses()); } } watchedProcesses = CleanupDeadProcesses(watchedProcesses, processSnapshot).ToList(); // Check if we are done, or if there are some proceses left that we missed if (watchedProcesses.Count == 0) { if (string.IsNullOrEmpty(UninstallerEntry.InstallLocation)) { break; } FindAndAddProcessesToWatch(watchedProcesses, processSnapshot); if (watchedProcesses.Count == 0) { break; } } // Check for deadlocks during silent uninstall if (checkCounters) { var processNames = watchedProcesses.Select(x => { try { return(x.ProcessName); } catch { // Ignore errors caused by processes that exited return(null); } }).Where(x => !string.IsNullOrEmpty(x)); if (TestUninstallerForStalls(processNames)) { idleCounter++; } else { idleCounter = 0; } // Kill the uninstaller (and children) if they were idle/stalled for too long if (idleCounter > 30) { KillProcesses(watchedProcesses); throw new IOException(Localisation.UninstallError_UninstallerTimedOut); } } else { Thread.Sleep(1000); } // Kill the uninstaller (and children) if user told us to or if it was idle for too long if (_skipLevel == SkipCurrentLevel.Terminate) { if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec) { watchedProcesses.AddRange(Process.GetProcessesByName("Msiexec")); } KillProcesses(watchedProcesses); break; } } if (_skipLevel == SkipCurrentLevel.None) { var exitVar = uninstaller.ExitCode; if (exitVar != 0) { if (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec && exitVar == 1602) { // 1602 ERROR_INSTALL_USEREXIT - The user has cancelled the installation. _skipLevel = SkipCurrentLevel.Skip; } else if (UninstallerEntry.UninstallerKind == UninstallerType.Nsis && (exitVar == 1 || exitVar == 2)) { // 1 - Installation aborted by user (cancel button) // 2 - Installation aborted by script (often after user clicks cancel) _skipLevel = SkipCurrentLevel.Skip; } else if (exitVar == -1073741510) { /* 3221225786 / 0xC000013A / -1073741510 * The application terminated as a result of a CTRL+C. * Indicates that the application has been terminated either by user's * keyboard input CTRL+C or CTRL+Break or closing command prompt window. */ _skipLevel = SkipCurrentLevel.Terminate; } else { switch (exitVar) { // The system cannot find the file specified. Indicates that the file can not be found in specified location. case 2: // The system cannot find the path specified. Indicates that the specified path can not be found. case 3: // Access is denied. Indicates that user has no access right to specified resource. case 5: // Program is not recognized as an internal or external command, operable program or batch file. case 9009: break; default: if (options.RetryFailedQuiet) { retry = true; } break; } throw new IOException(Localisation.UninstallError_UninstallerReturnedCode + exitVar); } } } } } catch (Exception ex) { error = ex; } finally { try { _perfCounterBuffer.ForEach(x => x.Value.Dispose()); } catch { // Ignore any errors to make sure rest of this code runs } _perfCounterBuffer.Clear(); // Take care of the aftermath if (_skipLevel != SkipCurrentLevel.None) { _skipLevel = SkipCurrentLevel.None; CurrentStatus = UninstallStatus.Skipped; CurrentError = new OperationCanceledException(Localisation.ManagerError_Skipped); } else if (error != null) { //Localisation.ManagerError_PrematureWorkerStop is unused CurrentStatus = UninstallStatus.Failed; CurrentError = error; } else { CurrentStatus = UninstallStatus.Completed; } if (retry && _canRetry) { CurrentStatus = UninstallStatus.Waiting; _canRetry = false; } else { Finished = true; } } }