1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge branch 'feature/auto-run-task'

This commit is contained in:
Robert 2021-01-25 20:21:29 +01:00
commit 7d7a985d35
18 changed files with 538 additions and 61 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -7,10 +8,11 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class RestartEventArgs : EventArgs public class RestartEventArgs : EventArgs
{ {
internal RestartEventArgs(bool elevate, TimeSpan delay) internal RestartEventArgs(bool elevate, TimeSpan delay, List<string>? extraArgs)
{ {
Elevate = elevate; Elevate = elevate;
Delay = delay; Delay = delay;
ExtraArgs = extraArgs;
} }
/// <summary> /// <summary>
@ -22,5 +24,10 @@ namespace Artemis.Core
/// Gets the delay before killing process and restarting /// Gets the delay before killing process and restarting
/// </summary> /// </summary>
public TimeSpan Delay { get; } public TimeSpan Delay { get; }
/// <summary>
/// A list of extra arguments to pass to Artemis when restarting
/// </summary>
public List<string>? ExtraArgs { get; }
} }
} }

View File

@ -63,6 +63,7 @@ namespace Artemis.Core.Services
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug); _loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug);
_renderScale = settingsService.GetSetting("Core.RenderScale", 0.5); _renderScale = settingsService.GetSetting("Core.RenderScale", 0.5);
_frameStopWatch = new Stopwatch(); _frameStopWatch = new Stopwatch();
StartupArguments = new List<string>();
UpdatePluginCache(); UpdatePluginCache();
@ -77,7 +78,7 @@ namespace Artemis.Core.Services
public TimeSpan FrameTime { get; private set; } public TimeSpan FrameTime { get; private set; }
public bool ModuleRenderingDisabled { get; set; } public bool ModuleRenderingDisabled { get; set; }
public List<string>? StartupArguments { get; set; } public List<string> StartupArguments { get; set; }
public bool IsElevated { get; set; } public bool IsElevated { get; set; }
public void Dispose() 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 // 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; Version? hidSharpVersion = Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version;
_logger.Debug("Forcing plugins to use HidSharp {hidSharpVersion}", hidSharpVersion); _logger.Debug("Forcing plugins to use HidSharp {hidSharpVersion}", hidSharpVersion);
DeserializationLogger.Initialize(Kernel); DeserializationLogger.Initialize(Kernel);
// Initialize the services // Initialize the services
_pluginManagementService.CopyBuiltInPlugins(); _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; ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId); _logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);

View File

@ -26,7 +26,7 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Gets or sets a list of startup arguments /// Gets or sets a list of startup arguments
/// </summary> /// </summary>
List<string>? StartupArguments { get; set; } List<string> StartupArguments { get; set; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions) /// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions)

View File

@ -26,7 +26,7 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Loads all installed plugins. If plugins already loaded this will reload them all /// Loads all installed plugins. If plugins already loaded this will reload them all
/// </summary> /// </summary>
void LoadPlugins(bool ignorePluginLock, bool isElevated); void LoadPlugins(bool ignorePluginLock, bool stayElevated, bool isElevated);
/// <summary> /// <summary>
/// Unloads all installed plugins. /// Unloads all installed plugins.

View File

@ -172,7 +172,7 @@ namespace Artemis.Core.Services
#region Plugins #region Plugins
public void LoadPlugins(bool ignorePluginLock, bool isElevated) public void LoadPlugins(bool ignorePluginLock, bool stayElevated, bool isElevated)
{ {
if (LoadingPlugins) if (LoadingPlugins)
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet."); throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
@ -198,9 +198,9 @@ namespace Artemis.Core.Services
// ReSharper disable InconsistentlySynchronizedField - It's read-only, idc // ReSharper disable InconsistentlySynchronizedField - It's read-only, idc
_logger.Debug("Loaded {count} plugin(s)", _plugins.Count); _logger.Debug("Loaded {count} plugin(s)", _plugins.Count);
bool mustElevate = !isElevated && _plugins.Any(p => p.Entity.IsEnabled && p.Info.RequiresAdmin); bool adminRequired = _plugins.Any(p => p.Entity.IsEnabled && p.Info.RequiresAdmin);
if (mustElevate) if (!isElevated && adminRequired)
{ {
_logger.Information("Restarting because one or more plugins requires elevation"); _logger.Information("Restarting because one or more plugins requires elevation");
// No need for a delay this early on, nothing that needs graceful shutdown is happening yet // No need for a delay this early on, nothing that needs graceful shutdown is happening yet
@ -208,6 +208,14 @@ namespace Artemis.Core.Services
return; return;
} }
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;
}
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled)) foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
EnablePlugin(plugin, false, ignorePluginLock); EnablePlugin(plugin, false, ignorePluginLock);

