mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge pull request #127 from pizzaboxer/bugfix-124
Add proper global exception handling
This commit is contained in:
commit
48de954eb0
@ -3,7 +3,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Bloxstrap"
|
xmlns:local="clr-namespace:Bloxstrap"
|
||||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
ShutdownMode="OnExplicitShutdown">
|
ShutdownMode="OnExplicitShutdown"
|
||||||
|
DispatcherUnhandledException="GlobalExceptionHandler">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
@ -8,6 +8,8 @@ using System.Reflection;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
using Bloxstrap.Dialogs;
|
using Bloxstrap.Dialogs;
|
||||||
@ -77,35 +79,25 @@ namespace Bloxstrap
|
|||||||
Logger.Initialize(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
|
Logger.Initialize(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
|
||||||
|
Logger.WriteLine("[App::OnStartup] An exception occurred when running the main thread");
|
||||||
|
Logger.WriteLine($"[App::OnStartup] {e.Exception}");
|
||||||
|
|
||||||
|
if (!IsQuiet)
|
||||||
|
Settings.Prop.BootstrapperStyle.GetNew().ShowError($"{e.Exception.GetType()}: {e.Exception.Message}");
|
||||||
|
|
||||||
|
Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Starting {ProjectName} v{Version}");
|
Logger.WriteLine($"[App::OnStartup] Starting {ProjectName} v{Version}");
|
||||||
|
|
||||||
// todo: remove this once 32-bit support is fully gone
|
|
||||||
if (!App.IsQuiet && !Environment.Is64BitOperatingSystem)
|
|
||||||
{
|
|
||||||
string message = "In the near future, Roblox will no longer support 32-bit Windows devices. To keep playing Roblox, please use a device that is 64-bit compatible.";
|
|
||||||
|
|
||||||
// check if the processor actually supports 64-bit with wmic
|
|
||||||
// chances are the user just has a 32-bit version of windows installed on 64-bit hardware
|
|
||||||
Process p = new();
|
|
||||||
p.StartInfo.CreateNoWindow = true;
|
|
||||||
p.StartInfo.UseShellExecute = false;
|
|
||||||
p.StartInfo.RedirectStandardOutput = true;
|
|
||||||
p.StartInfo.FileName = "wmic.exe";
|
|
||||||
p.StartInfo.Arguments = "cpu get Architecture";
|
|
||||||
p.Start();
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor
|
|
||||||
// architecture type 9 is x64
|
|
||||||
if (p.StandardOutput.ReadToEnd().Contains('9'))
|
|
||||||
message += "\n\nYour computer is running a 32-bit version of Windows but is actually 64-bit compatible. Search online for how to upgrade to a 64-bit version of Windows.";
|
|
||||||
|
|
||||||
ShowMessageBox(message, MessageBoxImage.Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
// To customize application configuration such as set high DPI settings or default font,
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
// see https://aka.ms/applicationconfiguration.
|
// see https://aka.ms/applicationconfiguration.
|
||||||
ApplicationConfiguration.Initialize();
|
ApplicationConfiguration.Initialize();
|
||||||
@ -148,6 +140,29 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove this once 32-bit support is fully gone
|
||||||
|
if (!App.IsQuiet && !Environment.Is64BitOperatingSystem)
|
||||||
|
{
|
||||||
|
string message = "In the near future, Roblox will no longer support 32-bit Windows devices. To keep playing Roblox, please use a device that is 64-bit compatible.";
|
||||||
|
|
||||||
|
// check if the processor actually supports 64-bit with wmic
|
||||||
|
// chances are the user just has a 32-bit version of windows installed on 64-bit hardware
|
||||||
|
Process p = new();
|
||||||
|
p.StartInfo.CreateNoWindow = true;
|
||||||
|
p.StartInfo.UseShellExecute = false;
|
||||||
|
p.StartInfo.RedirectStandardOutput = true;
|
||||||
|
p.StartInfo.FileName = "wmic.exe";
|
||||||
|
p.StartInfo.Arguments = "cpu get Architecture";
|
||||||
|
p.Start();
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor
|
||||||
|
// architecture type 9 is x64
|
||||||
|
if (p.StandardOutput.ReadToEnd().Contains('9'))
|
||||||
|
message += "\n\nYour computer is running a 32-bit version of Windows but is actually 64-bit compatible. Search online for how to upgrade to a 64-bit version of Windows.";
|
||||||
|
|
||||||
|
ShowMessageBox(message, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
// check if installed
|
// check if installed
|
||||||
using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
|
using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
|
||||||
{
|
{
|
||||||
@ -192,151 +207,140 @@ namespace Bloxstrap
|
|||||||
Settings.Load();
|
Settings.Load();
|
||||||
State.Load();
|
State.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
try
|
if (!IsUninstall && !IsFirstRun)
|
||||||
{
|
Updater.CheckInstalledVersion();
|
||||||
if (!IsUninstall && !IsFirstRun)
|
|
||||||
Updater.CheckInstalledVersion();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
string commandLine = "";
|
string commandLine = "";
|
||||||
|
|
||||||
if (IsMenuLaunch)
|
if (IsMenuLaunch)
|
||||||
|
{
|
||||||
|
Mutex mutex;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
mutex = Mutex.OpenExisting("Bloxstrap_MenuMutex");
|
||||||
|
Logger.WriteLine("[App::OnStartup] Bloxstrap_MenuMutex mutex exists, aborting menu launch...");
|
||||||
try
|
Terminate();
|
||||||
{
|
|
||||||
mutex = Mutex.OpenExisting("Bloxstrap_MenuMutex");
|
|
||||||
Logger.WriteLine("[App::OnStartup] Bloxstrap_MenuMutex mutex exists, aborting menu launch...");
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// no mutex exists, continue to opening preferences menu
|
|
||||||
mutex = new(true, "Bloxstrap_MenuMutex");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utilities.GetProcessCount(ProjectName) > 1)
|
|
||||||
ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information);
|
|
||||||
|
|
||||||
new MainWindow().ShowDialog();
|
|
||||||
FastFlags.Save();
|
|
||||||
}
|
}
|
||||||
else if (LaunchArgs.Length > 0)
|
catch
|
||||||
{
|
{
|
||||||
if (LaunchArgs[0].StartsWith("roblox-player:"))
|
// no mutex exists, continue to opening preferences menu
|
||||||
{
|
mutex = new(true, "Bloxstrap_MenuMutex");
|
||||||
commandLine = Protocol.ParseUri(LaunchArgs[0]);
|
}
|
||||||
}
|
|
||||||
else if (LaunchArgs[0].StartsWith("roblox:"))
|
if (Utilities.GetProcessCount(ProjectName) > 1)
|
||||||
{
|
ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information);
|
||||||
commandLine = $"--app --deeplink {LaunchArgs[0]}";
|
|
||||||
}
|
new MainWindow().ShowDialog();
|
||||||
else
|
FastFlags.Save();
|
||||||
{
|
}
|
||||||
commandLine = "--app";
|
else if (LaunchArgs.Length > 0)
|
||||||
}
|
{
|
||||||
|
if (LaunchArgs[0].StartsWith("roblox-player:"))
|
||||||
|
{
|
||||||
|
commandLine = Protocol.ParseUri(LaunchArgs[0]);
|
||||||
|
}
|
||||||
|
else if (LaunchArgs[0].StartsWith("roblox:"))
|
||||||
|
{
|
||||||
|
commandLine = $"--app --deeplink {LaunchArgs[0]}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
commandLine = "--app";
|
commandLine = "--app";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(commandLine))
|
|
||||||
{
|
|
||||||
if (!IsFirstRun)
|
|
||||||
ShouldSaveConfigs = true;
|
|
||||||
|
|
||||||
DeployManager.SetChannel(Settings.Prop.Channel);
|
|
||||||
|
|
||||||
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper");
|
|
||||||
Bootstrapper bootstrapper = new(commandLine);
|
|
||||||
IBootstrapperDialog? dialog = null;
|
|
||||||
|
|
||||||
if (!IsQuiet)
|
|
||||||
{
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog");
|
|
||||||
dialog = Settings.Prop.BootstrapperStyle.GetNew();
|
|
||||||
bootstrapper.Dialog = dialog;
|
|
||||||
dialog.Bootstrapper = bootstrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle roblox singleton mutex for multi-instance launching
|
|
||||||
// note we're handling it here in the main thread and NOT in the
|
|
||||||
// bootstrapper as handling mutexes in async contexts suuuuuucks
|
|
||||||
|
|
||||||
Mutex? singletonMutex = null;
|
|
||||||
|
|
||||||
if (Settings.Prop.MultiInstanceLaunching)
|
|
||||||
{
|
|
||||||
Logger.WriteLine("[App::OnStartup] Creating singleton mutex");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Mutex.OpenExisting("ROBLOX_singletonMutex");
|
|
||||||
Logger.WriteLine("[App::OnStartup] Warning - singleton mutex already exists!");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// create the singleton mutex before the game client does
|
|
||||||
singletonMutex = new Mutex(true, "ROBLOX_singletonMutex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// there's a bug here that i have yet to fix!
|
|
||||||
// sometimes the task just terminates when the bootstrapper hasn't
|
|
||||||
// actually finished, causing the bootstrapper to hang indefinitely
|
|
||||||
// i have no idea how the fuck this happens, but it happens like VERY
|
|
||||||
// rarely so i'm not too concerned by it
|
|
||||||
// maybe one day ill find out why it happens
|
|
||||||
Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t =>
|
|
||||||
{
|
|
||||||
Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished");
|
|
||||||
|
|
||||||
if (t.IsFaulted)
|
|
||||||
Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper");
|
|
||||||
|
|
||||||
if (t.Exception is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Logger.WriteLine($"[App::OnStartup] {t.Exception}");
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
throw t.Exception;
|
|
||||||
#else
|
|
||||||
var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception;
|
|
||||||
dialog?.ShowError($"{exception.GetType()}: {exception.Message}");
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog?.ShowBootstrapper();
|
|
||||||
bootstrapperTask.Wait();
|
|
||||||
|
|
||||||
if (singletonMutex is not null)
|
|
||||||
{
|
|
||||||
Logger.WriteLine($"[App::OnStartup] We have singleton mutex ownership! Running in background until all Roblox processes are closed");
|
|
||||||
|
|
||||||
// we've got ownership of the roblox singleton mutex!
|
|
||||||
// if we stop running, everything will screw up once any more roblox instances launched
|
|
||||||
while (Utilities.GetProcessCount("RobloxPlayerBeta", false) != 0)
|
|
||||||
{
|
|
||||||
Thread.Sleep(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
else
|
||||||
{
|
{
|
||||||
Logger.WriteLine("[App::OnStartup] An exception occurred when running the main thread");
|
commandLine = "--app";
|
||||||
Logger.WriteLine($"[App::OnStartup] {ex}");
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(commandLine))
|
||||||
|
{
|
||||||
|
if (!IsFirstRun)
|
||||||
|
ShouldSaveConfigs = true;
|
||||||
|
|
||||||
|
DeployManager.SetChannel(Settings.Prop.Channel);
|
||||||
|
|
||||||
|
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
||||||
|
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper");
|
||||||
|
Bootstrapper bootstrapper = new(commandLine);
|
||||||
|
IBootstrapperDialog? dialog = null;
|
||||||
|
|
||||||
if (!IsQuiet)
|
if (!IsQuiet)
|
||||||
Settings.Prop.BootstrapperStyle.GetNew().ShowError($"{ex.GetType()}: {ex.Message}");
|
{
|
||||||
}
|
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog");
|
||||||
|
dialog = Settings.Prop.BootstrapperStyle.GetNew();
|
||||||
|
bootstrapper.Dialog = dialog;
|
||||||
|
dialog.Bootstrapper = bootstrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle roblox singleton mutex for multi-instance launching
|
||||||
|
// note we're handling it here in the main thread and NOT in the
|
||||||
|
// bootstrapper as handling mutexes in async contexts suuuuuucks
|
||||||
|
|
||||||
|
Mutex? singletonMutex = null;
|
||||||
|
|
||||||
|
if (Settings.Prop.MultiInstanceLaunching)
|
||||||
|
{
|
||||||
|
Logger.WriteLine("[App::OnStartup] Creating singleton mutex");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Mutex.OpenExisting("ROBLOX_singletonMutex");
|
||||||
|
Logger.WriteLine("[App::OnStartup] Warning - singleton mutex already exists!");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// create the singleton mutex before the game client does
|
||||||
|
singletonMutex = new Mutex(true, "ROBLOX_singletonMutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's a bug here that i have yet to fix!
|
||||||
|
// sometimes the task just terminates when the bootstrapper hasn't
|
||||||
|
// actually finished, causing the bootstrapper to hang indefinitely
|
||||||
|
// i have no idea how the fuck this happens, but it happens like VERY
|
||||||
|
// rarely so i'm not too concerned by it
|
||||||
|
// maybe one day ill find out why it happens
|
||||||
|
Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t =>
|
||||||
|
{
|
||||||
|
Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished");
|
||||||
|
|
||||||
|
if (t.IsFaulted)
|
||||||
|
Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper");
|
||||||
|
|
||||||
|
if (t.Exception is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Logger.WriteLine($"[App::OnStartup] {t.Exception}");
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
throw t.Exception;
|
||||||
|
#else
|
||||||
|
var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception;
|
||||||
|
dialog?.ShowError($"{exception.GetType()}: {exception.Message}");
|
||||||
|
Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||||
#endif
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog?.ShowBootstrapper();
|
||||||
|
bootstrapperTask.Wait();
|
||||||
|
|
||||||
|
if (singletonMutex is not null)
|
||||||
|
{
|
||||||
|
Logger.WriteLine($"[App::OnStartup] We have singleton mutex ownership! Running in background until all Roblox processes are closed");
|
||||||
|
|
||||||
|
// we've got ownership of the roblox singleton mutex!
|
||||||
|
// if we stop running, everything will screw up once any more roblox instances launched
|
||||||
|
while (Utilities.GetProcessCount("RobloxPlayerBeta", false) != 0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Successfully reached end of main thread. Terminating...");
|
Logger.WriteLine($"[App::OnStartup] Successfully reached end of main thread. Terminating...");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user