Skip to content

Commit

Permalink
Sanity check install location/drive (#474)
Browse files Browse the repository at this point in the history
moved to its own class for better flow control
  • Loading branch information
pizzaboxer committed Jul 30, 2023
1 parent ba45bf4 commit 8945875
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 142 deletions.
44 changes: 7 additions & 37 deletions Bloxstrap/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public partial class App : Application
public static bool ShouldSaveConfigs { get; set; } = false;

public static bool IsSetupComplete { get; set; } = true;
public static bool IsFirstRun { get; private set; } = true;
public static bool IsFirstRun { get; set; } = true;

public static bool IsQuiet { get; private set; } = false;
public static bool IsUninstall { get; private set; } = false;
public static bool IsNoLaunch { get; private set; } = false;
Expand Down Expand Up @@ -149,11 +150,12 @@ protected override void OnStartup(StartupEventArgs e)

if (!IsMenuLaunch)
{
Logger.WriteLine(LOG_IDENT, "Performing connectivity check");
Logger.WriteLine(LOG_IDENT, "Performing connectivity check...");

try
{
HttpClient.GetAsync("https://detectportal.firefox.com").Wait();
Logger.WriteLine(LOG_IDENT, "Connectivity check finished");
}
catch (Exception ex)
{
Expand All @@ -173,41 +175,9 @@ protected override void OnStartup(StartupEventArgs e)
}
}


// check if installed
using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
{
string? installLocation = null;

if (registryKey is not null)
installLocation = (string?)registryKey.GetValue("InstallLocation");

if (registryKey is null || installLocation is null)
{
Logger.WriteLine(LOG_IDENT, "Running first-time install");

BaseDirectory = Path.Combine(Paths.LocalAppData, ProjectName);
Logger.Initialize(true);

if (!IsQuiet)
{
IsSetupComplete = false;
FastFlags.Load();
Controls.ShowMenu();
}
}
else
{
IsFirstRun = false;
BaseDirectory = installLocation;
}
}

// exit if we don't click the install button on installation
if (!IsSetupComplete)
using (var checker = new InstallChecker())
{
Logger.WriteLine(LOG_IDENT, "Installation cancelled!");
Terminate(ErrorCode.ERROR_CANCELLED);
checker.Check();
}

Paths.Initialize(BaseDirectory);
Expand All @@ -234,7 +204,7 @@ protected override void OnStartup(StartupEventArgs e)

#if !DEBUG
if (!IsUninstall && !IsFirstRun)
Updater.CheckInstalledVersion();
InstallChecker.CheckUpgrade();
#endif

string commandLine = "";
Expand Down
230 changes: 230 additions & 0 deletions Bloxstrap/InstallChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
using System.Windows;

using Microsoft.Win32;

namespace Bloxstrap
{
internal class InstallChecker : IDisposable
{
private RegistryKey? _registryKey;
private string? _installLocation;

internal InstallChecker()
{
_registryKey = Registry.CurrentUser.OpenSubKey($"Software\\{App.ProjectName}", true);

if (_registryKey is not null)
_installLocation = (string?)_registryKey.GetValue("InstallLocation");
}

internal void Check()
{
const string LOG_IDENT = "InstallChecker::Check";

if (_registryKey is null || _installLocation is null)
{
if (!File.Exists("Settings.json") || !File.Exists("State.json"))
{
FirstTimeRun();
return;
}

_installLocation = Path.GetDirectoryName(Paths.Process)!;

App.Logger.WriteLine(LOG_IDENT, $"Registry key is likely malformed. Setting install location as '{_installLocation}'");

if (_registryKey is null)
_registryKey = Registry.CurrentUser.CreateSubKey($"Software\\{App.ProjectName}");

_registryKey.SetValue("InstallLocation", _installLocation);
}

// check if drive that bloxstrap was installed to was removed from system, or had its drive letter changed

if (!Directory.Exists(_installLocation))
{
App.Logger.WriteLine(LOG_IDENT, "Could not find install location. Checking if drive has changed...");

bool driveExists = false;
string driveName = _installLocation[..3];
string? newDriveName = null;

foreach (var drive in DriveInfo.GetDrives())
{
if (drive.Name == driveName)
driveExists = true;
else if (Directory.Exists(_installLocation.Replace(driveName, drive.Name)))
newDriveName = drive.Name;
}

if (newDriveName is not null)
{
App.Logger.WriteLine(LOG_IDENT, $"Drive has changed from {driveName} to {newDriveName}");

Controls.ShowMessageBox(
$"{App.ProjectName} has detected a drive letter change and has reconfigured its install location from the {driveName} drive to the {newDriveName} drive.\n" +
"\n" +
$"While {App.ProjectName} will continue to work, it's recommended that you change the drive leter back to its original value as other installed applications can experience similar issues.",
MessageBoxImage.Warning,
MessageBoxButton.OK
);

_installLocation = _installLocation.Replace(driveName, newDriveName);
_registryKey.SetValue("InstallLocation", _installLocation);
}
else if (!driveExists)
{
App.Logger.WriteLine(LOG_IDENT, $"Drive {driveName} does not exist anymore, and has likely been removed");

var result = Controls.ShowMessageBox(
$"{App.ProjectName} was originally installed to the {driveName} drive, but it appears to no longer be present. Would you like to continue and carry out a fresh install?",
MessageBoxImage.Warning,
MessageBoxButton.OKCancel
);

if (result != MessageBoxResult.OK)
App.Terminate();

FirstTimeRun();
return;
}
else
{
App.Logger.WriteLine(LOG_IDENT, "Drive has not changed, folder was likely moved or deleted");
}
}

App.BaseDirectory = _installLocation;
App.IsFirstRun = false;
}

public void Dispose()
{
_registryKey?.Dispose();
GC.SuppressFinalize(this);
}

private static void FirstTimeRun()
{
const string LOG_IDENT = "InstallChecker::FirstTimeRun";

App.Logger.WriteLine(LOG_IDENT, "Running first-time install");

App.BaseDirectory = Path.Combine(Paths.LocalAppData, App.ProjectName);
App.Logger.Initialize(true);

if (App.IsQuiet)
return;

App.IsSetupComplete = false;

App.FastFlags.Load();
Controls.ShowMenu();

// exit if we don't click the install button on installation
if (App.IsSetupComplete)
return;

App.Logger.WriteLine(LOG_IDENT, "Installation cancelled!");
App.Terminate(ErrorCode.ERROR_CANCELLED);
}

internal static void CheckUpgrade()
{
const string LOG_IDENT = "InstallChecker::CheckUpgrade";

if (!File.Exists(Paths.Application) || Paths.Process == Paths.Application)
return;

// 2.0.0 downloads updates to <BaseFolder>/Updates so lol
bool isAutoUpgrade = Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates")) || Paths.Process.StartsWith(Path.Combine(Paths.LocalAppData, "Temp"));

FileVersionInfo existingVersionInfo = FileVersionInfo.GetVersionInfo(Paths.Application);
FileVersionInfo currentVersionInfo = FileVersionInfo.GetVersionInfo(Paths.Process);

if (MD5Hash.FromFile(Paths.Process) == MD5Hash.FromFile(Paths.Application))
return;

MessageBoxResult result;

// silently upgrade version if the command line flag is set or if we're launching from an auto update
if (App.IsUpgrade || isAutoUpgrade)
{
result = MessageBoxResult.Yes;
}
else
{
result = Controls.ShowMessageBox(
$"The version of {App.ProjectName} you've launched is different to the version you currently have installed.\nWould you like to upgrade your currently installed version?",
MessageBoxImage.Question,
MessageBoxButton.YesNo
);
}

if (result != MessageBoxResult.Yes)
return;

// yes, this is EXTREMELY hacky, but the updater process that launched the
// new version may still be open and so we have to wait for it to close
int attempts = 0;
while (attempts < 10)
{
attempts++;

try
{
File.Delete(Paths.Application);
break;
}
catch (Exception)
{
if (attempts == 1)
App.Logger.WriteLine(LOG_IDENT, "Waiting for write permissions to update version");

Thread.Sleep(500);
}
}

if (attempts == 10)
{
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not get write permissions after 5 seconds)");
return;
}

File.Copy(Paths.Process, Paths.Application);

Bootstrapper.Register();

// update migrations

if (App.BuildMetadata.CommitRef.StartsWith("tag") && existingVersionInfo.ProductVersion == "2.4.0")
{
App.FastFlags.SetValue("DFFlagDisableDPIScale", null);
App.FastFlags.SetValue("DFFlagVariableDPIScale2", null);
App.FastFlags.Save();
}

if (isAutoUpgrade)
{
App.NotifyIcon?.ShowAlert(
$"{App.ProjectName} has been upgraded to v{currentVersionInfo.ProductVersion}",
"See what's new in this version",
30,
(_, _) => Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/releases/tag/v{currentVersionInfo.ProductVersion}")
);
}
else if (!App.IsQuiet)
{
Controls.ShowMessageBox(
$"{App.ProjectName} has been upgraded to v{currentVersionInfo.ProductVersion}",
MessageBoxImage.Information,
MessageBoxButton.OK
);

Controls.ShowMenu();

App.Terminate();
}
}
}
}
Loading

0 comments on commit 8945875

Please sign in to comment.