View File

@ -1,7 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Linq;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Security.Principal; using System.Security.Principal;
@ -35,13 +36,14 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Restarts the application /// Restarts the application
/// </summary> /// </summary>
/// <param name="elevate">Whether the application should be restarted with elevated permissions</param> /// <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> /// <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> /// <summary>
@ -90,6 +92,11 @@ namespace Artemis.Core
} }
} }
private static void OnRestartRequested(RestartEventArgs e)
{
RestartRequested?.Invoke(null, e);
}
#region Events #region Events
/// <summary> /// <summary>
@ -98,20 +105,15 @@ namespace Artemis.Core
public static event EventHandler? ShutdownRequested; public static event EventHandler? ShutdownRequested;
/// <summary> /// <summary>
/// Occurs when the core has requested an application restart /// Occurs when the core has requested an application restart
/// </summary> /// </summary>
public static event EventHandler<RestartEventArgs>? RestartRequested; public static event EventHandler<RestartEventArgs>? RestartRequested;
private static void OnShutdownRequested() private static void OnShutdownRequested()
{ {
ShutdownRequested?.Invoke(null, EventArgs.Empty); ShutdownRequested?.Invoke(null, EventArgs.Empty);
} }
#endregion #endregion
private static void OnRestartRequested(RestartEventArgs e)
{
RestartRequested?.Invoke(null, e);
}
} }
} }

View File

@ -40,7 +40,7 @@
<Delete Files="$(OutputPath)\libSkiaSharp.dylib" /> <Delete Files="$(OutputPath)\libSkiaSharp.dylib" />
</Target> </Target>
<ItemGroup> <ItemGroup>
<FluentValidationExcludedCultures Include="cs;da;de;es;fa;fi;fr;it;ko;mk;nl;pl;pt;ru;ru-ru;sv;tr;zh-CN;uz-Latn-UZ"> <FluentValidationExcludedCultures Include="ar-DZ;cs;cs-CZ;da;de;es;fa;fi;fr;fr-FR;it;ko;mk;nl;pl;pt;pt-BR;ru;ru-ru;sv;tr;zh-CN;uz-Latn-UZ">
<InProject>false</InProject> <InProject>false</InProject>
</FluentValidationExcludedCultures> </FluentValidationExcludedCultures>
</ItemGroup> </ItemGroup>
@ -318,11 +318,20 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="buildinfo.json"> <None Update="buildinfo.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="DefaultTypes\DataModel\Display\SKColorDataModelDisplayView.xaml"> <Page Update="DefaultTypes\DataModel\Display\SKColorDataModelDisplayView.xaml">

View File

@ -2,7 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@ -17,9 +19,9 @@ using Artemis.UI.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Stylet; using Artemis.UI.Stylet;
using Artemis.UI.Utilities;
using Ninject; using Ninject;
using Serilog; using Serilog;
using SharpVectors.Dom.Resources;
using Stylet; using Stylet;
namespace Artemis.UI namespace Artemis.UI
@ -39,6 +41,7 @@ namespace Artemis.UI
protected override void Launch() protected override void Launch()
{ {
// TODO: Move shutdown code out of bootstrapper
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
Core.Utilities.PrepareFirstLaunch(); Core.Utilities.PrepareFirstLaunch();
@ -70,16 +73,10 @@ namespace Artemis.UI
}); });
// Initialize the core async so the UI can show the progress // Initialize the core async so the UI can show the progress
Task.Run(async () => Task.Run(() =>
{ {
try try
{ {
if (StartupArguments.Contains("--autorun"))
{
logger.Information("Sleeping for 15 seconds on auto run to allow vendor applications required for SDKs to start");
await Task.Delay(TimeSpan.FromSeconds(15));
}
_core.StartupArguments = StartupArguments; _core.StartupArguments = StartupArguments;
_core.IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); _core.IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
_core.Initialize(); _core.Initialize();
@ -144,15 +141,47 @@ namespace Artemis.UI
private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e) private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e)
{ {
ProcessStartInfo info = new() 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)
{ {
Arguments = ProcessStartInfo info = new()
$"-Command \"& {{Start-Sleep -Milliseconds {(int) e.Delay.TotalMilliseconds}; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '{Constants.ExecutablePath}' {(e.Elevate ? "-Verb RunAs" : "")}}}\"", {
WindowStyle = ProcessWindowStyle.Hidden, Arguments = command.Replace("}\"", " -Verb RunAs}\""),
CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden,
FileName = "PowerShell.exe" CreateNoWindow = true,
}; FileName = "PowerShell.exe"
Process.Start(info); };
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()); Execute.OnUIThread(() => Application.Current.Shutdown());
} }
@ -171,5 +200,8 @@ namespace Artemis.UI
Environment.Exit(1); Environment.Exit(1);
}); });
} }
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
} }
} }

