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;
            }
        }
Пример #2
0
        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;
        }
Пример #4
0
        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;
                }
            }
        }