feat: add uninstall code

This commit is contained in:
rjindael 2023-08-01 22:59:28 -07:00
parent f68ce87cb5
commit 7890fa380a
No known key found for this signature in database
GPG Key ID: D069369C906CCF31
9 changed files with 103 additions and 20 deletions

View File

@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Reflection;
using Microsoft.Win32;
@ -15,14 +16,69 @@ namespace Kiseki.Launcher.Windows
Directory.CreateDirectory(Directories.Base);
}
// TODO: Implement this
public static void Register()
public static void Uninstall(bool quiet = false)
{
using (RegistryKey applicationKey = Registry.CurrentUser.CreateSubKey($@"Software\{Constants.PROJECT_NAME}"))
DialogResult answer = quiet ? DialogResult.Yes : MessageBox.Show($"Are you sure you want to uninstall {Constants.PROJECT_NAME}?", Constants.PROJECT_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (answer != DialogResult.Yes)
Environment.Exit((int)Win32.ErrorCode.ERROR_CANCELLED);
// Close active processes
if (Process.GetProcessesByName($"{Constants.PROJECT_NAME}.Player").Any() || Process.GetProcessesByName($"{Constants.PROJECT_NAME}.Studio").Any())
{
applicationKey.SetValue("InstallLocation", Directories.Base);
answer = quiet ? DialogResult.Yes : MessageBox.Show($"Kiseki is currently running. Would you like to close Kiseki now?", Constants.PROJECT_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (answer != DialogResult.Yes)
Environment.Exit((int)Win32.ErrorCode.ERROR_CANCELLED);
try
{
foreach (Process process in Process.GetProcessesByName($"{Constants.PROJECT_NAME}.Player"))
process.Kill();
foreach (Process process in Process.GetProcessesByName($"{Constants.PROJECT_NAME}.Studio"))
process.Kill();
}
catch
{
Environment.Exit((int)Win32.ErrorCode.ERROR_INTERNAL_ERROR);
}
}
// Delete all files
Directory.Delete(Directories.Logs, true);
Directory.Delete(Directories.Versions, true);
Directory.Delete(Directories.License);
// Cleanup our registry entries
Unregister();
Protocol.Unregister();
answer = quiet ? DialogResult.OK : MessageBox.Show($"Sucessfully uninstalled {Constants.PROJECT_NAME}!", Constants.PROJECT_NAME, MessageBoxButtons.OK, MessageBoxIcon.Information);
if (answer == DialogResult.OK || answer == DialogResult.Cancel)
{
string command = $"del /Q \"{Directories.Application}\"";
if (Directory.GetFiles(Directories.Base, "*", SearchOption.AllDirectories).Length == 1)
{
// We're the only file in the directory, so we can delete the entire directory
command += $" && rmdir \"{Directories.Base}\"";
}
Process.Start(new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/c timeout 5 && {command}",
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden
});
Environment.Exit((int)Win32.ErrorCode.ERROR_SUCCESS);
}
}
public static void Register()
{
using RegistryKey uninstallKey = Registry.CurrentUser.CreateSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Constants.PROJECT_NAME}");
uninstallKey.SetValue("DisplayIcon", $"{Directories.Application},0");
@ -42,10 +98,18 @@ namespace Kiseki.Launcher.Windows
uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{Constants.PROJECT_REPOSITORY}/releases/latest");
}
// TODO: Implement this
public static void Unregister()
{
Registry.CurrentUser.DeleteSubKey($@"Software\{Constants.PROJECT_NAME}");
try
{
Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Constants.PROJECT_NAME}");
}
catch
{
#if DEBUG
throw;
#endif
}
}
#endregion

View File

@ -47,6 +47,12 @@ namespace Kiseki.Launcher.Windows
return;
}
if (args[0] == "uninstall")
{
Launcher.Uninstall(args[0] == "-quiet");
return;
}
ApplicationConfiguration.Initialize();
Application.Run(new MainWindow(args[0]));
}

View File

