mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Auto-update - Don't check for updates until after core init finished
Tray - Attempt to more reliably hide icon on application close Auto-run - Implemented dropping of permissions
This commit is contained in:
parent
097a5275e2
commit
ca7af1d142
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@ -7,10 +8,11 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public class RestartEventArgs : EventArgs
|
||||
{
|
||||
internal RestartEventArgs(bool elevate, TimeSpan delay)
|
||||
internal RestartEventArgs(bool elevate, TimeSpan delay, List<string>? extraArgs)
|
||||
{
|
||||
Elevate = elevate;
|
||||
Delay = delay;
|
||||
ExtraArgs = extraArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -22,5 +24,10 @@ namespace Artemis.Core
|
||||
/// Gets the delay before killing process and restarting
|
||||
/// </summary>
|
||||
public TimeSpan Delay { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of extra arguments to pass to Artemis when restarting
|
||||
/// </summary>
|
||||
public List<string>? ExtraArgs { get; }
|
||||
}
|
||||
}
|
||||
@ -63,6 +63,7 @@ namespace Artemis.Core.Services
|
||||
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
|
||||
_renderScale = settingsService.GetSetting("Core.RenderScale", 0.5);
|
||||
_frameStopWatch = new Stopwatch();
|
||||
StartupArguments = new List<string>();
|
||||
|
||||
UpdatePluginCache();
|
||||
|
||||
@ -77,7 +78,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
public bool ModuleRenderingDisabled { get; set; }
|
||||
public List<string>? StartupArguments { get; set; }
|
||||
public List<string> StartupArguments { get; set; }
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
@ -109,12 +110,16 @@ namespace Artemis.Core.Services
|
||||
// Just this line should prevent a certain someone from removing HidSharp as an unused dependency as well
|
||||
Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
|
||||
_logger.Debug("Forcing plugins to use HidSharp {hidSharpVersion}", hidSharpVersion);
|
||||
|
||||
|
||||
DeserializationLogger.Initialize(Kernel);
|
||||
|
||||
// Initialize the services
|
||||
_pluginManagementService.CopyBuiltInPlugins();
|
||||
_pluginManagementService.LoadPlugins(StartupArguments != null && StartupArguments.Contains("--ignore-plugin-lock"), IsElevated);
|
||||
_pluginManagementService.LoadPlugins(
|
||||
StartupArguments.Contains("--ignore-plugin-lock"),
|
||||
StartupArguments.Contains("--force-elevation"),
|
||||
IsElevated
|
||||
);
|
||||
|
||||
ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
|
||||
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
|
||||
|
||||
@ -26,7 +26,7 @@ namespace Artemis.Core.Services
|
||||
/// <summary>
|
||||
/// Gets or sets a list of startup arguments
|
||||
/// </summary>
|
||||
List<string>? StartupArguments { get; set; }
|
||||
List<string> StartupArguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)
|
||||
|
||||
@ -26,7 +26,7 @@ namespace Artemis.Core.Services
|
||||
/// <summary>
|
||||
/// Loads all installed plugins. If plugins already loaded this will reload them all
|
||||
/// </summary>
|
||||
void LoadPlugins(bool ignorePluginLock, bool isElevated);
|
||||
void LoadPlugins(bool ignorePluginLock, bool stayElevated, bool isElevated);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads all installed plugins.
|
||||
|
||||
@ -172,7 +172,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
#region Plugins
|
||||
|
||||
public void LoadPlugins(bool ignorePluginLock, bool isElevated)
|
||||
public void LoadPlugins(bool ignorePluginLock, bool stayElevated, bool isElevated)
|
||||
{
|
||||
if (LoadingPlugins)
|
||||
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
|
||||
@ -208,9 +208,10 @@ namespace Artemis.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
if (isElevated && !adminRequired)
|
||||
if (isElevated && !adminRequired && !stayElevated)
|
||||
{
|
||||
// No need for a delay this early on, nothing that needs graceful shutdown is happening yet
|
||||
_logger.Information("Restarting because no plugin requires elevation and --force-elevation was not supplied");
|
||||
Utilities.Restart(false, TimeSpan.Zero);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
|
||||
@ -38,9 +40,10 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
/// <param name="elevate">Whether the application should be restarted with elevated permissions</param>
|
||||
/// <param name="delay">Delay in seconds before killing process and restarting </param>
|
||||
public static void Restart(bool elevate, TimeSpan delay)
|
||||
/// <param name="extraArgs">A list of extra arguments to pass to Artemis when restarting</param>
|
||||
public static void Restart(bool elevate, TimeSpan delay, params string[] extraArgs)
|
||||
{
|
||||
OnRestartRequested(new RestartEventArgs(elevate, delay));
|
||||
OnRestartRequested(new RestartEventArgs(elevate, delay, extraArgs.ToList()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
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;
|
||||
@ -20,7 +22,6 @@ using Artemis.UI.Stylet;
|
||||
using Artemis.UI.Utilities;
|
||||
using Ninject;
|
||||
using Serilog;
|
||||
using SharpVectors.Dom.Resources;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI
|
||||
@ -40,6 +41,7 @@ namespace Artemis.UI
|
||||
|
||||
protected override void Launch()
|
||||
{
|
||||
// TODO: Move shutdown code out of bootstrapper
|
||||
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
|
||||
Core.Utilities.PrepareFirstLaunch();
|
||||
@ -139,25 +141,47 @@ namespace Artemis.UI
|
||||
|
||||
private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e)
|
||||
{
|
||||
string args = Args.Any() ? "-ArgumentList " + string.Join(',', Args.Select(a => "'" + a + "'")) : "";
|
||||
|
||||
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 \"& {{Start-Sleep -Milliseconds {(int) e.Delay.TotalMilliseconds}; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '{Constants.ExecutablePath}' {args} -Verb RunAs}}\"",
|
||||
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
|
||||
{
|
||||
ProcessUtilities.RunAsDesktopUser(Constants.ExecutablePath);
|
||||
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());
|
||||
}
|
||||
|
||||
@ -176,5 +200,8 @@ namespace Artemis.UI
|
||||
Environment.Exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Artemis.UI": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--test --test2"
|
||||
"commandLineArgs": "--force-elevation"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,9 +39,30 @@
|
||||
<TextBlock>Stay on top</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Content="Force garbage collection" Command="{s:Action ForceGarbageCollection}" />
|
||||
<Button Content="Elevate permissions" Command="{s:Action Elevate}" />
|
||||
<Button Content="Restart" Command="{s:Action Restart}" />
|
||||
<Button Command="{s:Action ForceGarbageCollection}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="DeleteEmpty"/>
|
||||
<TextBlock Margin="10 0">Force garbage collection</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Elevate}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull" />
|
||||
<TextBlock Margin="10 0">Elevate permissions</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Drop}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="ChevronDoubleDown" />
|
||||
<TextBlock Margin="10 0">Drop permissions</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Restart}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="Restart" />
|
||||
<TextBlock Margin="10 0">Restart</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</mde:AppBar>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Settings.Debug.Tabs;
|
||||
@ -29,6 +30,8 @@ namespace Artemis.UI.Screens.Settings.Debug
|
||||
public PluginSetting<bool> StayOnTopSetting { get; }
|
||||
|
||||
public string Title => "Debugger";
|
||||
public bool CanElevate => !_coreService.IsElevated;
|
||||
public bool CanDrop => _coreService.IsElevated;
|
||||
|
||||
public void ToggleStayOnTop()
|
||||
{
|
||||
@ -50,7 +53,12 @@ namespace Artemis.UI.Screens.Settings.Debug
|
||||
|
||||
public void Elevate()
|
||||
{
|
||||
Core.Utilities.Restart(true, TimeSpan.FromMilliseconds(500));
|
||||
Core.Utilities.Restart(true, TimeSpan.FromMilliseconds(500), "--force-elevation");
|
||||
}
|
||||
|
||||
public void Drop()
|
||||
{
|
||||
Core.Utilities.Restart(false, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
|
||||
@ -46,6 +46,9 @@ namespace Artemis.UI.Screens
|
||||
_debugService = debugService;
|
||||
CanShowRootViewModel = true;
|
||||
|
||||
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
||||
Core.Utilities.RestartRequested += UtilitiesOnShutdownRequested;
|
||||
|
||||
windowService.ConfigureMainWindowProvider(this);
|
||||
messageService.ConfigureNotificationProvider(this);
|
||||
bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun");
|
||||
@ -56,7 +59,9 @@ namespace Artemis.UI.Screens
|
||||
coreService.Initialized += (_, _) => TrayBringToForeground();
|
||||
}
|
||||
else
|
||||
updateService.AutoUpdate();
|
||||
{
|
||||
coreService.Initialized += (_, _) => updateService.AutoUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanShowRootViewModel
|
||||
@ -111,12 +116,14 @@ namespace Artemis.UI.Screens
|
||||
public void OnTrayBalloonTipClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (CanShowRootViewModel)
|
||||
{
|
||||
TrayBringToForeground();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrestle the main window to the front
|
||||
Window mainWindow = ((Window) _rootViewModel.View);
|
||||
if (mainWindow.WindowState == WindowState.Minimized)
|
||||
Window mainWindow = (Window) _rootViewModel.View;
|
||||
if (mainWindow.WindowState == WindowState.Minimized)
|
||||
mainWindow.WindowState = WindowState.Normal;
|
||||
mainWindow.Activate();
|
||||
mainWindow.Topmost = true;
|
||||
@ -125,6 +132,11 @@ namespace Artemis.UI.Screens
|
||||
}
|
||||
}
|
||||
|
||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||
{
|
||||
Execute.OnUIThread(() => _taskBarIcon?.Dispose());
|
||||
}
|
||||
|
||||
private void ShowSplashScreen()
|
||||
{
|
||||
Execute.OnUIThread(() =>
|
||||
@ -156,7 +168,7 @@ namespace Artemis.UI.Screens
|
||||
|
||||
PackIcon packIcon = new() {Kind = icon};
|
||||
Geometry geometry = Geometry.Parse(packIcon.Data);
|
||||
|
||||
|
||||
// Scale the icon up to fit a 256x256 image and draw it
|
||||
geometry = Geometry.Combine(geometry, Geometry.Empty, GeometryCombineMode.Union, new ScaleTransform(256 / geometry.Bounds.Right, 256 / geometry.Bounds.Bottom));
|
||||
drawingContext.DrawGeometry(new SolidColorBrush(Colors.White), null, geometry);
|
||||
|
||||
@ -29,7 +29,7 @@ namespace Artemis.UI.Utilities
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public static void RunAsDesktopUser(string fileName)
|
||||
public static Process RunAsDesktopUser(string fileName, string arguments, bool hideWindow)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
|
||||
@ -49,7 +49,7 @@ namespace Artemis.UI.Utilities
|
||||
{
|
||||
IntPtr process = GetCurrentProcess();
|
||||
if (!OpenProcessToken(process, 0x0020, ref hProcessToken))
|
||||
return;
|
||||
return null;
|
||||
|
||||
TOKEN_PRIVILEGES tkp = new()
|
||||
{
|
||||
@ -58,12 +58,12 @@ namespace Artemis.UI.Utilities
|
||||
};
|
||||
|
||||
if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
|
||||
return;
|
||||
return null;
|
||||
|
||||
tkp.Privileges[0].Attributes = 0x00000002;
|
||||
|
||||
if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -76,7 +76,7 @@ namespace Artemis.UI.Utilities
|
||||
// restarted elevated.
|
||||
IntPtr hwnd = GetShellWindow();
|
||||
if (hwnd == IntPtr.Zero)
|
||||
return;
|
||||
return null;
|
||||
|
||||
IntPtr hShellProcess = IntPtr.Zero;
|
||||
IntPtr hShellProcessToken = IntPtr.Zero;
|
||||
@ -86,29 +86,41 @@ namespace Artemis.UI.Utilities
|
||||
// Get the PID of the desktop shell process.
|
||||
uint dwPID;
|
||||
if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)
|
||||
return;
|
||||
return null;
|
||||
|
||||
// Open the desktop shell process in order to query it (get the token)
|
||||
hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);
|
||||
if (hShellProcess == IntPtr.Zero)
|
||||
return;
|
||||
return null;
|
||||
|
||||
// Get the process token of the desktop shell.
|
||||
if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
|
||||
return;
|
||||
return null;
|
||||
|
||||
uint dwTokenRights = 395U;
|
||||
|
||||
// Duplicate the shell's process token to get a primary token.
|
||||
// Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
|
||||
if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))
|
||||
return;
|
||||
return null;
|
||||
|
||||
// Start the target process with the new token.
|
||||
STARTUPINFO si = new();
|
||||
if (hideWindow)
|
||||
{
|
||||
si.dwFlags = 0x00000001;
|
||||
si.wShowWindow = 0;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION pi = new();
|
||||
if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
|
||||
return;
|
||||
if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, $"\"{fileName}\" {arguments}", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))
|
||||
{
|
||||
// Get the last error and display it.
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
return null;
|
||||
}
|
||||
|
||||
return Process.GetProcessById(pi.dwProcessId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -186,24 +198,24 @@ namespace Artemis.UI.Utilities
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
public readonly int cb;
|
||||
public readonly string lpReserved;
|
||||
public readonly string lpDesktop;
|
||||
public readonly string lpTitle;
|
||||
public readonly int dwX;
|
||||
public readonly int dwY;
|
||||
public readonly int dwXSize;
|
||||
public readonly int dwYSize;
|
||||
public readonly int dwXCountChars;
|
||||
public readonly int dwYCountChars;
|
||||
public readonly int dwFillAttribute;
|
||||
public readonly int dwFlags;
|
||||
public readonly short wShowWindow;
|
||||
public readonly short cbReserved2;
|
||||
public readonly IntPtr lpReserved2;
|
||||
public readonly IntPtr hStdInput;
|
||||
public readonly IntPtr hStdOutput;
|
||||
public readonly IntPtr hStdError;
|
||||
public int cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public int dwX;
|
||||
public int dwY;
|
||||
public int dwXSize;
|
||||
public int dwYSize;
|
||||
public int dwXCountChars;
|
||||
public int dwYCountChars;
|
||||
public int dwFillAttribute;
|
||||
public int dwFlags;
|
||||
public short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", ExactSpelling = true)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user