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:
commit
7d7a985d35
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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">
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
src/Artemis.UI/Properties/Resources.Designer.cs
generated
24
src/Artemis.UI/Properties/Resources.Designer.cs
generated
@ -190,6 +190,30 @@ namespace Artemis.UI.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-16"?>
|
||||||
|
///<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||||
|
/// <RegistrationInfo>
|
||||||
|
/// <Date>2021-01-24T15:05:33.7954017</Date>
|
||||||
|
/// <Author>DESKTOP-8CH3TD6\Robert</Author>
|
||||||
|
/// <URI>\Artemis 2 - Autorun</URI>
|
||||||
|
/// </RegistrationInfo>
|
||||||
|
/// <Triggers>
|
||||||
|
/// <LogonTrigger>
|
||||||
|
/// <Enabled>true</Enabled>
|
||||||
|
/// <Delay>PT15S</Delay>
|
||||||
|
/// </LogonTrigger>
|
||||||
|
/// </Triggers>
|
||||||
|
/// <Principals>
|
||||||
|
/// <Principal id="Author">
|
||||||
|
/// <UserId>S-1-5-21-184080501-1733858 [rest of string was truncated]";.
|
||||||
|
/// </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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
2
src/Artemis.UI/Properties/Settings.Designer.cs
generated
2
src/Artemis.UI/Properties/Settings.Designer.cs
generated
@ -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())));
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"Artemis.UI": {
|
"Artemis.UI": {
|
||||||
"commandName": "Project"
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "--force-elevation"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
src/Artemis.UI/Resources/Artemis 2 - Autorun.xml
Normal file
BIN
src/Artemis.UI/Resources/Artemis 2 - Autorun.xml
Normal file
Binary file not shown.
@ -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>
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
257
src/Artemis.UI/Utilities/ProcessUtilities.cs
Normal file
257
src/Artemis.UI/Utilities/ProcessUtilities.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user