@ -6,17 +6,17 @@ namespace Kiseki.Launcher.Windows
{
public const string PROTOCOL_KEY = "kiseki";
public void Register(string key, string name, string handler)
public static void Register()
{
string arguments = $"\"{handler}\" \"%1\"";
string arguments = $"\"{Directories.Application}\" \"%1\"";
RegistryKey uriKey = Registry.CurrentUser.CreateSubKey(@$"Software\Classes\{key}");
RegistryKey uriKey = Registry.CurrentUser.CreateSubKey(@$"Software\Classes\{PROTOCOL_KEY}");
RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon");
RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command");
if (uriKey.GetValue("") is null)
{
uriKey.SetValue("", $"URL: {name} Protocol");
uriKey.SetValue("", $"URL: {Constants.PROJECT_NAME} Protocol");
uriKey.SetValue("URL Protocol", "");
}
@ -30,9 +30,9 @@ namespace Kiseki.Launcher.Windows
uriCommandKey.Close();
}
public void Unregister(string key)
public static void Unregister()
{
Registry.CurrentUser.DeleteSubKeyTree(@$"Software\Classes\{key}");
Registry.CurrentUser.DeleteSubKeyTree(@$"Software\Classes\{PROTOCOL_KEY}");
}
}
}

View File

@ -4,13 +4,24 @@ namespace Kiseki.Launcher.Windows
{
public static class Win32
{
[DllImport("shell32", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
private static extern string SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, nint hToken = default);
// https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
// https://i-logic.com/serial/errorcodes.htm
public enum ErrorCode
{
ERROR_SUCCESS = 0,
ERROR_INSTALL_USEREXIT = 1602,
ERROR_INSTALL_FAILURE = 1603,
ERROR_CANCELLED = 1223,
ERROR_INTERNAL_ERROR = 1359
}
// https://www.codeproject.com/Articles/878605/Getting-All-Special-Folders-in-NET
public static string GetDownloadsPath()
{
return SHGetKnownFolderPath(new("374DE290-123F-4565-9164-39C4925E467B"), 0);
}
[DllImport("shell32", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
private static extern string SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, nint hToken = default);
}
}

View File

@ -49,6 +49,7 @@ namespace Kiseki.Launcher
{
PageHeadingChange("Downloading Kiseki...");
ProgressBarStateChange(ProgressBarState.Normal);
marquee = false;
}
@ -60,6 +61,7 @@ namespace Kiseki.Launcher
await Task.Delay(2800);
yield return 4;
await Task.Delay(200);
}

View File

@ -9,6 +9,7 @@ namespace Kiseki.Launcher.Helpers
try
{
string json = Web.HttpClient.GetStringAsync(url).Result;
return JsonSerializer.Deserialize<T>(json);
}
catch

View File

@ -3,6 +3,7 @@ namespace Kiseki.Launcher
public interface ILauncher
{
static abstract void Install();
static abstract void Uninstall(bool quiet = false);
static abstract void Register();
static abstract void Unregister();
}

View File

@ -2,7 +2,7 @@ namespace Kiseki.Launcher
{
public interface IProtocol
{
void Register(string key, string name, string handler);
void Unregister(string key);
static abstract void Register();
static abstract void Unregister();
}
}

View File

@ -22,12 +22,11 @@ namespace Kiseki.Launcher
CurrentUrl = IsInMaintenance ? $"{MAINTENANCE_DOMAIN}.{BASE_URL}" : BASE_URL;
int response = CheckHealth();
if (response != RESPONSE_SUCCESS)
{
if (response == RESPONSE_MAINTENANCE)
{
IsInMaintenance = true;
}
return false;
}
@ -55,10 +54,9 @@ namespace Kiseki.Launcher
HttpClient.DefaultRequestHeaders.Clear();
var headers = JsonSerializer.Deserialize<Dictionary<string, string>>(license)!;
for (int i = 0; i < headers.Count; i++)
{
HttpClient.DefaultRequestHeaders.Add(headers.ElementAt(i).Key, headers.ElementAt(i).Value);
}
}
catch
{