From 7cda363fa728e7278c42ed7e3f01b032f9493b50 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sun, 2 Jul 2023 23:46:16 +0100 Subject: [PATCH] Install location warning, +reliable uninstalling --- Bloxstrap/Bootstrapper.cs | 63 +++++++++++------ Bloxstrap/Directories.cs | 6 +- Bloxstrap/Logger.cs | 4 +- .../ViewModels/Menu/InstallationViewModel.cs | 5 +- .../UI/ViewModels/Menu/MainWindowViewModel.cs | 68 +++++++++++++------ 5 files changed, 99 insertions(+), 47 deletions(-) diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index ec9e417c..9e83cbdb 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -683,43 +683,68 @@ private void Uninstall() ProtocolHandler.Register("roblox-player", "Roblox", bootstrapperLocation); } - try - { - // delete application key - Registry.CurrentUser.DeleteSubKey($@"Software\{App.ProjectName}"); - - // delete start menu folder - Directory.Delete(Directories.StartMenu, true); - - // delete desktop shortcut - File.Delete(Path.Combine(Directories.Desktop, "Play Roblox.lnk")); + // if the folder we're installed to does not end with "Bloxstrap", we're installed to a user-selected folder + // in which case, chances are they chose to install to somewhere they didn't really mean to (prior to the added warning in 2.4.0) + // if so, we're walking on eggshells and have to ensure we only clean up what we need to clean up + bool cautiousUninstall = !Directories.Base.EndsWith(App.ProjectName); - // delete uninstall key - Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{App.ProjectName}"); + var cleanupSequence = new List + { + () => Registry.CurrentUser.DeleteSubKey($@"Software\{App.ProjectName}"), + () => Directory.Delete(Directories.StartMenu, true), + () => File.Delete(Path.Combine(Directories.Desktop, "Play Roblox.lnk")), + () => Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{App.ProjectName}") + }; - // delete installation folder - // (should delete everything except bloxstrap itself) - Directory.Delete(Directories.Base, true); + if (cautiousUninstall) + { + cleanupSequence.Add(() => Directory.Delete(Directories.Downloads, true)); + cleanupSequence.Add(() => Directory.Delete(Directories.Modifications, true)); + cleanupSequence.Add(() => Directory.Delete(Directories.Versions, true)); + cleanupSequence.Add(() => Directory.Delete(Directories.Logs, true)); + + cleanupSequence.Add(() => File.Delete(App.Settings.FileLocation)); + cleanupSequence.Add(() => File.Delete(App.State.FileLocation)); + } + else + { + cleanupSequence.Add(() => Directory.Delete(Directories.Base, true)); } - catch (Exception ex) + + foreach (var process in cleanupSequence) { - App.Logger.WriteLine($"Could not fully uninstall! ({ex})"); + try + { + process(); + } + catch (Exception ex) + { + App.Logger.WriteLine($"[Bootstrapper::Uninstall] Encountered exception when running cleanup sequence (#{cleanupSequence.IndexOf(process)})"); + App.Logger.WriteLine($"[Bootstrapper::Uninstall] {ex}"); + } } Action? callback = null; if (Directory.Exists(Directories.Base)) { - callback = () => + callback = delegate { // this is definitely one of the workaround hacks of all time // could antiviruses falsely detect this as malicious behaviour though? // "hmm whats this program doing running a cmd command chain quietly in the background that auto deletes an entire folder" + string deleteCommand; + + if (cautiousUninstall) + deleteCommand = $"del /Q \"{Directories.Application}\""; + else + deleteCommand = $"del /Q \"{Directories.Base}\\*\" && rmdir \"{Directories.Base}\""; + Process.Start(new ProcessStartInfo() { FileName = "cmd.exe", - Arguments = $"/c timeout 5 && del /Q \"{Directories.Base}\\*\" && rmdir \"{Directories.Base}\"", + Arguments = $"/c timeout 5 && {deleteCommand}", UseShellExecute = true, WindowStyle = ProcessWindowStyle.Hidden }); diff --git a/Bloxstrap/Directories.cs b/Bloxstrap/Directories.cs index 877c7862..faddb26d 100644 --- a/Bloxstrap/Directories.cs +++ b/Bloxstrap/Directories.cs @@ -15,23 +15,27 @@ static class Directories public static string Base { get; private set; } = ""; public static string Downloads { get; private set; } = ""; + public static string Logs { get; private set; } = ""; public static string Integrations { get; private set; } = ""; public static string Versions { get; private set; } = ""; public static string Modifications { get; private set; } = ""; public static string Application { get; private set; } = ""; - public static bool Initialized => string.IsNullOrEmpty(Base); + public static bool Initialized => !String.IsNullOrEmpty(Base); public static void Initialize(string baseDirectory) { Base = baseDirectory; Downloads = Path.Combine(Base, "Downloads"); + Logs = Path.Combine(Base, "Logs"); Integrations = Path.Combine(Base, "Integrations"); Versions = Path.Combine(Base, "Versions"); Modifications = Path.Combine(Base, "Modifications"); Application = Path.Combine(Base, $"{App.ProjectName}.exe"); + + } } } diff --git a/Bloxstrap/Logger.cs b/Bloxstrap/Logger.cs index 2b54d928..aabc96e0 100644 --- a/Bloxstrap/Logger.cs +++ b/Bloxstrap/Logger.cs @@ -56,9 +56,9 @@ public void Initialize(bool useTempDir = false) FileLocation = location; // clean up any logs older than a week - if (!useTempDir) + if (Directories.Initialized && Directory.Exists(Directories.Logs)) { - foreach (FileInfo log in new DirectoryInfo(directory).GetFiles()) + foreach (FileInfo log in new DirectoryInfo(Directories.Logs).GetFiles()) { if (log.LastWriteTimeUtc.AddDays(7) > DateTime.UtcNow) continue; diff --git a/Bloxstrap/UI/ViewModels/Menu/InstallationViewModel.cs b/Bloxstrap/UI/ViewModels/Menu/InstallationViewModel.cs index 9399fbf3..43d33eb0 100644 --- a/Bloxstrap/UI/ViewModels/Menu/InstallationViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Menu/InstallationViewModel.cs @@ -69,10 +69,7 @@ private void BrowseInstallLocation() if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; - if (!dialog.SelectedPath.EndsWith(App.ProjectName)) - InstallLocation = Path.Combine(dialog.SelectedPath, App.ProjectName); - else - InstallLocation = dialog.SelectedPath; + InstallLocation = dialog.SelectedPath; OnPropertyChanged(nameof(InstallLocation)); } diff --git a/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs b/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs index 45b3c582..2a239a43 100644 --- a/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs @@ -10,7 +10,7 @@ using Wpf.Ui.Controls.Interfaces; using Wpf.Ui.Mvvm.Contracts; -using Bloxstrap.UI.MessageBox; +using System.Linq; namespace Bloxstrap.UI.ViewModels.Menu { @@ -41,27 +41,53 @@ private void ConfirmSettings() return; } - try - { - // check if we can write to the directory (a bit hacky but eh) - string testFile = Path.Combine(App.BaseDirectory, $"{App.ProjectName}WriteTest.txt"); + bool shouldCheckInstallLocation = App.IsFirstRun || App.BaseDirectory != _originalBaseDirectory; - Directory.CreateDirectory(App.BaseDirectory); - File.WriteAllText(testFile, "hi"); - File.Delete(testFile); - } - catch (UnauthorizedAccessException) - { - Controls.ShowMessageBox( - $"{App.ProjectName} does not have write access to the install location you selected. Please choose another install location.", - MessageBoxImage.Error - ); - return; - } - catch (Exception ex) + if (shouldCheckInstallLocation) { - Controls.ShowMessageBox(ex.Message, MessageBoxImage.Error); - return; + try + { + // check if we can write to the directory (a bit hacky but eh) + string testFile = Path.Combine(App.BaseDirectory, $"{App.ProjectName}WriteTest.txt"); + + Directory.CreateDirectory(App.BaseDirectory); + File.WriteAllText(testFile, "hi"); + File.Delete(testFile); + } + catch (UnauthorizedAccessException) + { + Controls.ShowMessageBox( + $"{App.ProjectName} does not have write access to the install location you've selected. Please choose another location.", + MessageBoxImage.Error + ); + return; + } + catch (Exception ex) + { + Controls.ShowMessageBox(ex.Message, MessageBoxImage.Error); + return; + } + + if (!App.BaseDirectory.EndsWith(App.ProjectName) && Directory.Exists(App.BaseDirectory) && Directory.EnumerateFileSystemEntries(App.BaseDirectory).Any()) + { + string suggestedChange = Path.Combine(App.BaseDirectory, App.ProjectName); + + MessageBoxResult result = Controls.ShowMessageBox( + $"The folder you've chosen to install {App.ProjectName} to already exists and is NOT empty. It is strongly recommended for {App.ProjectName} to be installed to its own independent folder.\n\n" + + "Changing to the following location is suggested:\n" + + $"{suggestedChange}\n\n" + + "Would you like to change your install location to this?\n" + + "Selecting 'No' will ignore this warning and continue installation.", + MessageBoxImage.Warning, + MessageBoxButton.YesNoCancel, + MessageBoxResult.Yes + ); + + if (result == MessageBoxResult.Yes) + App.BaseDirectory = suggestedChange; + else if (result == MessageBoxResult.Cancel) + return; + } } if (!App.IsFirstRun) @@ -69,7 +95,7 @@ private void ConfirmSettings() App.ShouldSaveConfigs = true; App.FastFlags.Save(); - if (App.BaseDirectory != _originalBaseDirectory) + if (shouldCheckInstallLocation) { App.Logger.WriteLine($"[MainWindowViewModel::ConfirmSettings] Changing install location from {_originalBaseDirectory} to {App.BaseDirectory}");