Publically available actions for handling uninstall actions
Inheritance: IDisposable
 internal static void VSFilters(Primitives ip)
 {
     ip.Filters.Add(Filter.CreateFilter("Replace Visual Studio with shorter version", "Microsoft Visual Studio ", "VS "));
     ip.Filters.Add(Filter.CreateFilter("Shorten Microsoft", "Microsoft ", "MS "));
     ip.Filters.Add(Filter.CreateFilter("Shorten Team Foundation Server", "Team Foundation Server ", "TFS "));
     ip.Filters.Add(Filter.CreateFilter("Shorten Visual C++", "Visual C++ ", "VC "));
 }
        internal static void VSUninstallActions(Primitives ip)
        {

            ip.UninstallActions.Add(
                UninstallAction.CreateUninstallAction(
                    new List<ArchitectureConfiguration> { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 },
                    new List<OperatingSystemConfiguration> { OperatingSystemConfiguration.Windows81 },
                    "2999226",
                    UninstallAction.TemplateType.Pre,
                    UninstallAction.WixObjectType.MSU
                    )
                );
            ip.UninstallActions.Add(
                UninstallAction.CreateUninstallAction(
                new List<ArchitectureConfiguration> { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 },
                new List<OperatingSystemConfiguration> { OperatingSystemConfiguration.Windows8 },
                "2999226",
                UninstallAction.TemplateType.Pre,
                UninstallAction.WixObjectType.MSU
                )
            );

            ip.UninstallActions.Add(
                UninstallAction.CreateUninstallAction(
                new List<ArchitectureConfiguration> { ArchitectureConfiguration.x86, ArchitectureConfiguration.x64 },
                new List<OperatingSystemConfiguration> { OperatingSystemConfiguration.Windows7 },
                "2999226",
                UninstallAction.TemplateType.Pre,
                UninstallAction.WixObjectType.MSU
                )
            );

        }
        private static int UninstallMSIs(Primitives ip)
        {
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI uninstall starting"), Logger.MessageLevel.Verbose, AppName);
            var exitcode = 0;

            var Bundles = ip.GetAllInstalledItemsCompareWixPdb;
            foreach (Bundle bundle in Bundles)
            {
                Logger.Log(String.Format(CultureInfo.InvariantCulture, "-Get list of remaining MSIs after bundle uninstall"), Logger.MessageLevel.Information, AppName);
                var packages = bundle.Packages;
                if (packages.Count() > 0)
                {
                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "--MSI uninstall for bundle [{0}]", bundle.Name), Logger.MessageLevel.Information, AppName);
                    Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Bundle: {0} \r\n-- MSIs remaining after uninstall: {1}", bundle.Name, packages.Count().ToString(CultureInfo.InvariantCulture)));
                    var pos = 0;

                    var selectedPackage = bundle.Packages.ElementAtOrDefault(pos);
                    while ((selectedPackage != null) && (exitcode == 0) && (pos <= (bundle.Packages.Count() - 1)))
                    {
                        Logger.Log(String.Format(CultureInfo.InvariantCulture, "---MSI uninstall [{0}]", selectedPackage.ProductName), Logger.MessageLevel.Verbose, AppName);
                        exitcode = ip.Uninstall(selectedPackage);
                        Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "-- {0}", selectedPackage.ProductName));

                        if (exitcode != 0)
                        {
                            Console.WriteLine(Logger.Log(String.Format(CultureInfo.InvariantCulture, "---MSI uninstall [{0}] failed with error code: {1}", selectedPackage.ProductName, exitcode), Logger.MessageLevel.Error, AppName));
                            break;
                        }
                        pos++;
                        selectedPackage = bundle.Packages.ElementAtOrDefault(pos);
                    }
                }
                Logger.Log(String.Format(CultureInfo.InvariantCulture, "--MSI uninstall for bundle [{0}] completed", bundle.Name), Logger.MessageLevel.Information, AppName);
            }
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI uninstall ending"), Logger.MessageLevel.Verbose, AppName);
            return exitcode;
        }
 private static void ProcessUninstallMSIs(Primitives ip)
 {
     Logger.Log(String.Format(CultureInfo.InvariantCulture, "Bundle uninstall completed successfully"), Logger.MessageLevel.Information, AppName);
     Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI uninstall started."), Logger.MessageLevel.Verbose, AppName);
     var msiexitcode = UninstallMSIs(ip);
     if (msiexitcode != 0)
     {
         Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI exited with non-zero exit code: {0}", msiexitcode), Logger.MessageLevel.Information, AppName);
         Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "MSI exited with non-zero exit code: {0}", msiexitcode));
     }
     else
     {
         Logger.Log(String.Format(CultureInfo.InvariantCulture, "MSI batch uninstall completed successfully"), Logger.MessageLevel.Information, AppName);
         Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "MSI batch uninstall completed successfully"));
     }
 }
        private static void ProcessUninstallBundles(Primitives ip, ICollection<Bundle> Bundles)
        {
            // Uninstall only VS selected items
            var pos = 0;
            var selectedbundle = Bundles.ElementAtOrDefault(pos);
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "-Bundle uninstall started."), Logger.MessageLevel.Verbose, AppName);
            var bundleexitcode = 0;
            while ((selectedbundle != null) && (bundleexitcode == 0) && (pos <= (Bundles.Count() - 1)))
            {
                Logger.Log(String.Format(CultureInfo.InvariantCulture, "--Bundle uninstall [{0}]", selectedbundle.Name), Logger.MessageLevel.Verbose, AppName);

                // Is the bundle installed
                if (selectedbundle.Installed)
                {
                    Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Initiating uninstall: {0}", selectedbundle.Name));
                    bundleexitcode = ip.Uninstall(selectedbundle);
                }
                pos++;
                selectedbundle = Bundles.ElementAtOrDefault(pos);
            }
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Bundle uninstall completed"), Logger.MessageLevel.Verbose, AppName);
            switch (bundleexitcode)
            {
                // Reboot error codes from bundle and then from MSU
                case -2147205120:
                case 3010:
                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Reboot exception has been hit from bundle: {0}", selectedbundle.Name), Logger.MessageLevel.Error, AppName);
                    throw new RebootRequiredException("A reboot is required to complete the uninstall process or an error can occur in the uninstall process.");
                case 0:
                    ProcessUninstallMSIs(ip);
                    break;
                default:
                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Bundle exited with non-zero exit code: {0}", bundleexitcode), Logger.MessageLevel.Error, AppName);
                    Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Bundle exited with non-zero exit code: {0}", bundleexitcode));
                    break;
            }
        }
        private static ICollection<Bundle> IfNotProcessed(Primitives ip)
        {
            ICollection<Bundle> Bundles;
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Attempting to load data from wixpdbs."), Logger.MessageLevel.Information, AppName);
            Bundles = ip.GetDataFromPdb(false);
            if (Bundles.FirstOrDefault() == null)
            {
                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Attempting to load data from files."), Logger.MessageLevel.Information, AppName);
                Bundles = ip.LoadFromFiles();
            }

            if (Bundles.FirstOrDefault() == null)
            {
                var msg = "No WixPdbs or output files to use as source.\r\n  Please add WixPdbs or configuration files to the working directory.";
                Console.WriteLine(msg);
                throw new NoSourceFilesAvailableForParsingException(msg);
            }
            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Configuration loaded successfully."), Logger.MessageLevel.Information, AppName);
            return Bundles;
        }
        private static void Main(string[] args)
        {
            if (args != null && args.Count() > 0)
            {
                foreach (var arg in args)
                {
                    switch(arg)
                    {
                        case "break":
                            Console.WriteLine("Program stopped, please attach debugger and then hit any key to continue.");
                            Console.ReadKey(true);
                            break;
                        case "debug":
                            _debug = true;
                            break;
                        case "noprocess":
                            _donotprocess = true;
                            break;
                    }
                }
            }

            ICollection<Bundle> Bundles = new List<Bundle>();
            ICollection<string> Userselected = new List<string>();
            var ip = new Primitives();

            ConsoleOperations.PrimitiveObject = ip;
            ConsoleOperations.SetUpLogging();

            ip.DoNotExecuteProcess = _donotprocess;
            ip.DebugReporting = _debug;

            try
            {
                // Change visual of console application to fit screen.
                ConsoleOperations.SetConsoleAttributes();

                // Check for permissions to run uninstall actions
                var elev = new ElevationDetection();
                if (!elev.Level)
                {
                    ConsoleOperations.SecurityWarning();
                }
                else
                {
                    Logger.Log("Running elevated or as administrator", Logger.MessageLevel.Information, AppName);
                }
                elev = null;

                // Define base variables for use of primitives object; adding filters, uninstall actions, logging location, and default location of data files
                ConsoleOperations.SetupPrimitivesValues(_debug, _donotprocess);

                var cmd = string.Empty;
                while (cmd != "quit")
                {
                    try
                    {
                        var cmdset = cmd.ToUpperInvariant().Split(' ');
                        cmd = cmdset[0];

                        Logger.Log(String.Format(CultureInfo.InvariantCulture, "Command value passed from user: {0}", cmd), Logger.MessageLevel.Information, AppName);
                        switch (cmd)
                        {
                            #region Command processor
                            case ConsoleOperations.COMMAND_COMPARE_TEMP:
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Temp directory command started."), Logger.MessageLevel.Verbose, AppName);
                                ConsoleOperations.OpenTempDirectory();
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Temp directory command ended."), Logger.MessageLevel.Verbose, AppName);
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_SELECT:
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Display select options command started."), Logger.MessageLevel.Verbose, AppName);
                                ConsoleOperations.SelectReleaseFromAvailableBundles();
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Display select options command ended."), Logger.MessageLevel.Verbose, AppName);
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_CREATE:
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Save command started."), Logger.MessageLevel.Verbose, AppName);
                                ip.Processed = false;
                                ip.GetDataFromPdb();
                                ip.SaveAll();
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "Save command ended."), Logger.MessageLevel.Verbose, AppName);
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_LOAD:
                                try
                                {
                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Load command started."), Logger.MessageLevel.Verbose, AppName);
                                    // Load up object files from disk
                                    Bundles = ip.LoadFromFiles();
                                    Console.WriteLine("Files loaded successfully.");
                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Load command ended."), Logger.MessageLevel.Verbose, AppName);
                                }
                                catch (Exception ex)
                                {
                                    Logger.Log(ex, AppName);
                                    Console.WriteLine(ex.Message);
                                }
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_INSTALLED:
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "What is installed command started."), Logger.MessageLevel.Verbose, AppName);
                                // Using WindowsInstaller.Deployment to determine what is installed on the machine.
                                var packages = ip.GetAllInstalledItems;

                                foreach (Package package in packages)
                                {
                                    var pname = package.ProductName.PadRight(50);
                                    var pcode = package.ProductCode.ToString().PadRight(30);
                                    Console.WriteLine(Logger.Log(String.Format(CultureInfo.InvariantCulture, "{0} {1}", pcode, pname), Logger.MessageLevel.Information, AppName));
                                }
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "What is installed command ended."), Logger.MessageLevel.Verbose, AppName);
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_DIRECTORY:
                                ConsoleOperations.ChangeWorkingDirectory(cmdset);
                                break;
                            case ConsoleOperations.COMMAND_COMPARE_LIST:  // What releases were loaded from config files or wixpdbs
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "List releases command started."), Logger.MessageLevel.Verbose, AppName);
                                Console.WriteLine(ConsoleOperations.PrintReleaseInfo());
                                Logger.Log(String.Format(CultureInfo.InvariantCulture, "List releases command ended."), Logger.MessageLevel.Verbose, AppName);
                                break;
                            default:
                                #region Make sure something is selected before executing these commands
                                // Check for if something was selected
                                if (ip.Releases.FirstOrDefault(x => x.Selected) != null)
                                {
                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Selection count is greater than 0."), Logger.MessageLevel.Information, AppName);
                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "Check if Bundles object has an objects populated."), Logger.MessageLevel.Verbose, AppName);
                                    if (!ip.Processed)
                                    {
                                        Logger.Log(String.Format(CultureInfo.InvariantCulture, "Populate Bundles object started"), Logger.MessageLevel.Verbose, AppName);
                                        Bundles = IfNotProcessed(ip);
                                        Logger.Log(String.Format(CultureInfo.InvariantCulture, "Populate Bundles object ended"), Logger.MessageLevel.Verbose, AppName);
                                    }
                                    else { Bundles = ip.Releases.Where(x => x.Selected).ToList(); }

                                    switch (cmd) // Commands that require release(s) to be selected
                                    {
                                        case ConsoleOperations.COMMAND_COMPARE_UNINSTALL:
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Uninstall command started."), Logger.MessageLevel.Verbose, AppName);
                                            ProcessUninstallBundles(ip, Bundles);
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Uninstall command ended."), Logger.MessageLevel.Verbose, AppName);
                                            break;
                                        case ConsoleOperations.COMMAND_COMPARE_MSIS:
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Uninstall MSI command started."), Logger.MessageLevel.Verbose, AppName);
                                            ProcessUninstallMSIs(ip);
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Uninstall MSI command ended."), Logger.MessageLevel.Verbose, AppName);
                                            break;
                                        case ConsoleOperations.COMMAND_COMPARE_VSINSTALLED:
                                            // On a given machine, this what VS installed.
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "VS Installs command started."), Logger.MessageLevel.Verbose, AppName);
                                            foreach (Bundle bundle in ip.GetAllInstalledItemsCompareWixPdb)
                                            {
                                                Console.WriteLine(bundle.Name);
                                                foreach (Package package in bundle.Packages)
                                                {
                                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "-- {0}", ip.ApplyFilter(package.ProductName).PadRight(40) + package.ProductCode.ToString().PadRight(30)), Logger.MessageLevel.Information, AppName);
                                                    Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "-- {0}", ip.ApplyFilter(package.ProductName).PadRight(40) + package.ProductCode.ToString().PadRight(30)));
                                                }
                                            }
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "VS Installs command ended."), Logger.MessageLevel.Verbose, AppName);
                                            break;
                                        case ConsoleOperations.COMMAND_COMPARE_LOGSELECTED:
                                            // Writes out product name, product code, and package type
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Log selected command started."), Logger.MessageLevel.Verbose, AppName);
                                            foreach (Bundle bundle in Bundles)
                                            {
                                                Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Logging start: {0}", bundle.Name));
                                                foreach (Package package in bundle.Packages)
                                                {
                                                    Logger.Log(String.Format(CultureInfo.InvariantCulture, "-- {0}", ip.ApplyFilter(package.ProductName).PadRight(80) + package.ChainingPackage.PadRight(60) + package.ProductCode.ToString().PadRight(45)) + package.Type.ToString().PadRight(30), Logger.MessageLevel.Information, AppName);
                                                }
                                                Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Logging end: {0}", bundle.Name));
                                            }
                                            Logger.Log(String.Format(CultureInfo.InvariantCulture, "Log selected command started."), Logger.MessageLevel.Verbose, AppName);
                                            break;
                                    }
                                }
                                else if ((Userselected.Count == 0) && (!String.IsNullOrEmpty(cmd)))
                                {
                                    Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Use the 'Select' command to determine which bundles you want to try \r\n to uninstall"));
                                }
                                break;  // End check for if something was selected
                                #endregion
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(Logger.Log(ex, AppName));
                    }

                    if (ConsoleOperations.Options.Where(x => x.CommandCompareValue.Contains(cmd)) == null)
                    {
                        Console.WriteLine("\r\nInvalid command. Please try again.\r");
                    }

                    if (!String.IsNullOrEmpty(cmd))
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        Console.WriteLine("\r\n\r\nPress any key to continue.");
                        Console.ReadLine();
                        Console.ResetColor();
                        Console.Clear();
                    }

                    ConsoleOperations.GetUsage();
                    cmd = Console.ReadLine();
                    Console.Clear();
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex, AppName);
            }
            finally
            {
                ip.Dispose();
            }
        }
        private static int Main(string[] args)
        {
            string wixpdbsPathsFile = string.Empty;
            string[] wixpdbsPaths = null;
            string dataFilePath = string.Empty;
            //args = new string[] { "noprocess", @"/wixpdbs:C:\Users\user\Desktop\test\paths.txt" };
            //args = new string[] { "noprocess", @"/binfile:C:\Users\user\Desktop\test\DataFile.bin" };
            //args = new string[] { "noprocess", @"/binfile:C:\Users\user\Desktop\test\DataFile.bin", @"/wixpdbs:\\myshare\Drops\user\wixpdbsPS\sub\Files.txt" };
            if (args != null && args.Count() > 0)
            {
                foreach (var arg in args)
                {
                    switch(arg.ToLowerInvariant())
                    {
                        case "help":
                        case "/help":
                        case "/?":
                            PrintUsage();
                            return 0;
                        case "break":
                            Console.WriteLine("Program stopped, please attach debugger and then hit any key to continue.");
                            Console.ReadKey(true);
                            break;
                        case "debug":
                            _debug = true;
                            break;
                        case "noprocess":
                            _donotprocess = true;
                            break;
                        default:
                            // Path to the file containing a list of paths to the wixpdbs.
                            // e.g. /wixpdbs:c:\myPaths.txt
                            if (arg.StartsWith("/wixpdbs:", StringComparison.OrdinalIgnoreCase))
                            {
                                wixpdbsPathsFile = arg.Substring("/wixpdbs:".Length);
                                wixpdbsPaths = File.ReadAllLines(wixpdbsPathsFile);
                            }
                            // Path to the file containing the DataFile.bin; if no file is passed in, it will use the embedded one.
                            // e.g. /binfile:C:\DataFile.bin
                            else if (arg.StartsWith("/binfile:", StringComparison.OrdinalIgnoreCase))
                            {
                                dataFilePath = arg.Substring("/binfile:".Length);
                            }
                            break;
                    }
                }
            }

            var ip = new Primitives();

            ConsoleOperations.PrimitiveObject = ip;
            ConsoleOperations.SetUpLogging();

            ip.DoNotExecuteProcess = _donotprocess;
            ip.DebugReporting = _debug;

            try
            {
                // Check for permissions to run uninstall actions
                var elev = new ElevationDetection();
                if (!elev.Level)
                {
                    ConsoleOperations.SecurityWarning();
                    return 0;
                }
                else
                {
                    Logger.Log("Running elevated or as administrator", Logger.MessageLevel.Information, AppName);
                }
                elev = null;

                // Define base variables for use of primitives object; adding filters, uninstall actions, logging location, and default location of data files
                ConsoleOperations.SetupPrimitivesValues(_debug, _donotprocess);

                // If /wixpdbs is used, .bin data file is generated for the user.
                if (wixpdbsPaths != null && wixpdbsPaths.Length > 0)
                {
                    if (!string.IsNullOrEmpty(dataFilePath) && File.Exists(dataFilePath))
                    {
                        Logger.LogWithOutput(string.Format("Loading from {0}", dataFilePath));
                        ip.LoadFromDataFile(dataFilePath);
                    }

                    Logger.LogWithOutput("Generating data file from wixpdbs ....");
                    foreach (var wixpdbPath in wixpdbsPaths)
                    {
                        ip.LoadFromWixpdb(wixpdbPath);
                    }

                    ip.SaveToDataFile();
                    Logger.Log("Data File generation operation is successful.  Exiting ...", Logger.MessageLevel.Information, AppName);
                    return 0;
                }
                // Else uninstall Visual Studio 2013/2015/vNext
                else
                {
                    if (!string.IsNullOrEmpty(dataFilePath) && File.Exists(dataFilePath))
                    {
                        ip.LoadFromDataFile(dataFilePath);
                    }
                    else
                    {
                        // load from embedded.
                        var assembly = Assembly.GetExecutingAssembly();
                        var dataFile = "Microsoft.VisualStudio.Setup.DataFile.bin";

                        using (Stream stream = assembly.GetManifestResourceStream(dataFile))
                        {
                            ip.LoadFromDataFile(stream);
                        }
                    }

                    ip.InstalledVisualStudioReport();
                    Logger.LogWithOutput("WARNING: This executable is designed to cleanup/scorch all Preview/RC/RTM releases of Visual Studio 2013, Visual Studio 2015 and Visual Studio vNext.");
                    Logger.LogWithOutput("It should be used as the last resort to clean up the user's system before resorting to reimaging the machine. ");
                    Logger.LogWithOutput("Would you like to continue? [Y/N]");
                    var action = Console.ReadLine();
                    if (!string.IsNullOrEmpty(action) && action.StartsWith("y", StringComparison.OrdinalIgnoreCase))
                    {
                        // cache the vs dirs in memory before uninstalling.
                        var vsDirs = GetVisualStudioInstallationDirs();

                        int exitCode = ip.Uninstall();

                        if (exitCode == 3010)
                        {
                            Logger.LogWithOutput("Bundle requested to reboot the system.  Please reboot your computer and run this application again.");
                            return 3010;
                        }
                        ip.CleanupVisualStudioFolders(vsDirs);
                        ip.CleanupSecondaryInstallerCache();
                        ip.CleanupVisualStudioRegistryHives();
                    }
                    else
                    {
                        Logger.LogWithOutput("Exiting ...");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex, AppName);
            }
            finally
            {
                ip.Dispose();
            }

            return 0;
        }