More things implemented

This commit is contained in:
Noraa Junker
2025-11-18 22:01:20 +01:00
parent 6fea4d9c5d
commit 2ddf561f57
8 changed files with 213 additions and 53 deletions

View File

@@ -11,8 +11,56 @@ namespace RunnerV2.Helpers
{
internal static partial class ElevationHelper
{
internal static RestartScheduledMode RestartScheduled { get; set; } = RestartScheduledMode.None;
internal enum RestartScheduledMode
{
None,
RestartElevated,
RestartElevatedWithOpenSettings,
RestartNonElevated,
}
private static bool? _cachedValue;
internal static void RestartIfScheudled()
{
switch (RestartScheduled)
{
case RestartScheduledMode.None:
return;
case RestartScheduledMode.RestartElevated:
RestartAsAdministrator("--restartedElevated");
break;
case RestartScheduledMode.RestartElevatedWithOpenSettings:
RestartAsAdministrator("--restartedElevated --open-settings");
break;
case RestartScheduledMode.RestartNonElevated:
// Todo: restart unelevated
break;
}
}
private static void RestartAsAdministrator(string arguments)
{
ProcessStartInfo processStartInfo = new()
{
Arguments = arguments,
Verb = "runas",
UseShellExecute = true,
FileName = Environment.ProcessPath,
};
try
{
Process.Start(processStartInfo);
}
catch (Exception ex)
{
Console.WriteLine("Failed to restart as administrator: " + ex);
}
}
internal static bool IsProcessElevated(bool useCachedValue = true)
{
if (_cachedValue is not null && useCachedValue)

View File

@@ -112,9 +112,32 @@ namespace RunnerV2.Helpers
{
switch (property.Name)
{
case "action":
_settingsUtils.SaveSettings(property.Value.GetProperty("general").ToString(), string.Empty);
switch (property.Value.GetProperty("general").GetProperty("action_name").GetString())
{
case "restart_elevation":
ElevationHelper.RestartScheduled = ElevationHelper.RestartScheduledMode.RestartElevatedWithOpenSettings;
Runner.Close();
break;
case "request_update_state_date":
// Todo:
break;
}
break;
case "get_all_hotkey_conflicts":
// Todo: Handle hotkey conflict
break;
case "bugreport":
TrayIconManager.ProcessTrayMenuCommand((nuint)TrayIconManager.TrayButton.ReportBug);
break;
case "bug_report_status":
_ipc?.Send($@"{{""bug_report_running:"" {(TrayIconManager.IsBugReportToolRunning ? "true" : "false")}");
break;
case "killrunner":
Runner.Close();
break;
case "general":
_settingsUtils.SaveSettings(property.Value.ToString(), string.Empty);
foreach (IPowerToysModule module in Runner.LoadedModules)
@@ -124,25 +147,38 @@ namespace RunnerV2.Helpers
}
break;
case string s:
_settingsUtils.SaveSettings(property.Value.ToString(), s);
case "powertoys":
foreach (var powertoysSettingsPart in property.Value.EnumerateObject())
{
_settingsUtils.SaveSettings(property.Value.ToString(), powertoysSettingsPart.Name);
if (Runner.LoadedModules.Find(m => m.Name == s) is IPowerToysModule moduleFound)
{
moduleFound.OnSettingsChanged(s, property.Value);
}
else
{
// If no specific module was found, notify all enabled modules
foreach (IPowerToysModule module in Runner.LoadedModules.Where(m => m.Enabled))
if (Runner.LoadedModules.Find(m => m.Name == powertoysSettingsPart.Name) is IPowerToysModule module)
{
module.OnSettingsChanged(s, property.Value);
module.OnSettingsChanged(powertoysSettingsPart.Name, powertoysSettingsPart.Value);
}
else
{
// If no specific module was found, notify all enabled modules
foreach (IPowerToysModule module2 in Runner.LoadedModules.Where(m => m.Enabled))
{
module2.OnSettingsChanged(powertoysSettingsPart.Name, powertoysSettingsPart.Value);
}
}
}
break;
default:
Console.WriteLine($"Unknown message received from Settings: {property.Name}");
break;
}
}
}
public static void CloseSettingsWindow()
{
InteropEvent closeEventWrapper = new(InteropEvent.SettingsTerminate);
closeEventWrapper.Fire();
closeEventWrapper.Dispose();
}
}
}

View File

