diff --git a/src/RunnerV2/RunnerV2/Helpers/ElevationHelper.cs b/src/RunnerV2/RunnerV2/Helpers/ElevationHelper.cs index fbc26d923c..db5ee42a03 100644 --- a/src/RunnerV2/RunnerV2/Helpers/ElevationHelper.cs +++ b/src/RunnerV2/RunnerV2/Helpers/ElevationHelper.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; using static RunnerV2.NativeMethods; namespace RunnerV2.Helpers @@ -31,17 +32,22 @@ namespace RunnerV2.Helpers case RestartScheduledMode.None: return; case RestartScheduledMode.RestartElevated: - RestartAsAdministrator("--restartedElevated"); + RestartAsAdministrator("--restartedElevated --restarted"); break; case RestartScheduledMode.RestartElevatedWithOpenSettings: - RestartAsAdministrator("--restartedElevated --open-settings"); + RestartAsAdministrator("--restartedElevated --open-settings --restarted"); break; case RestartScheduledMode.RestartNonElevated: - // Todo: restart unelevated + RestartAsNonElevated("--restarted --open-settings"); break; } } + private static void RestartAsNonElevated(string arguments) + { + PowerToys.Interop.Elevation.RunNonElevated(Environment.ProcessPath, arguments); + } + private static void RestartAsAdministrator(string arguments) { Logger.LogInfo("Restarting as administrator, because it was scheudled."); @@ -62,6 +68,8 @@ namespace RunnerV2.Helpers { Logger.LogError("Failed to restart as administrator.", ex); } + + Environment.Exit(0); } internal static bool IsProcessElevated(bool useCachedValue = true) diff --git a/src/RunnerV2/RunnerV2/Helpers/SettingsHelper.cs b/src/RunnerV2/RunnerV2/Helpers/SettingsHelper.cs index f052a02b1e..8a2094c403 100644 --- a/src/RunnerV2/RunnerV2/Helpers/SettingsHelper.cs +++ b/src/RunnerV2/RunnerV2/Helpers/SettingsHelper.cs @@ -119,13 +119,18 @@ namespace RunnerV2.Helpers Runner.Close(); break; case "restart_maintain_elevation": - // Todo: + ElevationHelper.RestartScheduled = ElevationHelper.IsProcessElevated() ? ElevationHelper.RestartScheduledMode.RestartElevatedWithOpenSettings : ElevationHelper.RestartScheduledMode.RestartNonElevated; + + ElevationHelper.RestartIfScheudled(); + break; case "check_for_updates": UpdateSettingsHelper.TriggerUpdateCheck(); break; case "request_update_state_date": - // Todo: + JsonObject response = []; + response["updateStateDate"] = UpdateSettingsHelper.GetLastCheckedDate(); + _ipc?.Send(response.ToJsonString()); break; } diff --git a/src/RunnerV2/RunnerV2/Models/ProcessModuleAbstractClass.cs b/src/RunnerV2/RunnerV2/Models/ProcessModuleAbstractClass.cs index 47b0ad1044..5e998ea190 100644 --- a/src/RunnerV2/RunnerV2/Models/ProcessModuleAbstractClass.cs +++ b/src/RunnerV2/RunnerV2/Models/ProcessModuleAbstractClass.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Globalization; +using System.IO; using ManagedCommon; using RunnerV2.Helpers; @@ -61,6 +62,11 @@ namespace RunnerV2.Models /// Sets the launched process to realtime priority. /// RealtimePriority = 128, + + /// + /// Indicates that the process should never be launched with elevated privileges, even if the runner process is elevated. + /// + NeverElevate = 256, } /// @@ -126,6 +132,12 @@ namespace RunnerV2.Models string arguments = (LaunchOptions.HasFlag(ProcessLaunchOptions.RunnerProcessIdAsFirstArgument) ? Environment.ProcessId.ToString(CultureInfo.InvariantCulture) + (string.IsNullOrEmpty(ProcessArguments) ? string.Empty : " ") : string.Empty) + ProcessArguments; + if (ElevationHelper.IsProcessElevated() && LaunchOptions.HasFlag(ProcessLaunchOptions.NeverElevate)) + { + PowerToys.Interop.Elevation.RunNonElevated(Path.GetFullPath(ProcessPath), arguments); + return; + } + Process? p = Process.Start(new ProcessStartInfo() { UseShellExecute = LaunchOptions.HasFlag(ProcessLaunchOptions.UseShellExecute), diff --git a/src/RunnerV2/RunnerV2/ModuleInterfaces/PeekModuleInterface.cs b/src/RunnerV2/RunnerV2/ModuleInterfaces/PeekModuleInterface.cs index 86d3f8783a..ee73205e5c 100644 --- a/src/RunnerV2/RunnerV2/ModuleInterfaces/PeekModuleInterface.cs +++ b/src/RunnerV2/RunnerV2/ModuleInterfaces/PeekModuleInterface.cs @@ -38,7 +38,7 @@ namespace RunnerV2.ModuleInterfaces public override string ProcessName => "PowerToys.Peek.UI"; - public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.SingletonProcess; + public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.SingletonProcess | (SettingsUtils.Default.GetSettings(Name).Properties.AlwaysRunNotElevated.Value ? ProcessLaunchOptions.NeverElevate : 0); public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = []; diff --git a/src/RunnerV2/RunnerV2/Program.cs b/src/RunnerV2/RunnerV2/Program.cs index d4b93a7f56..f99ab80a29 100644 --- a/src/RunnerV2/RunnerV2/Program.cs +++ b/src/RunnerV2/RunnerV2/Program.cs @@ -56,11 +56,14 @@ internal sealed class Program throw new NotImplementedException("Special modes are not implemented yet."); } + // If PowerToys restarted the old process may still be around + bool hasRestartedArgment = args.Contains("--restarted"); + bool shouldOpenSettings = args.Any(s => s.StartsWith("--open-settings", StringComparison.InvariantCulture)); bool shouldOpenSettingsToSpecificPage = args.Any(s => s.StartsWith("--open-settings=", StringComparison.InvariantCulture)); // Check if PowerToys is already running - if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1) + if ((!hasRestartedArgment && Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1) || Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 2) { IntPtr hwndMain = NativeMethods.FindWindowW(Runner.TrayWindowClassName, null!); NativeMethods.PostMessageW(hwndMain, 0x0111, 1, IntPtr.Zero); @@ -74,7 +77,16 @@ internal sealed class Program bool isElevated = ElevationHelper.IsProcessElevated(); bool hasDontElevateArgument = args.Contains("--dont-elevate"); - bool runElevatedSetting = GeneralSettings.RunElevated; + bool runElevatedSetting = false; + try + { + runElevatedSetting = GeneralSettings.RunElevated; + } + catch + { + Logger.LogError("Could not retrieve run elevated setting"); + } + bool hasRestartedElevatedArgment = args.Contains("--restartedElevated"); Action afterInitializationAction = () => { }; @@ -112,8 +124,8 @@ internal sealed class Program switch ((isElevated, hasDontElevateArgument, runElevatedSetting, hasRestartedElevatedArgment)) { case (true, true, false, _): - // Todo: Scheudle restart as non elevated - throw new NotImplementedException(); + ElevationHelper.RestartScheduled = ElevationHelper.RestartScheduledMode.RestartNonElevated; + break; case (true, _, _, _): case (_, _, false, _): case (_, true, _, _): diff --git a/src/RunnerV2/RunnerV2/Runner.cs b/src/RunnerV2/RunnerV2/Runner.cs index 06c166faa3..18bf252aab 100644 --- a/src/RunnerV2/RunnerV2/Runner.cs +++ b/src/RunnerV2/RunnerV2/Runner.cs @@ -135,7 +135,14 @@ namespace RunnerV2 while (GetMessageW(out MSG msg, IntPtr.Zero, 0, 0) != 0 || true) { TranslateMessage(ref msg); - DispatchMessageW(ref msg); + try + { + DispatchMessageW(ref msg); + } + catch (Exception e) + { + Logger.LogError("Uncaught error in message loop: ", e); + } // Supress duplicate handling of HOTKEY messages if (msg.Message == (uint)WindowMessages.HOTKEY) @@ -196,8 +203,6 @@ namespace RunnerV2 { if ((module.Enabled && (module.GpoRuleConfigured != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)) || module.GpoRuleConfigured == PowerToys.GPOWrapper.GpoRuleConfigured.Enabled) { - /* Todo: conflict manager */ - if (!LoadedModules.Contains(module)) { module.Enable(); diff --git a/src/Update/UpdateSettingsHelper.cs b/src/Update/UpdateSettingsHelper.cs index 8d1899cd5c..3a2cb94092 100644 --- a/src/Update/UpdateSettingsHelper.cs +++ b/src/Update/UpdateSettingsHelper.cs @@ -51,6 +51,18 @@ namespace Update _updateThread.Start(); } + public static string GetLastCheckedDate() + { + UpdatingSettings updatingSettings = UpdatingSettings.LoadSettings(); + if (long.TryParse(updatingSettings.LastCheckedDate, out long lastCheckedDateSeconds)) + { + DateTimeOffset lastCheckedDate = DateTimeOffset.FromUnixTimeSeconds(lastCheckedDateSeconds); + return lastCheckedDate.ToString("g", CultureInfo.CurrentCulture); + } + + return string.Empty; + } + internal record UpdateInfo { private UpdateInfo() diff --git a/src/common/interop/Elevation.cpp b/src/common/interop/Elevation.cpp new file mode 100644 index 0000000000..c6204036f8 --- /dev/null +++ b/src/common/interop/Elevation.cpp @@ -0,0 +1,14 @@ +#include "pch.h" +#define SUPRESS_LOGGER +#include "../utils/elevation.h" +#include "Elevation.h" +#include "Elevation.g.cpp" + +namespace winrt::PowerToys::Interop::implementation +{ + void Elevation::RunNonElevated(const winrt::hstring& file, const winrt::hstring& params) + { + RunNonElevatedFailsafe((std::wstring)file, params.c_str(), get_module_folderpath()); + } +} + diff --git a/src/common/interop/Elevation.h b/src/common/interop/Elevation.h new file mode 100644 index 0000000000..9833a42571 --- /dev/null +++ b/src/common/interop/Elevation.h @@ -0,0 +1,17 @@ +#pragma once +#include "Elevation.g.h" + +namespace winrt::PowerToys::Interop::implementation +{ + struct Elevation : ElevationT + { + static void RunNonElevated(const winrt::hstring& file, const winrt::hstring& params); + }; +} +namespace winrt::PowerToys::Interop::factory_implementation +{ + struct Elevation : ElevationT + { + }; +} + diff --git a/src/common/interop/Elevation.idl b/src/common/interop/Elevation.idl new file mode 100644 index 0000000000..8b111a03e4 --- /dev/null +++ b/src/common/interop/Elevation.idl @@ -0,0 +1,12 @@ +namespace PowerToys +{ + namespace Interop + { + [default_interface] runtimeclass Elevation + { + static void RunNonElevated(String file, String params); + } + + } +} + diff --git a/src/common/interop/PowerToys.Interop.vcxproj b/src/common/interop/PowerToys.Interop.vcxproj index ccf244c47c..9266381a5f 100644 --- a/src/common/interop/PowerToys.Interop.vcxproj +++ b/src/common/interop/PowerToys.Interop.vcxproj @@ -41,7 +41,6 @@ DynamicLibrary - Unicode false @@ -101,6 +100,7 @@ KeyboardListener.idl + HotkeyManager.idl @@ -131,6 +131,7 @@ KeyboardListener.idl + HotkeyManager.idl @@ -168,6 +169,7 @@ + @@ -175,6 +177,11 @@ + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + diff --git a/src/common/interop/PowerToys.Interop.vcxproj.filters b/src/common/interop/PowerToys.Interop.vcxproj.filters index 2f161d5959..78a5568e24 100644 --- a/src/common/interop/PowerToys.Interop.vcxproj.filters +++ b/src/common/interop/PowerToys.Interop.vcxproj.filters @@ -63,6 +63,9 @@ Header Files + + Header Files + @@ -101,6 +104,9 @@ Source Files + + Source Files + @@ -142,6 +148,9 @@ Source Files + + Source Files + diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index e412ce5aa3..3fd8eef36a 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -17,11 +17,45 @@ #include #include +#ifndef SUPRESS_LOGGER #include +#endif // SUPRESS_LOGGER #include #include #include +#ifdef SUPRESS_LOGGER +class Logger +{ +public: + template + static void trace(const FormatString&, const Args&...) + { + } + template + static void debug(const FormatString&, const Args&...) + { + } + template + static void info(const FormatString&, const Args&...) + { + } + template + static void warn(const FormatString&, const Args&...) + { + } + template + static void error(const FormatString&, const Args&...) + { + } + template + static void critical(const FormatString&, const Args&...) + { + } +}; +#endif // SUPRESS_LOGGER + + namespace { inline std::wstring GetErrorString(HRESULT handle) @@ -406,7 +440,7 @@ inline std::optional RunNonElevatedFailsafe(const std::wstring& fil { Logger::warn(L"RunNonElevatedEx() failed. Trying fallback"); std::wstring action_runner_path = get_module_folderpath() + L"\\PowerToys.ActionRunner.exe"; - std::wstring newParams = fmt::format(L"-run-non-elevated -target \"{}\" {}", file, params); + std::wstring newParams = std::format(L"-run-non-elevated -target \"{}\" {}", file, params); launched = run_non_elevated(action_runner_path, newParams, nullptr, working_dir.c_str()); if (launched) {