From 10d379f350a6f94a37b177368301cdc1e2415395 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 22 May 2025 19:54:42 +0800 Subject: [PATCH] Support running application under non-admin mode & Show UAC dialogs when running admin mode when application is under admin mode --- .../Languages/en.xaml | 2 + Plugins/Flow.Launcher.Plugin.Program/Main.cs | 10 +++ .../Programs/UWPPackage.cs | 30 +++++--- .../Programs/Win32.cs | 73 ++++++++++++------- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index e551a7dcb..cd1cc051c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -97,5 +97,7 @@ Successfully disabled this program from displaying in your query This app is not intended to be run as administrator Unable to run {0} + User Account Control + Do you want to allow this app to make changes to your device? diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d28845994..f220190e2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; @@ -32,6 +33,8 @@ namespace Flow.Launcher.Plugin.Program internal static PluginInitContext Context { get; private set; } + internal static bool IsAdmin = IsAdministrator(); + private static readonly List emptyResults = new(); private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 }; @@ -459,5 +462,12 @@ namespace Flow.Launcher.Plugin.Program { Win32.Dispose(); } + + private static bool IsAdministrator() + { + using var identity = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index cb33250e1..182f3fed5 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -4,17 +4,17 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Principal; +using System.Threading.Channels; using System.Threading.Tasks; +using System.Windows.Input; using System.Windows.Media.Imaging; -using Windows.ApplicationModel; -using Windows.Management.Deployment; +using System.Xml; using Flow.Launcher.Plugin.Program.Logger; using Flow.Launcher.Plugin.SharedModels; -using System.Threading.Channels; -using System.Xml; -using Windows.ApplicationModel.Core; -using System.Windows.Input; using MemoryPack; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Core; +using Windows.Management.Deployment; namespace Flow.Launcher.Plugin.Program.Programs { @@ -454,7 +454,9 @@ namespace Flow.Launcher.Plugin.Program.Programs bool elevated = e.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift); bool shouldRunElevated = elevated && CanRunElevated; - _ = Task.Run(() => Launch(shouldRunElevated)).ConfigureAwait(false); + + Launch(shouldRunElevated); + if (elevated && !shouldRunElevated) { var title = api.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error"); @@ -497,7 +499,8 @@ namespace Flow.Launcher.Plugin.Program.Programs Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"), Action = c => { - _ = Task.Run(() => Launch(true)).ConfigureAwait(false); + Launch(true); + return true; }, IcoPath = "Images/cmd.png", @@ -510,12 +513,17 @@ namespace Flow.Launcher.Plugin.Program.Programs private void Launch(bool elevated = false) { - string command = "shell:AppsFolder\\" + UserModelId; + var command = "shell:AppsFolder\\" + UserModelId; command = Environment.ExpandEnvironmentVariables(command.Trim()); - var info = new ProcessStartInfo(command) { UseShellExecute = true, Verb = elevated ? "runas" : "" }; + var info = new ProcessStartInfo() + { + FileName = command, + UseShellExecute = true, + Verb = elevated ? "runas" : "" + }; - Main.StartProcess(Process.Start, info); + _ = Task.Run(() => Main.StartProcess(Process.Start, info)).ConfigureAwait(false); } internal static bool IfAppCanRunElevated(XmlNode appNode) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a87b002d4..3dd7ec012 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -1,21 +1,22 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Security; using System.Text; +using System.Threading.Channels; using System.Threading.Tasks; -using Microsoft.Win32; +using System.Windows; +using System.Windows.Input; using Flow.Launcher.Plugin.Program.Logger; +using Flow.Launcher.Plugin.Program.Views.Models; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Plugin.SharedModels; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Channels; -using Flow.Launcher.Plugin.Program.Views.Models; using IniParser; -using System.Windows.Input; using MemoryPack; +using Microsoft.Win32; namespace Flow.Launcher.Plugin.Program.Programs { @@ -196,15 +197,7 @@ namespace Flow.Launcher.Plugin.Program.Programs // Ctrl + Shift + Enter to run as admin bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift); - var info = new ProcessStartInfo - { - FileName = FullPath, - WorkingDirectory = ParentDirectory, - UseShellExecute = true, - Verb = runAsAdmin ? "runas" : "", - }; - - _ = Task.Run(() => Main.StartProcess(Process.Start, info)); + Launch(runAsAdmin); return true; } @@ -213,6 +206,44 @@ namespace Flow.Launcher.Plugin.Program.Programs return result; } + private void Launch(bool elevated = false) + { + var info = new ProcessStartInfo + { + FileName = FullPath, + WorkingDirectory = ParentDirectory, + UseShellExecute = true, + Verb = elevated ? "runas" : "", + }; + + if (Main.IsAdmin) + { + if (elevated) + { + // Since we are already elevated, we need to create UAC dialog manually + if (Main.Context.API.ShowMsgBox( + Main.Context.API.GetTranslation("flowlauncher_plugin_program_user_account_control_subtitle"), + Main.Context.API.GetTranslation("flowlauncher_plugin_program_user_account_control_title"), + MessageBoxButton.YesNo) != MessageBoxResult.Yes) + { + return; + } + } + else + { + // Use explorer.exe as workaround to start process as standard user + info = new ProcessStartInfo + { + FileName = "explorer.exe", + Arguments = $"\"{FullPath}\"", + WorkingDirectory = ParentDirectory, + UseShellExecute = true, + }; + } + } + + _ = Task.Run(() => Main.StartProcess(Process.Start, info)).ConfigureAwait(false); ; + } public List ContextMenus(IPublicAPI api) { @@ -228,7 +259,7 @@ namespace Flow.Launcher.Plugin.Program.Programs FileName = FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true }; - _ = Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info)); + _ = Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info)).ConfigureAwait(false);; return true; }, @@ -240,15 +271,7 @@ namespace Flow.Launcher.Plugin.Program.Programs Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"), Action = c => { - var info = new ProcessStartInfo - { - FileName = FullPath, - WorkingDirectory = ParentDirectory, - Verb = "runas", - UseShellExecute = true - }; - - _ = Task.Run(() => Main.StartProcess(Process.Start, info)); + Launch(true); return true; },