@@ -44,7 +44,7 @@ namespace RunnerV2.Helpers
Shell_NotifyIcon(NIMDELETE, ref notifyicondata);
}
private enum TrayButton : uint
internal enum TrayButton : uint
{
Settings = 1,
Documentation,
@@ -102,6 +102,8 @@ namespace RunnerV2.Helpers
}
}
internal static bool IsBugReportToolRunning { get; set; }
internal static void ProcessTrayMenuCommand(nuint commandId)
{
switch ((TrayButton)commandId)
@@ -132,9 +134,11 @@ namespace RunnerV2.Helpers
{
bugReportProcess.Dispose();
EnableMenuItem(_trayIconMenu, (uint)TrayButton.ReportBug, 0x00000000);
IsBugReportToolRunning = false;
};
bugReportProcess.Start();
IsBugReportToolRunning = true;
break;
case TrayButton.Close:

View File

@@ -17,9 +17,8 @@ using Settings.UI.Library;
internal sealed class Program
{
private static readonly SettingsUtils _settingsUtils = new();
private static GeneralSettings _generalSettings = _settingsUtils.GetSettings<GeneralSettings>();
public static GeneralSettings GeneralSettings => _generalSettings;
internal static GeneralSettings GeneralSettings => _settingsUtils.GetSettings<GeneralSettings>();
private static void Main(string[] args)
{
@@ -46,13 +45,13 @@ internal sealed class Program
bool isElevated = ElevationHelper.IsProcessElevated();
bool hasDontElevateArgument = args.Contains("--dont-elevate");
bool runElevatedSetting = _generalSettings.RunElevated;
bool runElevatedSetting = GeneralSettings.RunElevated;
bool hasRestartedElevatedArgment = args.Contains("--restartedElevated");
Action afterInitializationAction = () => { };
Version version = Assembly.GetExecutingAssembly().GetName().Version!;
if ($"v{version.Major}.{version.Minor}.{version.Build}" != _settingsUtils.GetSettings<LastVersionRunSettings>(fileName: "last_version_run.json").LastVersion && (!_generalSettings.ShowWhatsNewAfterUpdates || GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue() != GpoRuleConfigured.Disabled))
if ($"v{version.Major}.{version.Minor}.{version.Build}" != _settingsUtils.GetSettings<LastVersionRunSettings>(fileName: "last_version_run.json").LastVersion && (!GeneralSettings.ShowWhatsNewAfterUpdates || GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue() != GpoRuleConfigured.Disabled))
{
afterInitializationAction += () =>
{
@@ -68,6 +67,14 @@ internal sealed class Program
};
}
if (shouldOpenSettings)
{
afterInitializationAction += () =>
{
SettingsHelper.OpenSettingsWindow(additionalArguments: shouldOpenSettingsToSpecificPage ? args.First(s => s.StartsWith("--open-settings=", StringComparison.InvariantCulture)).Replace("--open-settings=", string.Empty, StringComparison.InvariantCulture) : null);
};
}
// Set last version run
_settingsUtils.SaveSettings(new LastVersionRunSettings() { LastVersion = $"v{version.Major}.{version.Minor}.{version.Build}" }.ToJsonString(), fileName: "last_version_run.json");
@@ -80,14 +87,18 @@ internal sealed class Program
case (_, _, false, _):
case (_, true, _, _):
case (false, _, _, true):
_ = Runner.Run(afterInitializationAction);
GeneralSettings tempGeneralSettings = GeneralSettings;
tempGeneralSettings.IsElevated = isElevated;
_settingsUtils.SaveSettings(tempGeneralSettings.ToJsonString());
// Todo: Save settings
_ = Runner.Run(afterInitializationAction);
break;
default:
// Todo: scheudle restart as elevated
throw new NotImplementedException();
ElevationHelper.RestartScheduled = ElevationHelper.RestartScheduledMode.RestartElevated;
break;
}
ElevationHelper.RestartIfScheudled();
}
private static SpecialMode ShouldRunInSpecialMode(string[] args)

View File

@@ -36,7 +36,6 @@ namespace RunnerV2
internal static bool Run(Action afterInitializationAction)
{
// Todo: Start tray icon
TrayIconManager.StartTrayIcon();
FrozenSet<string> modulesToLoad = ["PowerToys.AlwaysOnTopModuleInterface.dll", "WinUI3Apps\\PowerToys.Hosts.dll"];
@@ -78,21 +77,24 @@ namespace RunnerV2
private static void MessageLoop()
{
while (true)
while (GetMessageW(out MSG msg, IntPtr.Zero, 0, 0) != 0)
{
if (GetMessageW(out MSG msg, IntPtr.Zero, 0, 0) != 0)
{
TranslateMessage(ref msg);
DispatchMessageW(ref msg);
TranslateMessage(ref msg);
DispatchMessageW(ref msg);
HandleMessage(msg.HWnd, msg.Message, (nint)msg.WParam, (nint)msg.LParam);
}
HandleMessage(msg.HWnd, msg.Message, (nint)msg.WParam, (nint)msg.LParam);
}
Close();
}
[DoesNotReturn]
internal static void Close()
{
TrayIconManager.StopTrayIcon();
SettingsHelper.CloseSettingsWindow();
ElevationHelper.RestartIfScheudled();
foreach (IPowerToysModule module in _successfullyAddedModules)
{
try
@@ -193,12 +195,21 @@ namespace RunnerV2
}
}
private static bool _handledShortcut;
private static IntPtr HandleMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
case (uint)WindowMessages.HOTKEY:
if (_handledShortcut)
{
_handledShortcut = false;
break;
}
HotkeyManager.ProcessHotkey((nuint)wParam);
_handledShortcut = true;
break;
case (uint)WindowMessages.ICON_NOTIFY:
TrayIconManager.ProcessTrayIconMessage(lParam);
@@ -210,7 +221,7 @@ namespace RunnerV2
TrayIconManager.StartTrayIcon();
break;
case (uint)WindowMessages.DESTROY:
TrayIconManager.StopTrayIcon();
Close();
break;
default:
if (msg == _taskbarCreatedMessage)

View File

@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using static ManagedCommon.NativeMethods;
namespace ManagedCommon
{
public partial class InteropEvent : IDisposable
{
public const string AlwaysOnTopPin = "Local\\AlwaysOnTopPinEvent-892e0aa2-cfa8-4cc4-b196-ddeb32314ce8";
public const string AlwaysOnTopTerminate = "Local\\AlwaysOnTopTerminateEvent-cfdf1eae-791f-4953-8021-2f18f3837eae";
public const string SettingsTerminate = "Local\\PowerToysRunnerTerminateSettingsEvent-c34cb661-2e69-4613-a1f8-4e39c25d7ef6";
private IntPtr _eventHandle;
public InteropEvent(string eventName)
{
_eventHandle = CreateEventW(IntPtr.Zero, false, false, eventName);
}
public void Fire()
{
if (_eventHandle != IntPtr.Zero)
{
SetEvent(_eventHandle);
}
}
~InteropEvent()
{
Dispose();
}
public void Dispose()
{
if (_eventHandle != IntPtr.Zero)
{
CloseHandle(_eventHandle);
_eventHandle = IntPtr.Zero;
}
GC.SuppressFinalize(this);
}
}
}

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
namespace ManagedCommon
{
internal static class NativeMethods
internal static partial class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
@@ -21,6 +21,13 @@ namespace ManagedCommon
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, System.Text.StringBuilder lpExeName, ref uint lpdwSize);
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
internal static partial IntPtr CreateEventW(IntPtr lpEventAttributes, [MarshalAs(UnmanagedType.Bool)] bool bManualReset, [MarshalAs(UnmanagedType.Bool)] bool bInitialState, string lpName);
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);

View File

@@ -12,9 +12,9 @@ using PowerToys.GPOWrapper;
namespace AlwaysOnTopModuleInterface
{
public class ModuleInterface : IPowerToysModule
public partial class ModuleInterface : IPowerToysModule, IDisposable
{
public bool Enabled => true;
public bool Enabled => new SettingsUtils().GetSettings<GeneralSettings>().Enabled.AlwaysOnTop;
public string Name => "AlwaysOnTop";
@@ -22,24 +22,22 @@ namespace AlwaysOnTopModuleInterface
private Process? _process;
private IntPtr pinEvent = CreateEventW(IntPtr.Zero, false, false, "Local\\AlwaysOnTopPinEvent-892e0aa2-cfa8-4cc4-b196-ddeb32314ce8");
private InteropEvent? _pinEventWrapper;
public void Disable()
{
if (_process is not null && !_process.HasExited)
{
_process.Kill();
}
if (pinEvent != IntPtr.Zero)
{
CloseHandle(pinEvent);
pinEvent = IntPtr.Zero;
}
InteropEvent terminateEventWrapper = new(InteropEvent.AlwaysOnTopTerminate);
terminateEventWrapper.Fire();
terminateEventWrapper.Dispose();
_process?.Dispose();
_pinEventWrapper?.Dispose();
_pinEventWrapper = null;
}
public void Enable()
{
_pinEventWrapper = new InteropEvent(InteropEvent.AlwaysOnTopPin);
var psi = new ProcessStartInfo
{
FileName = "PowerToys.AlwaysOnTop.exe",
@@ -54,19 +52,17 @@ namespace AlwaysOnTopModuleInterface
public Action OnHotkey => () =>
{
if (_process is not null && !_process.HasExited && pinEvent != IntPtr.Zero)
if (!_process?.HasExited ?? false)
{
_ = SetEvent(pinEvent);
_pinEventWrapper?.Fire();
}
};
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateEventW(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetEvent(IntPtr hEvent);
public void Dispose()
{
_process?.Dispose();
_pinEventWrapper?.Dispose();
GC.SuppressFinalize(this);
}
}
}