/// <summary>
        ///     Run the uninstaller on a new thread.
        /// </summary>
        internal void RunUninstaller(RunUninstallerOptions options)
        {
            lock (_operationLock)
            {
                if (Finished || IsRunning || CurrentStatus != UninstallStatus.Waiting)
                {
                    return;
                }

                if ((UninstallerEntry.IsRegistered && !UninstallerEntry.RegKeyStillExists()) ||
                    (UninstallerEntry.UninstallerKind == UninstallerType.Msiexec &&
                     MsiTools.MsiEnumProducts().All(g => !g.Equals(UninstallerEntry.BundleProviderKey))))
                {
                    CurrentStatus = UninstallStatus.Completed;
                    Finished      = true;
                    return;
                }

                CurrentStatus = UninstallStatus.Uninstalling;
                IsRunning     = true;
            }

            var worker = new Thread(UninstallThread)
            {
                Name = "RunBulkUninstall_Worker"
            };

            worker.Start(options);
        }
        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;
        }