mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Startup - Bring existing instances to foreground/focus them
Bootstrapper - Cleaned up code into a separate state manager
This commit is contained in:
parent
28e1532064
commit
5c2a96eee0
127
src/Artemis.UI/ApplicationStateManager.cs
Normal file
127
src/Artemis.UI/ApplicationStateManager.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Utilities;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI
|
||||
{
|
||||
public class ApplicationStateManager
|
||||
{
|
||||
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
||||
private Mutex _artemisMutex;
|
||||
|
||||
public ApplicationStateManager(string[] startupArguments)
|
||||
{
|
||||
StartupArguments = startupArguments;
|
||||
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
|
||||
}
|
||||
|
||||
public string[] StartupArguments { get; }
|
||||
public bool IsElevated { get; }
|
||||
|
||||
public bool FocusExistingInstance()
|
||||
{
|
||||
_artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535d", out bool createdNew);
|
||||
if (createdNew)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Blocking is required here otherwise Artemis shuts down before the remote call gets a chance to finish
|
||||
RemoteFocus().GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Not much could go wrong here but this code runs so early it'll crash if something does go wrong
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task RemoteFocus()
|
||||
{
|
||||
// At this point we cannot read the database yet to retrieve the web server port.
|
||||
// Instead use the method external applications should use as well.
|
||||
if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt")))
|
||||
return;
|
||||
|
||||
string url = await File.ReadAllTextAsync(Path.Combine(Constants.DataFolder, "webserver.txt"));
|
||||
using HttpClient client = new();
|
||||
await client.PostAsync(url + "api/remote/bring-to-foreground", null!);
|
||||
}
|
||||
|
||||
private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e)
|
||||
{
|
||||
List<string> argsList = new();
|
||||
argsList.AddRange(StartupArguments);
|
||||
if (e.ExtraArgs != null)
|
||||
argsList.AddRange(e.ExtraArgs.Except(argsList));
|
||||
string args = argsList.Any() ? "-ArgumentList " + string.Join(',', argsList) : "";
|
||||
string command =
|
||||
$"-Command \"& {{Start-Sleep -Milliseconds {(int) e.Delay.TotalMilliseconds}; " +
|
||||
"(Get-Process 'Artemis.UI').kill(); " +
|
||||
$"Start-Process -FilePath '{Constants.ExecutablePath}' -WorkingDirectory '{Constants.ApplicationFolder}' {args}}}\"";
|
||||
// Elevated always runs with RunAs
|
||||
if (e.Elevate)
|
||||
{
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = command.Replace("}\"", " -Verb RunAs}\""),
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
}
|
||||
// Non-elevated runs regularly if currently not elevated
|
||||
else if (!IsElevated)
|
||||
{
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = command,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
}
|
||||
// Non-elevated runs via a utility method is currently elevated (de-elevating is hacky)
|
||||
else
|
||||
{
|
||||
string powerShell = Path.Combine(Environment.SystemDirectory, "WindowsPowerShell", "v1.0", "powershell.exe");
|
||||
ProcessUtilities.RunAsDesktopUser(powerShell, command, true);
|
||||
}
|
||||
|
||||
// Lets try a graceful shutdown, PowerShell will kill if needed
|
||||
Execute.OnUIThread(() => Application.Current.Shutdown());
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object sender, EventArgs e)
|
||||
{
|
||||
// Use PowerShell to kill the process after 2 sec just in case
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = "-Command \"& {Start-Sleep -s 2; (Get-Process 'Artemis.UI').kill()}",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
|
||||
Execute.OnUIThread(() => Application.Current.Shutdown());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Threading;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject;
|
||||
@ -19,7 +14,6 @@ using Artemis.UI.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Stylet;
|
||||
using Artemis.UI.Utilities;
|
||||
using Ninject;
|
||||
using Serilog;
|
||||
using Stylet;
|
||||
@ -28,6 +22,7 @@ namespace Artemis.UI
|
||||
{
|
||||
public class Bootstrapper : NinjectBootstrapper<TrayViewModel>
|
||||
{
|
||||
private ApplicationStateManager _applicationStateManager;
|
||||
private ICoreService _core;
|
||||
public static List<string> StartupArguments { get; private set; }
|
||||
|
||||
@ -41,14 +36,18 @@ namespace Artemis.UI
|
||||
|
||||
protected override void Launch()
|
||||
{
|
||||
// TODO: Move shutdown code out of bootstrapper
|
||||
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
|
||||
_applicationStateManager = new ApplicationStateManager(Args);
|
||||
Core.Utilities.PrepareFirstLaunch();
|
||||
|
||||
ILogger logger = Kernel.Get<ILogger>();
|
||||
IViewManager viewManager = Kernel.Get<IViewManager>();
|
||||
if (_applicationStateManager.FocusExistingInstance())
|
||||
{
|
||||
logger.Information("Shutting down because a different instance is already running.");
|
||||
Application.Current.Shutdown(1);
|
||||
return;
|
||||
}
|
||||
|
||||
IViewManager viewManager = Kernel.Get<IViewManager>();
|
||||
StartupArguments = Args.ToList();
|
||||
|
||||
// Create the Artemis core
|
||||
@ -78,7 +77,7 @@ namespace Artemis.UI
|
||||
try
|
||||
{
|
||||
_core.StartupArguments = StartupArguments;
|
||||
_core.IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
_core.IsElevated = _applicationStateManager.IsElevated;
|
||||
_core.Initialize();
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -125,67 +124,6 @@ namespace Artemis.UI
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object sender, EventArgs e)
|
||||
{
|
||||
// Use PowerShell to kill the process after 2 sec just in case
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = "-Command \"& {Start-Sleep -s 2; (Get-Process 'Artemis.UI').kill()}",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
|
||||
Execute.OnUIThread(() => Application.Current.Shutdown());
|
||||
}
|
||||
|
||||
private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e)
|
||||
{
|
||||
List<string> argsList = new();
|
||||
argsList.AddRange(Args);
|
||||
if (e.ExtraArgs != null)
|
||||
argsList.AddRange(e.ExtraArgs.Except(argsList));
|
||||
string args = argsList.Any() ? "-ArgumentList " + string.Join(',', argsList) : "";
|
||||
string command =
|
||||
$"-Command \"& {{Start-Sleep -Milliseconds {(int) e.Delay.TotalMilliseconds}; " +
|
||||
$"(Get-Process 'Artemis.UI').kill(); " +
|
||||
$"Start-Process -FilePath '{Constants.ExecutablePath}' -WorkingDirectory '{Constants.ApplicationFolder}' {args}}}\"";
|
||||
// Elevated always runs with RunAs
|
||||
if (e.Elevate)
|
||||
{
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = command.Replace("}\"", " -Verb RunAs}\""),
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
}
|
||||
// Non-elevated runs regularly if currently not elevated
|
||||
else if (!_core.IsElevated)
|
||||
{
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
Arguments = command,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerShell.exe"
|
||||
};
|
||||
Process.Start(info);
|
||||
}
|
||||
// Non-elevated runs via a utility method is currently elevated (de-elevating is hacky)
|
||||
else
|
||||
{
|
||||
string powerShell = Path.Combine(Environment.SystemDirectory, "WindowsPowerShell", "v1.0", "powershell.exe");
|
||||
ProcessUtilities.RunAsDesktopUser(powerShell, command, true);
|
||||
}
|
||||
|
||||
// Lets try a graceful shutdown, PowerShell will kill if needed
|
||||
Execute.OnUIThread(() => Application.Current.Shutdown());
|
||||
}
|
||||
|
||||
private void HandleFatalException(Exception e, ILogger logger)
|
||||
{
|
||||
logger.Fatal(e, "Fatal exception during initialization, shutting down.");
|
||||
@ -201,8 +139,5 @@ namespace Artemis.UI
|
||||
Environment.Exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user