View File

@ -190,6 +190,30 @@ namespace Artemis.UI.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-16&quot;?&gt;
///&lt;Task version=&quot;1.2&quot; xmlns=&quot;http://schemas.microsoft.com/windows/2004/02/mit/task&quot;&gt;
/// &lt;RegistrationInfo&gt;
/// &lt;Date&gt;2021-01-24T15:05:33.7954017&lt;/Date&gt;
/// &lt;Author&gt;DESKTOP-8CH3TD6\Robert&lt;/Author&gt;
/// &lt;URI&gt;\Artemis 2 - Autorun&lt;/URI&gt;
/// &lt;/RegistrationInfo&gt;
/// &lt;Triggers&gt;
/// &lt;LogonTrigger&gt;
/// &lt;Enabled&gt;true&lt;/Enabled&gt;
/// &lt;Delay&gt;PT15S&lt;/Delay&gt;
/// &lt;/LogonTrigger&gt;
/// &lt;/Triggers&gt;
/// &lt;Principals&gt;
/// &lt;Principal id=&quot;Author&quot;&gt;
/// &lt;UserId&gt;S-1-5-21-184080501-1733858 [rest of string was truncated]&quot;;.
/// </summary>
internal static string artemis_autorun {
get {
return ResourceManager.GetString("artemis-autorun", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>

View File

@ -112,12 +112,12 @@
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader"> <resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="aero_crosshair" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="aero_crosshair" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\cursors\aero_crosshair.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\resources\cursors\aero_crosshair.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
@ -157,6 +157,9 @@
<data name="aero_rotate_tr" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="aero_rotate_tr" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\cursors\aero_rotate_tr.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\resources\cursors\aero_rotate_tr.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="artemis-autorun" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Artemis 2 - Autorun.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
</data>
<data name="bow" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="bow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\images\logo\bow.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\resources\images\logo\bow.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>

View File

@ -12,7 +12,7 @@ namespace Artemis.UI.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

View File

@ -1,7 +1,8 @@
{ {
"profiles": { "profiles": {
"Artemis.UI": { "Artemis.UI": {
"commandName": "Project" "commandName": "Project",
"commandLineArgs": "--force-elevation"
} }
} }
} }

Binary file not shown.

View File

@ -39,9 +39,30 @@
<TextBlock>Stay on top</TextBlock> <TextBlock>Stay on top</TextBlock>
</StackPanel> </StackPanel>
</Button> </Button>
<Button Content="Force garbage collection" Command="{s:Action ForceGarbageCollection}" /> <Button Command="{s:Action ForceGarbageCollection}">
<Button Content="Elevate permissions" Command="{s:Action Elevate}" /> <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<Button Content="Restart" Command="{s:Action Restart}" /> <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> </StackPanel>
</materialDesign:PopupBox> </materialDesign:PopupBox>
</mde:AppBar> </mde:AppBar>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Screens.Settings.Debug.Tabs; using Artemis.UI.Screens.Settings.Debug.Tabs;
@ -8,12 +9,16 @@ namespace Artemis.UI.Screens.Settings.Debug
{ {
public class DebugViewModel : Conductor<Screen>.Collection.OneActive public class DebugViewModel : Conductor<Screen>.Collection.OneActive
{ {
private readonly ICoreService _coreService;
public DebugViewModel( public DebugViewModel(
ISettingsService settingsService, ISettingsService settingsService,
ICoreService coreService,
RenderDebugViewModel renderDebugViewModel, RenderDebugViewModel renderDebugViewModel,
DataModelDebugViewModel dataModelDebugViewModel, DataModelDebugViewModel dataModelDebugViewModel,
LogsDebugViewModel logsDebugViewModel) LogsDebugViewModel logsDebugViewModel)
{ {
_coreService = coreService;
Items.Add(renderDebugViewModel); Items.Add(renderDebugViewModel);
Items.Add(dataModelDebugViewModel); Items.Add(dataModelDebugViewModel);
Items.Add(logsDebugViewModel); Items.Add(logsDebugViewModel);
@ -25,6 +30,8 @@ namespace Artemis.UI.Screens.Settings.Debug
public PluginSetting<bool> StayOnTopSetting { get; } public PluginSetting<bool> StayOnTopSetting { get; }
public string Title => "Debugger"; public string Title => "Debugger";
public bool CanElevate => !_coreService.IsElevated;
public bool CanDrop => _coreService.IsElevated;
public void ToggleStayOnTop() public void ToggleStayOnTop()
{ {
@ -46,12 +53,17 @@ namespace Artemis.UI.Screens.Settings.Debug
public void Elevate() 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() public void Restart()
{ {
Core.Utilities.Restart(false, TimeSpan.FromMilliseconds(500)); Core.Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500));
} }
} }
} }

View File

@ -3,10 +3,15 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
using System.Xml.XPath;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Properties;
using Artemis.UI.Screens.StartupWizard; using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services; using Artemis.UI.Services;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
@ -290,24 +295,103 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
private void ApplyAutorun() private void ApplyAutorun()
{ {
if (!StartWithWindows)
StartMinimized = false;
// Remove the old auto-run method of placing a shortcut in shell:startup
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
if (File.Exists(autoRunFile))
File.Delete(autoRunFile);
// Create or remove the task if necessary
try try
{ {
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk"); Process schtasks = new()
string executableFile = Constants.ExecutablePath; {
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/TN \"Artemis 2 autorun\""
}
};
if (File.Exists(autoRunFile)) schtasks.Start();
File.Delete(autoRunFile); schtasks.WaitForExit();
if (StartWithWindows) bool taskCreated = schtasks.ExitCode == 0;
ShortcutUtilities.Create(autoRunFile, executableFile, "--autorun", new FileInfo(executableFile).DirectoryName, "Artemis", "", "");
else if (StartWithWindows && !taskCreated)
StartMinimized = false; CreateAutoRunTask();
else if (!StartWithWindows && taskCreated)
RemoveAutoRunTask();
} }
catch (Exception e) catch (Exception e)
{ {
_dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e); Execute.PostToUIThread(() => _dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e));
throw; throw;
} }
} }
private void CreateAutoRunTask()
{
XDocument document = XDocument.Parse(Resources.artemis_autorun);
XElement task = document.Descendants().First();
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
.SetValue(DateTime.Now);
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
.SetValue(System.Security.Principal.WindowsIdentity.GetCurrent().Name);
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
.SetValue(System.Security.Principal.WindowsIdentity.GetCurrent().User.Value);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
.SetValue(Constants.ApplicationFolder);
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
.SetValue("\"" + Constants.ExecutablePath + "\"");
string xmlPath = Path.GetTempFileName();
using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
{
document.Save(fileStream);
}
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\""
}
};
schtasks.Start();
schtasks.WaitForExit();
File.Delete(xmlPath);
}
private void RemoveAutoRunTask()
{
Process schtasks = new()
{
StartInfo =
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
Verb = "runas",
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
}
};
schtasks.Start();
schtasks.WaitForExit();
}
} }
public enum ApplicationColorScheme public enum ApplicationColorScheme

