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 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 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; }