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)
{