View File

@ -46,6 +46,9 @@ namespace Artemis.UI.Screens
_debugService = debugService; _debugService = debugService;
CanShowRootViewModel = true; CanShowRootViewModel = true;
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnShutdownRequested;
windowService.ConfigureMainWindowProvider(this); windowService.ConfigureMainWindowProvider(this);
messageService.ConfigureNotificationProvider(this); messageService.ConfigureNotificationProvider(this);
bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun"); bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun");
@ -56,7 +59,9 @@ namespace Artemis.UI.Screens
coreService.Initialized += (_, _) => TrayBringToForeground(); coreService.Initialized += (_, _) => TrayBringToForeground();
} }
else else
updateService.AutoUpdate(); {
coreService.Initialized += (_, _) => updateService.AutoUpdate();
}
} }
public bool CanShowRootViewModel public bool CanShowRootViewModel
@ -111,12 +116,14 @@ namespace Artemis.UI.Screens
public void OnTrayBalloonTipClicked(object sender, EventArgs e) public void OnTrayBalloonTipClicked(object sender, EventArgs e)
{ {
if (CanShowRootViewModel) if (CanShowRootViewModel)
{
TrayBringToForeground(); TrayBringToForeground();
}
else else
{ {
// Wrestle the main window to the front // Wrestle the main window to the front
Window mainWindow = ((Window) _rootViewModel.View); Window mainWindow = (Window) _rootViewModel.View;
if (mainWindow.WindowState == WindowState.Minimized) if (mainWindow.WindowState == WindowState.Minimized)
mainWindow.WindowState = WindowState.Normal; mainWindow.WindowState = WindowState.Normal;
mainWindow.Activate(); mainWindow.Activate();
mainWindow.Topmost = true; 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() private void ShowSplashScreen()
{ {
Execute.OnUIThread(() => Execute.OnUIThread(() =>
@ -156,7 +168,7 @@ namespace Artemis.UI.Screens
PackIcon packIcon = new() {Kind = icon}; PackIcon packIcon = new() {Kind = icon};
Geometry geometry = Geometry.Parse(packIcon.Data); Geometry geometry = Geometry.Parse(packIcon.Data);
// Scale the icon up to fit a 256x256 image and draw it // 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)); 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); drawingContext.DrawGeometry(new SolidColorBrush(Colors.White), null, geometry);

View File

@ -0,0 +1,257 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Artemis.UI.Utilities
{
public static class ProcessUtilities
{
public static Task<int> RunProcessAsync(string fileName, string arguments)
{
TaskCompletionSource<int> tcs = new();
Process process = new()
{
StartInfo = {FileName = fileName, Arguments = arguments},
EnableRaisingEvents = true
};
process.Exited += (sender, args) =>
{
tcs.SetResult(process.ExitCode);
process.Dispose();
};
process.Start();
return tcs.Task;
}
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));
// To start process as shell user you will need to carry out these steps:
// 1. Enable the SeIncreaseQuotaPrivilege in your current token
// 2. Get an HWND representing the desktop shell (GetShellWindow)
// 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)
// 4. Open that process(OpenProcess)
// 5. Get the access token from that process (OpenProcessToken)
// 6. Make a primary token with that token(DuplicateTokenEx)
// 7. Start the new process with that primary token(CreateProcessWithTokenW)
IntPtr hProcessToken = IntPtr.Zero;
// Enable SeIncreaseQuotaPrivilege in this process. (This won't work if current process is not elevated.)
try
{
IntPtr process = GetCurrentProcess();
if (!OpenProcessToken(process, 0x0020, ref hProcessToken))
return null;
TOKEN_PRIVILEGES tkp = new()
{
PrivilegeCount = 1,
Privileges = new LUID_AND_ATTRIBUTES[1]
};
if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))
return null;
tkp.Privileges[0].Attributes = 0x00000002;
if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))
return null;
}
finally
{
CloseHandle(hProcessToken);
}
// Get an HWND representing the desktop shell.
// CAVEATS: This will fail if the shell is not running (crashed or terminated), or the default shell has been
// replaced with a custom shell. This also won't return what you probably want if Explorer has been terminated and
// restarted elevated.
IntPtr hwnd = GetShellWindow();
if (hwnd == IntPtr.Zero)
return null;
IntPtr hShellProcess = IntPtr.Zero;
IntPtr hShellProcessToken = IntPtr.Zero;
IntPtr hPrimaryToken = IntPtr.Zero;
try
{
// Get the PID of the desktop shell process.
uint dwPID;
if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)
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 null;
// Get the process token of the desktop shell.
if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))
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 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, $"\"{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
{
CloseHandle(hShellProcessToken);
CloseHandle(hPrimaryToken);
CloseHandle(hShellProcess);
}
}
#region Interop
private struct TOKEN_PRIVILEGES
{
public uint PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct LUID
{
public readonly uint LowPart;
public readonly int HighPart;
}
[Flags]
private enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public readonly IntPtr hProcess;
public readonly IntPtr hThread;
public readonly int dwProcessId;
public readonly int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct STARTUPINFO
{
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)]
private static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("user32.dll")]
private static extern IntPtr GetShellWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType,
out IntPtr phNewToken);
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment,
string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
#endregion
}
}