Compare commits

...

166 commits

Author SHA1 Message Date
Jack Ye
ce04d8060e
Merge 6b2b7ac304 into 5ef6a2b688 2026-03-09 11:05:41 +08:00
Jack251970
6b2b7ac304 Update solution for VS 18 and streamline project configs
Updated Flow.Launcher.sln for Visual Studio 18 (insiders), reordered project dependencies, and replaced duplicated project configuration entries with a single set to improve build consistency. Solution Items section adjusted with no item changes.
2026-02-26 11:49:56 +08:00
Jack251970
5ad361f132 Update target framework to .NET 9.0
Changed Flow.Launcher.Command.csproj and Net9.0-SelfContained.pubxml to use .NET 9.0 instead of .NET 7.0 for building and publishing. This ensures compatibility with the latest .NET runtime.
2026-02-26 11:40:06 +08:00
Jack Ye
80d8777d53
Merge branch 'dev' into administrator_mode 2026-02-26 10:52:50 +08:00
Jack Ye
8d5b287dbe
Merge branch 'dev' into administrator_mode 2025-11-20 21:07:01 +08:00
Jack Ye
e20863b482
Merge branch 'dev' into administrator_mode 2025-11-13 11:30:01 +08:00
Jack Ye
9b1c2f442d
Improve translations
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-08 11:41:50 +08:00
Jack Ye
2c8df64559
Merge branch 'dev' into administrator_mode 2025-11-08 11:32:58 +08:00
Jack251970
c01b2081aa Expand using directives in PublicAPIInstance.cs
Added multiple namespaces to the using directives in PublicAPIInstance.cs, including System.Collections, System.Linq, System.Threading, and others. This change enables access to a broader range of functionality required by the file.
2025-10-16 19:21:18 +08:00
Jack Ye
e04a040177
Merge branch 'dev' into administrator_mode 2025-10-16 19:13:23 +08:00
Jack Ye
9823a2afa5
Merge branch 'dev' into administrator_mode 2025-10-14 21:53:15 +08:00
Jack Ye
cf1d162f2c
Merge branch 'dev' into administrator_mode 2025-10-12 14:23:02 +08:00
Jack251970
9797ad10a4 Adjust UI 2025-10-05 20:54:32 +08:00
Jack251970
3b0242c457 Adjust UI 2025-10-05 20:53:30 +08:00
Jack251970
7bcde06b4c Fix format 2025-10-05 19:23:37 +08:00
Jack251970
5ee2c3a916 Revert changes 2025-10-05 19:20:56 +08:00
Jack251970
73f0e302fc Remove unnecessary code comments 2025-10-05 19:20:20 +08:00
Jack251970
477bd9e458 Add project build dependency 2025-10-05 19:19:54 +08:00
Jack251970
20b4734321 Fix build issue 2025-10-05 19:13:38 +08:00
Jack Ye
4f77dc9f84
Merge branch 'dev' into administrator_mode 2025-10-05 19:10:48 +08:00
Jack251970
0da575dc16 Fix build issue 2025-10-02 20:18:34 +08:00
Jack251970
3d67ed4ae5 Cleanup NativeMethods.txt 2025-10-02 20:15:57 +08:00
Jack Ye
8eee959e6d
Merge branch 'dev' into administrator_mode 2025-10-02 20:13:34 +08:00
Jack Ye
7a702eb7de
Merge branch 'dev' into administrator_mode 2025-09-25 19:59:55 +08:00
Jack Ye
1829d69d56
Merge branch 'dev' into administrator_mode 2025-09-21 21:37:30 +08:00
Jack251970
e168984129 Resolve conflicts 2025-09-21 13:06:14 +08:00
Jack Ye
3282b02eed
Merge branch 'dev' into administrator_mode 2025-09-21 13:05:37 +08:00
Jack251970
97e3b3c850 Initialize STARTUPINFOW.cb 2025-09-06 13:34:30 +08:00
Jack Ye
4d4f1e8bf5
Merge branch 'dev' into administrator_mode 2025-09-06 13:32:16 +08:00
Jack Ye
a9c2240738
Merge branch 'dev' into administrator_mode 2025-09-05 19:51:48 +08:00
Jack Ye
c9ca92e370
Merge branch 'dev' into administrator_mode 2025-08-30 21:38:03 +08:00
Jack Ye
1cf0d44719
Merge branch 'dev' into administrator_mode 2025-08-17 09:20:17 +01:00
Jack Ye
e85c569cfb
Merge branch 'dev' into administrator_mode 2025-08-16 04:44:21 +01:00
Jack Ye
aac717c011
Merge branch 'dev' into administrator_mode 2025-08-07 02:40:27 +01:00
Jack Ye
2f25855e47
Merge branch 'dev' into administrator_mode 2025-07-24 11:58:43 +01:00
Jack251970
a029b2af5d Fix card type 2025-07-21 16:10:49 +08:00
Jack251970
236ba67d08 Fix build issue 2025-07-20 21:00:32 +08:00
Jack Ye
35faa46715
Merge branch 'dev' into administrator_mode 2025-07-20 20:04:06 +08:00
Jack Ye
ba007da5ae
Merge branch 'dev' into administrator_mode 2025-07-19 20:41:20 +08:00
Jack Ye
9a320bc193
Merge branch 'dev' into administrator_mode 2025-07-15 20:53:58 +08:00
Jack Ye
0a5e50a0e1
Merge branch 'dev' into administrator_mode 2025-07-14 09:08:45 +08:00
Jack251970
c8de5ef2d4 Revert to InsideFit type 2025-07-07 21:05:19 +08:00
Jack251970
42e11d88c8 Remove blank line 2025-07-07 21:02:20 +08:00
Jack Ye
758d025c59
Merge branch 'dev' into administrator_mode 2025-07-07 20:20:48 +08:00
Jack251970
bedabb28d1 Fix document & using issue 2025-07-05 18:08:32 +08:00
Jack Ye
fb5680d9c4
Merge branch 'dev' into administrator_mode 2025-07-05 18:05:31 +08:00
Jack Ye
f02a6c5118
Merge branch 'dev' into administrator_mode 2025-06-28 18:26:29 +08:00
Jack251970
7de9d25415 Fix CI issue 2025-06-23 10:39:17 +08:00
Jack251970
0e70781559 Build command project before main project 2025-06-23 10:28:20 +08:00
Jack251970
8eaaf91db7 Adjust setting ui 2025-06-23 10:05:20 +08:00
Jack Ye
48ed8979f9
Merge branch 'dev' into administrator_mode 2025-06-17 17:13:49 +08:00
Jack251970
71af5ef7e0 Check if the application is running as administrator ealier 2025-06-16 22:54:23 +08:00
Jack251970
4416efece8 Revert "Check if the application is running as administrator ealier"
This reverts commit 4862a2d816.
2025-06-16 22:53:35 +08:00
Jack251970
4862a2d816 Check if the application is running as administrator ealier 2025-06-16 21:58:49 +08:00
Jack Ye
10567fd409
Merge branch 'dev' into administrator_mode 2025-06-16 21:58:05 +08:00
Jack251970
81667d03ee Fix ui issue 2025-06-15 17:46:52 +08:00
Jack251970
721bb987fd Handle create no window parameter in command exe 2025-06-15 16:36:13 +08:00
Jack251970
7ea76b8539 Support administrator handling for ShellRun api function 2025-06-15 16:25:16 +08:00
Jack251970
3493446d3e Support createNoWindow for start process api 2025-06-15 16:22:56 +08:00
Jack251970
911999810c Build command project after main project 2025-06-15 15:54:17 +08:00
Jack251970
478ba29966 Change target framework to remove dll files 2025-06-15 13:02:21 +08:00
Jack Ye
d1daf512ab
Merge branch 'dev' into administrator_mode 2025-06-15 12:51:19 +08:00
Jack251970
44b25b487a Prefer injected Context over static Main.Context 2025-06-15 12:29:05 +08:00
Jack251970
39de484d2d Merge branch 'administrator_mode' of https://github.com/Flow-Launcher/Flow.Launcher into administrator_mode 2025-06-15 01:27:58 +08:00
Jack Ye
1dfec7f3eb
Merge branch 'dev' into administrator_mode 2025-06-15 01:26:59 +08:00
Jack251970
4df7264c8a Improve documents 2025-06-15 01:25:34 +08:00
Jack251970
b6b45d495a Improve code quality 2025-06-15 01:24:07 +08:00
Jack251970
e5316a0ac4 Check Process.Start usage 2025-06-15 01:16:46 +08:00
Jack Ye
a76ade8ae1
Merge branch 'dev' into administrator_mode 2025-06-14 20:42:18 +08:00
Jack251970
e1242eefda Check Process.Start usage 2025-06-14 17:07:45 +08:00
Jack251970
8c8180815b Change default value 2025-06-14 16:03:41 +08:00
Jack251970
935101c6e3 Change command project output path 2025-06-14 15:51:30 +08:00
Jack251970
2f3b97302b Do not publish command project 2025-06-14 15:08:59 +08:00
Jack251970
764b524ea4 Use exe console type & Add flag support for helper method 2025-06-14 15:08:48 +08:00
Jack251970
ad6d598c4a Change publish sequence 2025-06-14 14:26:59 +08:00
Jack251970
a34e4042a4 Change target framework 2025-06-14 13:57:59 +08:00
Jack Ye
7fd3a4a427
Merge branch 'dev' into administrator_mode 2025-06-13 23:47:37 +08:00
Jack251970
11c0eb3111 Fix command project publish issue 2025-06-13 23:45:28 +08:00
Jack251970
8dd9b9c963 Publish command project 2025-06-13 23:19:03 +08:00
Jack251970
7930d57c91 Add publish file 2025-06-13 23:07:16 +08:00
Jack251970
0602143359 Change target framework 2025-06-13 19:23:19 +08:00
Jack251970
d363cade42 Improve code quality 2025-06-13 18:28:54 +08:00
Jack251970
3a921022f3 Add new api RestartAppAsAdmin 2025-06-13 18:07:42 +08:00
Jack251970
f2d3ba712f Combine restart functions to one function 2025-06-13 17:58:05 +08:00
Jack251970
9d480fc2b9 Quote the executable path to survive spaces & Improve code quality 2025-06-13 17:53:57 +08:00
Jack251970
def02b34a1 Improve xml documents 2025-06-13 17:50:51 +08:00
Jack251970
1fabe8d849 Improve string resource 2025-06-13 17:43:41 +08:00
Jack251970
dd0899f69a Check always running as administrator 2025-06-13 17:39:21 +08:00
Jack Ye
02de9d617e
Merge branch 'dev' into administrator_mode 2025-06-13 17:10:00 +08:00
Jack251970
680419faa3 Code quality 2025-06-13 14:57:25 +08:00
Jack Ye
6843fe0ac4
Fix parameter issue
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-13 14:34:39 +08:00
Jack Ye
5ddf705a2f
Fix typo
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-13 14:29:58 +08:00
Jack251970
d1b4c3ce70 Merge branch 'administrator_mode' of https://github.com/Flow-Launcher/Flow.Launcher into administrator_mode 2025-06-13 14:28:57 +08:00
Jack251970
be2c59e8ef Call Win32Helper.IsAdministrator() directly 2025-06-13 14:28:51 +08:00
Jack Ye
8287b3373b
Fix typo
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-13 14:27:24 +08:00
Jack251970
6109504ddc Add solution assembly information 2025-06-13 14:25:51 +08:00
Jack251970
dae6f5d95c Use WinExe to avoid console ui 2025-06-13 14:19:43 +08:00
Jack251970
d56f0a0d1a Improve process information handler 2025-06-13 14:13:52 +08:00
Jack251970
50a8e888f0 Use double quotes to fix possible issue 2025-06-13 14:13:39 +08:00
Jack251970
a2be6e0d0e Use new api for shell plugin 2025-06-13 13:39:36 +08:00
Jack251970
4ebfb3c5fd Add api function 2025-06-13 13:39:13 +08:00
Jack251970
898dbb8402 Use new api for uwp package opening 2025-06-13 13:30:51 +08:00
Jack251970
2c0210c480 Revert unnecessary changes in solution file 2025-06-13 13:28:03 +08:00
Jack251970
5551bdf9d0 Remove uac dialog api function 2025-06-13 13:23:33 +08:00
Jack251970
3ed8af8c6c Use new api for program plugin 2025-06-13 13:22:27 +08:00
Jack251970
a0ebe2916f Improve api function 2025-06-13 13:20:37 +08:00
Jack251970
b8a697c266 Fix running issue 2025-06-13 13:12:00 +08:00
Jack251970
13b5012386 Add command executer path 2025-06-13 13:11:27 +08:00
Jack251970
2df7a6179b Add command project 2025-06-13 13:10:42 +08:00
Jack251970
393d106c15 Fix command line pass issue 2025-06-13 13:07:55 +08:00
Jack251970
61717dcd50 Ensure SetImageAsync exceptions are handled 2025-06-12 18:51:03 +08:00
Jack251970
8cfaeeae6b Return value of RunAsDesktopUser 2025-06-12 18:46:31 +08:00
Jack251970
9c6c015b1d Reflect user intent 2025-06-12 18:45:22 +08:00
Jack251970
6842fcaf44 Replace IntPtr.Zero with Handle or HWND 2025-06-12 16:30:54 +08:00
Jack251970
94f0bb961e Handle command running with administrator mode correctly 2025-06-12 16:21:00 +08:00
Jack251970
8b8fcd8c7b Add IsAdmin 2025-06-12 16:16:58 +08:00
Jack251970
a3212affd7 Dynamically find wt.exe 2025-06-12 16:16:47 +08:00
Jack251970
a775323108 Improve uac dialog api function 2025-06-12 16:07:09 +08:00
Jack251970
0d1358fdc3 Add return value for start process api function 2025-06-12 15:32:34 +08:00
Jack251970
21e8d92aa2 Add show uac dialog api function 2025-06-12 15:30:25 +08:00
Jack251970
433e0fd0e1 Improve uac dialog path 2025-06-12 15:18:14 +08:00
Jack251970
255ca5ac01 Improve code quality 2025-06-11 21:34:33 +08:00
Jack251970
b15cece4b8 Fix absolute exe file path issue 2025-06-11 21:13:09 +08:00
Jack251970
fb4735fbc0 Fix program deelevate open issue 2025-06-11 20:02:54 +08:00
Jack251970
30deb6da99 Fix argument sequence issue 2025-06-11 20:02:32 +08:00
Jack251970
f9b2be1fc7 Let work directory can be empty 2025-06-11 20:01:37 +08:00
Jack251970
d13901bb9d Update work directory correctly 2025-06-11 20:00:40 +08:00
Jack251970
ea9ede2e0e Fix deelevate process running issue 2025-06-11 20:00:25 +08:00
Jack251970
e8c03c61d8 Update documents 2025-06-11 20:00:02 +08:00
Jack251970
f1f7309762 Add working directory check & Add try-catch 2025-06-11 16:55:38 +08:00
Jack251970
4cf66362af Fix handle close issue 2025-06-11 16:52:19 +08:00
Jack251970
d77993e215 Use new api function to start shell process 2025-06-11 16:22:24 +08:00
Jack251970
fc53efd6a6 Use new api function to open application 2025-06-11 16:04:17 +08:00
Jack251970
100c21a83e Add new api to run process as desktop user 2025-06-11 16:02:24 +08:00
Jack251970
e391c98474 Code quality 2025-06-11 15:35:17 +08:00
Jack251970
1a8334f1d3 Add RunAsDesktopUser helper method 2025-06-11 15:18:14 +08:00
Jack Ye
2c1f1617cd
Merge branch 'dev' into administrator_mode 2025-06-10 19:42:41 +08:00
Jack251970
23f2489680 Force admin restart and fix build issue 2025-06-03 17:22:26 +08:00
Jack251970
de26abe7b8 Use api functions instead 2025-06-03 17:22:05 +08:00
Jack251970
b2edea4201 Improve code quality 2025-06-03 17:21:48 +08:00
Jack251970
ba1ada7bf1 Continue to restart app as administrator if app is run as administrator already 2025-06-03 14:45:12 +08:00
Jack Ye
ca6f077e63
Merge branch 'dev' into administrator_mode 2025-06-03 14:38:56 +08:00
Jack251970
1b80290995 Do not show noticification when run level is correct 2025-05-23 16:11:29 +08:00
Jack251970
80c0288235 Throw exception when editing highest run level task 2025-05-23 13:24:32 +08:00
Jack251970
c3ec002b72 Show message box to ask users to restart as administrator when encountering issue checking startup 2025-05-23 13:21:34 +08:00
Jack251970
479b49db94 Throw UnauthorizedAccessException when encountering admin issue 2025-05-23 13:20:36 +08:00
Jack251970
d6462f4fdc Move restart function to app class 2025-05-23 13:18:19 +08:00
Jack251970
eacccf9823 Add code comments & Use local function 2025-05-23 12:56:23 +08:00
Jack251970
35f4fd5b18 Fix restart as administrator issue 2025-05-23 12:36:03 +08:00
Jack251970
52c36ff8d4 Only restart from non-admin to admin & Fix restart as administrator issue 2025-05-23 11:14:15 +08:00
Jack251970
eae4a9584b Use readonly bool variable 2025-05-23 11:05:19 +08:00
Jack251970
365ba6afa5 Code quality 2025-05-23 11:04:27 +08:00
Jack251970
e9c40e4586 Revert "Add new restart api"
This reverts commit 7611b00287.
2025-05-23 11:01:32 +08:00
Jack251970
1073061bda Remove old before creating new one 2025-05-22 22:55:29 +08:00
Jack251970
c5c24acb17 Check run level 2025-05-22 22:55:17 +08:00
Jack251970
902c8f4412 Check value changed 2025-05-22 22:18:25 +08:00
Jack251970
91f7a4fd55 Remove useless semicolon 2025-05-22 22:17:10 +08:00
Jack Ye
0edd626cfb
Remove useless semicolon
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-22 22:13:46 +08:00
Jack251970
fe70a72a62 Add admin mode configuration in general page 2025-05-22 22:04:47 +08:00
Jack251970
40cf413898 Add admin support for auto startup 2025-05-22 22:04:32 +08:00
Jack251970
d29a5d4b1f Add setting model 2025-05-22 22:03:25 +08:00
Jack251970
7611b00287 Add new restart api 2025-05-22 21:46:27 +08:00
Jack251970
484f91c52d Improve UAC dialog 2025-05-22 20:26:41 +08:00
Jack251970
10d379f350 Support running application under non-admin mode & Show UAC dialogs when running admin mode when application is under admin mode 2025-05-22 19:54:42 +08:00
Jack251970
369ed86d1d Add administrator text in tray icon 2025-05-22 18:44:01 +08:00
Jack251970
7549bba293 Add administrator mode check 2025-05-22 18:43:41 +08:00
29 changed files with 840 additions and 131 deletions

View file

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Flow.Launcher.Command</RootNamespace>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<ApplicationIcon>app.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Output\Debug\Command\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Output\Release\Command\</OutputPath>
<DefineConstants>TRACE;RELEASE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Content Include="app.ico" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SolutionAssemblyInfo.cs" Link="Properties\SolutionAssemblyInfo.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,132 @@
using System.Diagnostics;
namespace Flow.Launcher.Command;
internal static class Program
{
[STAThread]
private static int Main(string[] args)
{
if (args.Length == 0) return -1;
// Start process with arguments
// Usage: Flow.Launcher.Command -StartProcess -FileName <file> -WorkingDirectory <directory> -Arguments <args> -UseShellExecute <true|false> -Verb <verb> -CreateNoWindow <true|false>
if (args[0] == @"-StartProcess")
{
var fileName = string.Empty;
var workingDirectory = Environment.CurrentDirectory;
var argumentList = new List<string>();
var useShellExecute = true;
var verb = string.Empty;
var createNoWindow = false;
var isArguments = false;
for (int i = 1; i < args.Length; i++)
{
switch (args[i])
{
case "-FileName":
if (i + 1 < args.Length)
fileName = args[++i];
isArguments = false;
break;
case "-WorkingDirectory":
if (i + 1 < args.Length)
workingDirectory = args[++i];
isArguments = false;
break;
case "-Arguments":
if (i + 1 < args.Length)
argumentList.Add(args[++i]);
isArguments = true;
break;
case "-UseShellExecute":
if (i + 1 < args.Length && bool.TryParse(args[++i], out bool useShell))
useShellExecute = useShell;
isArguments = false;
break;
case "-Verb":
if (i + 1 < args.Length)
verb = args[++i];
isArguments = false;
break;
case "-CreateNoWindow":
if (i + 1 < args.Length && bool.TryParse(args[++i], out bool createNoWin))
createNoWindow = createNoWin;
break;
default:
if (isArguments)
argumentList.Add(args[i]);
else
Console.WriteLine($"Unknown parameter: {args[i]}");
break;
}
}
if (string.IsNullOrEmpty(fileName))
{
Console.WriteLine("Error: -FileName is required.");
return -2;
}
try
{
ProcessStartInfo info;
if (argumentList.Count == 0)
{
info = new ProcessStartInfo
{
FileName = fileName,
WorkingDirectory = workingDirectory,
UseShellExecute = useShellExecute,
Verb = verb,
CreateNoWindow = createNoWindow
};
}
else if (argumentList.Count == 1)
{
info = new ProcessStartInfo
{
FileName = fileName,
WorkingDirectory = workingDirectory,
Arguments = argumentList[0],
UseShellExecute = useShellExecute,
Verb = verb,
CreateNoWindow = createNoWindow
};
}
else
{
info = new ProcessStartInfo
{
FileName = fileName,
WorkingDirectory = workingDirectory,
UseShellExecute = useShellExecute,
Verb = verb,
CreateNoWindow = createNoWindow
};
foreach (var arg in argumentList)
{
info.ArgumentList.Add(arg);
}
}
Process.Start(info)?.Dispose();
Console.WriteLine("Success.");
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
return -3;
}
}
return -4;
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
<PublishDir>..\Output\Release\</PublishDir>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
<PublishTrimmed>False</PublishTrimmed>
</PropertyGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View file

@ -43,7 +43,7 @@ namespace Flow.Launcher.Core.Configuration
PublicApi.Instance.ShowMsgBox(Localize.restartToDisablePortableMode());
UpdateManager.RestartApp(Constant.ApplicationFileName);
PublicApi.Instance.RestartApp();
}
catch (Exception e)
{
@ -66,7 +66,7 @@ namespace Flow.Launcher.Core.Configuration
PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode());
UpdateManager.RestartApp(Constant.ApplicationFileName);
PublicApi.Instance.RestartApp();
}
catch (Exception e)
{

View file

@ -1,19 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using JetBrains.Annotations;
using Squirrel;
@ -91,7 +91,7 @@ namespace Flow.Launcher.Core
if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
_api.RestartApp();
}
}
catch (Exception e)

View file

@ -16,6 +16,7 @@ namespace Flow.Launcher.Infrastructure
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
public static readonly string ProgramDirectory = Directory.GetParent(Assembly.Location.NonNull()).ToString();
public static readonly string ExecutablePath = Path.Combine(ProgramDirectory, FlowLauncher + ".exe");
public static readonly string CommandExecutablePath = Path.Combine(ProgramDirectory, "Command", "Flow.Launcher.Command.exe");
public static readonly string ApplicationDirectory = Directory.GetParent(ProgramDirectory).ToString();
public static readonly string RootDirectory = Directory.GetParent(ApplicationDirectory).ToString();

View file

@ -226,7 +226,7 @@ namespace Flow.Launcher.Infrastructure.Http
Log.Debug(ClassName, $"Url <{url}>");
return await client.GetStringAsync(url, token);
}
catch (System.Exception e)
catch (System.Exception)
{
return string.Empty;
}

View file

@ -88,9 +88,25 @@ EVENT_SYSTEM_DIALOGEND
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
WM_POWERBROADCAST
PBT_APMRESUMEAUTOMATIC
PBT_APMRESUMESUSPEND
PowerRegisterSuspendResumeNotification
PowerUnregisterSuspendResumeNotification
OpenProcessToken
GetCurrentProcess
LookupPrivilegeValue
SE_INCREASE_QUOTA_NAME
CloseHandle
TOKEN_PRIVILEGES
AdjustTokenPrivileges
GetShellWindow
GetWindowThreadProcessId
OpenProcess
GetProcessId
DuplicateTokenEx
CreateProcessWithTokenW
DeviceNotifyCallbackRoutine
MonitorFromWindow
MonitorFromWindow

View file

@ -483,6 +483,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool HideWhenDeactivated { get; set; } = true;
public bool ShowTaskbarWhenInvoked { get; set; } = false;
public bool AlwaysRunAsAdministrator { get; set; } = false;
private bool _showAtTopmost = false;
public bool ShowAtTopmost
{

View file

@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@ -19,6 +20,7 @@ using Microsoft.Win32.SafeHandles;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.Security;
using Windows.Win32.System.Power;
using Windows.Win32.System.Threading;
using Windows.Win32.UI.Input.KeyboardAndMouse;
@ -690,6 +692,7 @@ namespace Flow.Launcher.Infrastructure
{
try
{
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
}
catch (System.Exception)
@ -1017,6 +1020,164 @@ namespace Flow.Launcher.Infrastructure
#endregion
#region Administrator Mode
public static bool IsAdministrator()
{
using var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
/// <summary>
/// Inspired by <see href="https://github.com/jay/RunAsDesktopUser">
/// Document: <see href="https://learn.microsoft.com/en-us/archive/blogs/aaron_margosis/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app">
/// </summary>
public static unsafe bool RunAsDesktopUser(string app, string currentDir, string cmdLine, bool loadProfile, bool createNoWindow, out string errorInfo)
{
STARTUPINFOW si = new()
{
cb = (uint)Marshal.SizeOf<STARTUPINFOW>()
};
PROCESS_INFORMATION pi = new();
errorInfo = string.Empty;
HANDLE hShellProcess = HANDLE.Null, hShellProcessToken = HANDLE.Null, hPrimaryToken = HANDLE.Null;
HWND hwnd;
uint dwPID;
// 1. Enable the SeIncreaseQuotaPrivilege in your current token
if (!PInvoke.OpenProcessToken(PInvoke.GetCurrentProcess_SafeHandle(), TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES, out var hProcessToken))
{
errorInfo = $"OpenProcessToken failed: {Marshal.GetLastWin32Error()}";
return false;
}
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_INCREASE_QUOTA_NAME, out var luid))
{
errorInfo = $"LookupPrivilegeValue failed: {Marshal.GetLastWin32Error()}";
hProcessToken.Dispose();
return false;
}
var tp = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = new()
{
e0 = new LUID_AND_ATTRIBUTES
{
Luid = luid,
Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED
}
}
};
PInvoke.AdjustTokenPrivileges(hProcessToken, false, &tp, 0, null, null);
var lastError = Marshal.GetLastWin32Error();
hProcessToken.Dispose();
if (lastError != 0)
{
errorInfo = $"AdjustTokenPrivileges failed: {lastError}";
return false;
}
retry:
// 2. Get an HWND representing the desktop shell
hwnd = PInvoke.GetShellWindow();
if (hwnd == HWND.Null)
{
errorInfo = "No desktop shell is present.";
return false;
}
// 3. Get the Process ID (PID) of the process associated with that window
_ = PInvoke.GetWindowThreadProcessId(hwnd, &dwPID);
if (dwPID == 0)
{
errorInfo = "Unable to get PID of desktop shell.";
return false;
}
// 4. Open that process
hShellProcess = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION, false, dwPID);
if (hShellProcess == HANDLE.Null)
{
errorInfo = $"Can't open desktop shell process: {Marshal.GetLastWin32Error()}";
return false;
}
if (hwnd != PInvoke.GetShellWindow())
{
PInvoke.CloseHandle(hShellProcess);
goto retry;
}
_ = PInvoke.GetWindowThreadProcessId(hwnd, &dwPID);
if (dwPID != PInvoke.GetProcessId(hShellProcess))
{
PInvoke.CloseHandle(hShellProcess);
goto retry;
}
// 5. Get the access token from that process
if (!PInvoke.OpenProcessToken(hShellProcess, TOKEN_ACCESS_MASK.TOKEN_DUPLICATE, &hShellProcessToken))
{
errorInfo = $"Can't get process token of desktop shell: {Marshal.GetLastWin32Error()}";
goto cleanup;
}
// 6. Make a primary token with that token
var tokenRights = TOKEN_ACCESS_MASK.TOKEN_QUERY | TOKEN_ACCESS_MASK.TOKEN_ASSIGN_PRIMARY |
TOKEN_ACCESS_MASK.TOKEN_DUPLICATE | TOKEN_ACCESS_MASK.TOKEN_ADJUST_DEFAULT |
TOKEN_ACCESS_MASK.TOKEN_ADJUST_SESSIONID;
if (!PInvoke.DuplicateTokenEx(hShellProcessToken, tokenRights, null, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, &hPrimaryToken))
{
errorInfo = $"Can't get primary token: {Marshal.GetLastWin32Error()}";
goto cleanup;
}
// 7. Start the new process with that primary token
fixed (char* appPtr = app)
// Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line
// So we add one more dash before the command line to make command line work correctly
fixed (char* cmdLinePtr = $"- {cmdLine}")
fixed (char* currentDirPtr = currentDir)
{
if (!PInvoke.CreateProcessWithToken(
hPrimaryToken,
// If you need to access content in HKEY_CURRENT_USER, please set loadProfile to true
loadProfile ? CREATE_PROCESS_LOGON_FLAGS.LOGON_WITH_PROFILE : 0,
appPtr,
cmdLinePtr,
// If you do not want to create a window for console app, please set createNoWindow to true
createNoWindow ? PROCESS_CREATION_FLAGS.CREATE_NO_WINDOW : 0,
null,
currentDirPtr,
&si,
&pi))
{
errorInfo = $"CreateProcessWithTokenW failed: {Marshal.GetLastWin32Error()}";
goto cleanup;
}
}
if (pi.hProcess != HANDLE.Null) PInvoke.CloseHandle(pi.hProcess);
if (pi.hThread != HANDLE.Null) PInvoke.CloseHandle(pi.hThread);
if (hShellProcessToken != HANDLE.Null) PInvoke.CloseHandle(hShellProcessToken);
if (hPrimaryToken != HANDLE.Null) PInvoke.CloseHandle(hPrimaryToken);
if (hShellProcess != HANDLE.Null) PInvoke.CloseHandle(hShellProcess);
return true;
cleanup:
if (hShellProcessToken != HANDLE.Null) PInvoke.CloseHandle(hShellProcessToken);
if (hPrimaryToken != HANDLE.Null) PInvoke.CloseHandle(hPrimaryToken);
if (hShellProcess != HANDLE.Null) PInvoke.CloseHandle(hShellProcess);
return false;
}
#endregion
#region Taskbar
public static unsafe void ShowTaskbar()

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
@ -29,13 +30,21 @@ namespace Flow.Launcher.Plugin
void ChangeQuery(string query, bool requery = false);
/// <summary>
/// Restart Flow Launcher
/// Restart Flow Launcher without changing the user privileges.
/// </summary>
void RestartApp();
/// <summary>
/// Restart Flow Launcher as administrator.
/// </summary>
void RestartAppAsAdmin();
/// <summary>
/// Run a shell command
/// </summary>
/// <remarks>
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
/// </remarks>
/// <param name="cmd">The command or program to run</param>
/// <param name="filename">the shell type to run, e.g. powershell.exe</param>
/// <exception cref="FileNotFoundException">Thrown when unable to find the file specified in the command </exception>
@ -637,5 +646,35 @@ namespace Flow.Launcher.Plugin
/// </summary>
/// <returns></returns>
string GetLogDirectory();
/// <summary>
/// Start a process with support for handling administrative privileges
/// </summary>
/// <remarks>
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
/// </remarks>
/// <param name="fileName">File name</param>
/// <param name="workingDirectory">Working directory. If not specified, the current directory will be used</param>
/// <param name="arguments">Optional arguments to pass to the process. If not specified, no arguments will be passed</param>
/// <param name="useShellExecute">Whether to use shell to execute the process</param>
/// <param name="verb">Verb to use when starting the process, e.g. "runas" for elevated permissions. If not specified, no verb will be used.</param>
/// <param name="createNoWindow">Whether to create console window</param>
/// <returns>Whether process is started successfully</returns>
public bool StartProcess(string fileName, string workingDirectory = "", string arguments = "", bool useShellExecute = false, string verb = "", bool createNoWindow = false);
/// <summary>
/// Start a process with support for handling administrative privileges
/// </summary>
/// <remarks>
/// It can help to start a de-elevated process and show user account control dialog when Flow is running as administrator.
/// </remarks>
/// <param name="fileName">File name</param>
/// <param name="workingDirectory">Working directory. If not specified, the current directory will be used</param>
/// <param name="argumentList">Optional argument list to pass to the process. If not specified, no arguments will be passed</param>
/// <param name="useShellExecute">Whether to use shell to execute the process</param>
/// <param name="verb">Verb to use when starting the process, e.g. "runas" for elevated permissions. If not specified, no verb will be used.</param>
/// <param name="createNoWindow">Whether to create console window</param>
/// <returns>Whether process is started successfully</returns>
public bool StartProcess(string fileName, string workingDirectory = "", Collection<string> argumentList = null, bool useShellExecute = false, string verb = "", bool createNoWindow = false);
}
}

View file

@ -1,21 +1,23 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
# Visual Studio Version 18
VisualStudioVersion = 18.4.11519.219 insiders
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}"
ProjectSection(ProjectDependencies) = postProject
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A}
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A}
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C} = {A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
@ -53,8 +55,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
LICENSE = LICENSE
Scripts\post_build.ps1 = Scripts\post_build.ps1
README.md = README.md
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
Settings.XamlStyler = Settings.XamlStyler
SolutionAssemblyInfo.cs = SolutionAssemblyInfo.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Shell", "Plugins\Flow.Launcher.Plugin.Shell\Flow.Launcher.Plugin.Shell.csproj", "{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}"
@ -71,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Plugin
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.WindowsSettings", "Plugins\Flow.Launcher.Plugin.WindowsSettings\Flow.Launcher.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow.Launcher.Command", "Flow.Launcher.Command\Flow.Launcher.Command.csproj", "{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -81,8 +85,19 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU
@ -105,18 +120,6 @@ Global
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|Any CPU
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|Any CPU
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.Build.0 = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU
{DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -286,6 +289,18 @@ Global
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|Any CPU
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|Any CPU
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.Build.0 = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x64.ActiveCfg = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x64.Build.0 = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x86.ActiveCfg = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Debug|x86.Build.0 = Debug|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|Any CPU.Build.0 = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x64.ActiveCfg = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x64.Build.0 = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x86.ActiveCfg = Release|Any CPU
{A9976C5C-B73A-4D29-B654-EF1C0C4C9C8C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -1,5 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -57,6 +59,13 @@ namespace Flow.Launcher
public App()
{
// Check if the application is running as administrator
if (_settings.AlwaysRunAsAdministrator && !Win32Helper.IsAdministrator())
{
RestartApp(true);
return;
}
// Do not use bitmap cache since it can cause WPF second window freezing issue
ShadowAssist.UseBitmapCache = false;
@ -293,12 +302,23 @@ namespace Flow.Launcher
{
try
{
Helper.AutoStartup.CheckIsEnabled(_settings.UseLogonTaskForStartup);
Helper.AutoStartup.CheckIsEnabled(_settings.UseLogonTaskForStartup, _settings.AlwaysRunAsAdministrator);
}
catch (UnauthorizedAccessException)
{
// If it fails for permission, we need to ask the user to restart as administrator
if (API.ShowMsgBox(
API.GetTranslation("runAsAdministratorChangeAndRestart"),
API.GetTranslation("runAsAdministratorChange"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
RestartApp(true);
}
}
catch (Exception e)
{
// but if it fails (permissions, etc) then don't keep retrying
// this also gives the user a visual indication in the Settings widget
// But if it fails for other reasons then do not keep retrying,
// set startup to false to give users a visual indication in the general page
_settings.StartFlowLauncherOnSystemStartup = false;
API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
}
@ -408,6 +428,59 @@ namespace Flow.Launcher
#endregion
#region Restart
/// <summary>
/// Restart the application without changing the user privileges.
/// </summary>
/// <remarks>
/// Since Squirrel does not provide a way to restart the app as administrator,
/// we need to do it manually by starting the update.exe with the runas verb
/// </remarks>
/// <param name="forceAdmin">
/// If true, the application will be restarted as administrator.
/// If false, it will be restarted with the same privileges as the current user.
/// </param>
/// <exception cref="Exception">Thrown when the Update.exe is not found in the expected location</exception>
public static void RestartApp(bool forceAdmin = false)
{
// Restart requires Squirrel's Update.exe to be present in the parent folder,
// it is only published from the project's release pipeline. When debugging without it,
// the project may not restart or just terminates. This is expected.
var startInfo = new ProcessStartInfo
{
FileName = getUpdateExe(),
Arguments = $"--processStartAndWait \"{Constant.ExecutablePath}\"",
UseShellExecute = true,
Verb = Win32Helper.IsAdministrator() || forceAdmin ? "runas" : ""
};
// No need to de-elevate since we are restarting Flow Launcher which cannot bring security risks
Process.Start(startInfo);
Thread.Sleep(500);
Environment.Exit(0);
// Local function
static string getUpdateExe()
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && Path.GetFileName(entryAssembly.Location).Equals("update.exe", StringComparison.OrdinalIgnoreCase) && entryAssembly.Location.IndexOf("app-", StringComparison.OrdinalIgnoreCase) == -1 && entryAssembly.Location.IndexOf("SquirrelTemp", StringComparison.OrdinalIgnoreCase) == -1)
{
return Path.GetFullPath(entryAssembly.Location);
}
entryAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
FileInfo fileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(entryAssembly.Location), "..\\Update.exe"));
if (!fileInfo.Exists)
{
throw new Exception("Update.exe not found, not a Squirrel-installed app?");
}
return fileInfo.FullName;
}
}
#endregion
#region IDisposable
protected virtual void Dispose(bool disposing)

View file

@ -18,18 +18,18 @@ public class AutoStartup
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
public static void CheckIsEnabled(bool useLogonTaskForStartup)
public static void CheckIsEnabled(bool useLogonTaskForStartup, bool alwaysRunAsAdministrator)
{
// We need to check both because if both of them are enabled,
// Hide Flow Launcher on startup will not work since the later one will trigger main window show event
var logonTaskEnabled = CheckLogonTask();
var logonTaskEnabled = CheckLogonTask(alwaysRunAsAdministrator);
var registryEnabled = CheckRegistry();
if (useLogonTaskForStartup)
{
// Enable logon task
if (!logonTaskEnabled)
{
Enable(true);
Enable(true, alwaysRunAsAdministrator);
}
// Disable registry
if (registryEnabled)
@ -42,7 +42,7 @@ public class AutoStartup
// Enable registry
if (!registryEnabled)
{
Enable(false);
Enable(false, alwaysRunAsAdministrator);
}
// Disable logon task
if (logonTaskEnabled)
@ -52,7 +52,7 @@ public class AutoStartup
}
}
private static bool CheckLogonTask()
private static bool CheckLogonTask(bool alwaysRunAsAdministrator)
{
using var taskService = new TaskService();
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
@ -60,22 +60,47 @@ public class AutoStartup
{
try
{
// Check if the action is the same as the current executable path
// If not, we need to unschedule and reschedule the task
if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction)
{
var action = taskAction.ToString().Trim();
var needsRecreation = !action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase)
|| task.Definition.Settings.Priority != ProcessPriorityClass.Normal;
if (needsRecreation)
var action = taskAction.ToString().Trim();
var pathCorrect = action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase);
var runLevelCorrect = CheckRunLevel(task.Definition.Principal.RunLevel, alwaysRunAsAdministrator);
var priorityCorrect = task.Definition.Settings.Priority == ProcessPriorityClass.Normal;
if (Win32Helper.IsAdministrator())
{
UnscheduleLogonTask();
ScheduleLogonTask();
// If path, run level or priority is not correct, we need to unschedule and reschedule the task
if (!pathCorrect || !runLevelCorrect || !priorityCorrect)
{
UnscheduleLogonTask();
ScheduleLogonTask(alwaysRunAsAdministrator);
}
}
else
{
// If run level is not correct, we cannot edit it because we are not administrator
// So we just throw an exception to let the user know
if (!runLevelCorrect)
{
throw new UnauthorizedAccessException("Cannot edit task run level because the app is not running as administrator.");
}
// If run level is correct and path or priority is not correct, we need to unschedule and reschedule the task
if (!pathCorrect || !priorityCorrect)
{
UnscheduleLogonTask();
ScheduleLogonTask(alwaysRunAsAdministrator);
}
}
}
return true;
}
catch (UnauthorizedAccessException e)
{
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
throw; // Throw exception so that App.AutoStartup can show error message
}
catch (Exception e)
{
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
@ -86,6 +111,11 @@ public class AutoStartup
return false;
}
private static bool CheckRunLevel(TaskRunLevel rl, bool alwaysRunAsAdministrator)
{
return alwaysRunAsAdministrator ? rl == TaskRunLevel.Highest : rl != TaskRunLevel.Highest;
}
private static bool CheckRegistry()
{
try
@ -120,16 +150,19 @@ public class AutoStartup
Disable(false);
}
public static void ChangeToViaLogonTask()
public static void ChangeToViaLogonTask(bool alwaysRunAsAdministrator)
{
Disable(false);
Enable(true);
Disable(true); // Remove old logon task so that we can create a new one
Enable(true, alwaysRunAsAdministrator);
}
public static void ChangeToViaRegistry()
{
Disable(true);
Enable(false);
Disable(false); // Remove old registry so that we can create a new one
// We do not need to use alwaysRunAsAdministrator for registry, so we just set false here
Enable(false, false);
}
private static void Disable(bool logonTask)
@ -152,13 +185,13 @@ public class AutoStartup
}
}
private static void Enable(bool logonTask)
private static void Enable(bool logonTask, bool alwaysRunAsAdministrator)
{
try
{
if (logonTask)
{
ScheduleLogonTask();
ScheduleLogonTask(alwaysRunAsAdministrator);
}
else
{
@ -172,14 +205,15 @@ public class AutoStartup
}
}
private static bool ScheduleLogonTask()
private static bool ScheduleLogonTask(bool alwaysRunAsAdministrator)
{
using var td = TaskService.Instance.NewTask();
td.RegistrationInfo.Description = LogonTaskDesc;
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
td.Actions.Add(Constant.ExecutablePath);
if (IsCurrentUserIsAdmin())
// Only if the app is running as administrator, we can set the run level to highest
if (Win32Helper.IsAdministrator() && alwaysRunAsAdministrator)
{
td.Principal.RunLevel = TaskRunLevel.Highest;
}
@ -216,13 +250,6 @@ public class AutoStartup
}
}
private static bool IsCurrentUserIsAdmin()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private static bool UnscheduleRegistry()
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);

View file

@ -70,6 +70,7 @@
<system:String x:Key="pluginStillInitializingSubtitle">Select this result to requery</system:String>
<system:String x:Key="pluginFailedToRespond">{0}: Failed to respond!</system:String>
<system:String x:Key="pluginFailedToRespondSubtitle">Select this result for more info</system:String>
<system:String x:Key="admin">(Admin)</system:String>
<!-- Setting General -->
<system:String x:Key="flowlauncher_settings">Settings</system:String>
@ -185,6 +186,10 @@
<system:String x:Key="showUnknownSourceWarningToolTip">Show warning when installing plugins from unknown sources</system:String>
<system:String x:Key="autoUpdatePlugins">Auto update plugins</system:String>
<system:String x:Key="autoUpdatePluginsToolTip">Automatically check plugin updates and notify if there are any updates available</system:String>
<system:String x:Key="alwaysRunAsAdministrator">Always run as administrator</system:String>
<system:String x:Key="alwaysRunAsAdministratorToolTip">Run Flow Launcher as administrator on startup</system:String>
<system:String x:Key="runAsAdministratorChange">Administrator Mode Change</system:String>
<system:String x:Key="runAsAdministratorChangeAndRestart">Do you want to restart as administrator to apply this change? Otherwise, you will need to run as administrator manually on the next start.</system:String>
<!-- Setting Plugin -->
<system:String x:Key="searchplugin">Search Plugin</system:String>

View file

@ -765,9 +765,13 @@ namespace Flow.Launcher
private void InitializeNotifyIcon()
{
var text = Win32Helper.IsAdministrator() ?
Constant.FlowLauncherFullName + " " + App.API.GetTranslation("admin") :
Constant.FlowLauncherFullName;
_notifyIcon = new NotifyIcon
{
Text = Constant.FlowLauncherFullName,
Text = text,
Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app,
Visible = !_settings.HideNotifyIcon
};

View file

@ -1,10 +1,12 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -73,7 +75,12 @@ namespace Flow.Launcher
_mainVM.ChangeQueryText(query, requery);
}
public void RestartApp()
public void RestartApp() => RestartApp(false);
public void RestartAppAsAdmin() => RestartApp(true);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
private async void RestartApp(bool runAsAdmin)
{
_mainVM.Hide();
@ -85,7 +92,7 @@ namespace Flow.Launcher
// Restart requires Squirrel's Update.exe to be present in the parent folder,
// it is only published from the project's release pipeline. When debugging without it,
// the project may not restart or just terminates. This is expected.
UpdateManager.RestartApp(Constant.ApplicationFileName);
App.RestartApp(runAsAdmin);
}
public void ShowMainWindow() => _mainVM.Show();
@ -151,8 +158,7 @@ namespace Flow.Launcher
{
var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}";
var startInfo = ShellCommand.SetProcessStartInfo(filename, arguments: args, createNoWindow: true);
ShellCommand.Execute(startInfo);
StartProcess(filename, arguments: args, createNoWindow: true);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
@ -450,11 +456,7 @@ namespace Flow.Launcher
{
try
{
Process.Start(new ProcessStartInfo()
{
FileName = uri.AbsoluteUri,
UseShellExecute = true
})?.Dispose();
StartProcess(uri.AbsoluteUri, arguments: string.Empty, useShellExecute: true);
}
catch (Exception e)
{
@ -623,6 +625,85 @@ namespace Flow.Launcher
public string GetLogDirectory() => DataLocation.VersionLogDirectory;
public bool StartProcess(string fileName, string workingDirectory = "", string arguments = "", bool useShellExecute = false, string verb = "", bool createNoWindow = false)
{
try
{
workingDirectory = string.IsNullOrEmpty(workingDirectory) ? Environment.CurrentDirectory : workingDirectory;
// Use command executer to run the process as desktop user if running as admin
if (Win32Helper.IsAdministrator())
{
var result = Win32Helper.RunAsDesktopUser(
Constant.CommandExecutablePath,
Environment.CurrentDirectory,
$"-StartProcess " +
$"-FileName {AddDoubleQuotes(fileName)} " +
$"-WorkingDirectory {AddDoubleQuotes(workingDirectory)} " +
$"-Arguments {AddDoubleQuotes(arguments)} " +
$"-UseShellExecute {useShellExecute} " +
$"-Verb {AddDoubleQuotes(verb)} " +
$"-CreateNoWindow {createNoWindow}",
false,
true, // Do not show the command window
out var errorInfo);
if (!string.IsNullOrEmpty(errorInfo))
{
LogError(ClassName, $"Failed to start process {fileName} with arguments {arguments} under {workingDirectory}: {errorInfo}");
}
return result;
}
var info = new ProcessStartInfo
{
FileName = fileName,
WorkingDirectory = workingDirectory,
Arguments = arguments,
UseShellExecute = useShellExecute,
Verb = verb,
CreateNoWindow = createNoWindow
};
Process.Start(info)?.Dispose();
return true;
}
catch (Exception e)
{
LogException(ClassName, $"Failed to start process {fileName} with arguments {arguments} under {workingDirectory}", e);
return false;
}
}
public bool StartProcess(string fileName, string workingDirectory = "", Collection<string> argumentList = null, bool useShellExecute = false, string verb = "", bool createNoWindow = false) =>
StartProcess(fileName, workingDirectory, JoinArgumentList(argumentList), useShellExecute, verb, createNoWindow);
private static string AddDoubleQuotes(string arg)
{
if (string.IsNullOrEmpty(arg))
return "\"\"";
// If already wrapped in double quotes, return as is
if (arg.Length >= 2 && arg[0] == '"' && arg[^1] == '"')
return arg;
return $"\"{arg}\"";
}
private static string JoinArgumentList(Collection<string> args)
{
if (args == null || args.Count == 0)
return string.Empty;
return string.Join(" ", args.Select(arg =>
{
if (string.IsNullOrEmpty(arg))
return "\"\"";
// Add double quotes
return AddDoubleQuotes(arg);
}));
}
#endregion
#region Private Methods

View file

@ -45,7 +45,7 @@ namespace Flow.Launcher.Resources.Pages
{
if (Settings.UseLogonTaskForStartup)
{
AutoStartup.ChangeToViaLogonTask();
AutoStartup.ChangeToViaLogonTask(Settings.AlwaysRunAsAdministrator);
}
else
{

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core;
@ -43,6 +44,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
get => Settings.StartFlowLauncherOnSystemStartup;
set
{
if (Settings.StartFlowLauncherOnSystemStartup == value) return;
Settings.StartFlowLauncherOnSystemStartup = value;
try
@ -51,7 +54,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
{
if (UseLogonTaskForStartup)
{
AutoStartup.ChangeToViaLogonTask();
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
}
else
{
@ -67,6 +70,13 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
{
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
}
// If we have enabled logon task startup, we need to check if we need to restart the app
// even if we encounter an error while setting the startup method
if (value && UseLogonTaskForStartup)
{
CheckAdminChangeAndAskForRestart();
}
}
}
@ -75,6 +85,8 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
get => Settings.UseLogonTaskForStartup;
set
{
if (UseLogonTaskForStartup == value) return;
Settings.UseLogonTaskForStartup = value;
if (StartFlowLauncherOnSystemStartup)
@ -83,7 +95,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
{
if (value)
{
AutoStartup.ChangeToViaLogonTask();
AutoStartup.ChangeToViaLogonTask(AlwaysRunAsAdministrator);
}
else
{
@ -94,10 +106,61 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
{
App.API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
}
}
}
// If we have enabled logon task startup, we need to check if we need to restart the app
// even if we encounter an error while setting the startup method
if (StartFlowLauncherOnSystemStartup && value)
{
CheckAdminChangeAndAskForRestart();
}
}
}
public bool AlwaysRunAsAdministrator
{
get => Settings.AlwaysRunAsAdministrator;
set
{
if (AlwaysRunAsAdministrator == value) return;
Settings.AlwaysRunAsAdministrator = value;
if (StartFlowLauncherOnSystemStartup && UseLogonTaskForStartup)
{
try
{
AutoStartup.ChangeToViaLogonTask(value);
}
catch (Exception e)
{
App.API.ShowMsg(App.API.GetTranslation("setAutoStartFailed"), e.Message);
}
// If we have enabled logon task startup, we need to check if we need to restart the app
// even if we encounter an error while setting the startup method
CheckAdminChangeAndAskForRestart();
}
}
}
private void CheckAdminChangeAndAskForRestart()
{
// When we change from non-admin to admin, we need to restart the app as administrator to apply the changes
// Under non-administrator, we cannot delete or set the logon task which is run as administrator
if (AlwaysRunAsAdministrator && !Win32Helper.IsAdministrator())
{
if (App.API.ShowMsgBox(
App.API.GetTranslation("runAsAdministratorChangeAndRestart"),
App.API.GetTranslation("runAsAdministratorChange"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
// Restart the app as administrator
App.API.RestartAppAsAdmin();
}
}
}
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");

View file

@ -49,8 +49,8 @@
OnContent="{DynamicResource enable}" />
</ui:SettingsCard>
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
<ui:SettingsCard
Margin="0 4 0 0"
Description="{DynamicResource hideOnStartupToolTip}"
@ -65,6 +65,20 @@
OnContent="{DynamicResource enable}" />
</ui:SettingsCard>
<ui:SettingsCard
Margin="0 4 0 0"
Description="{DynamicResource alwaysRunAsAdministratorToolTip}"
Header="{DynamicResource alwaysRunAsAdministrator}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Glyph="&#xE7EF;" />
</ui:SettingsCard.HeaderIcon>
<ui:ToggleSwitch
IsOn="{Binding AlwaysRunAsAdministrator}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}" />
</ui:SettingsCard>
<ui:SettingsCard Margin="0 12 0 0" Header="{DynamicResource hideFlowLauncherWhenLoseFocus}">
<ui:ToggleSwitch
IsOn="{Binding Settings.HideWhenDeactivated}"

View file

@ -344,11 +344,7 @@ namespace Flow.Launcher.Plugin.Explorer
{
try
{
Process.Start(new ProcessStartInfo()
{
FileName = editorPath,
ArgumentList = { record.FullPath }
});
Context.API.StartProcess(editorPath, arguments: record.FullPath);
return true;
}
catch (Exception e)
@ -377,10 +373,7 @@ namespace Flow.Launcher.Plugin.Explorer
{
try
{
Process.Start(new ProcessStartInfo()
{
FileName = shellPath, WorkingDirectory = record.FullPath
});
Context.API.StartProcess(shellPath, workingDirectory: record.FullPath, arguments: string.Empty);
return true;
}
catch (Exception e)
@ -444,6 +437,7 @@ namespace Flow.Launcher.Plugin.Explorer
Arguments = "srchadmin.dll"
};
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start(psi);
return true;
}
@ -468,6 +462,7 @@ namespace Flow.Launcher.Plugin.Explorer
SubTitle = Localize.plugin_explorer_openwith_subtitle(),
Action = _ =>
{
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start("rundll32.exe", $"{Path.Combine(Environment.SystemDirectory, "shell32.dll")},OpenAs_RunDLL {record.FullPath}");
return true;
},

View file

@ -57,7 +57,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
}
Settings.EverythingInstalledPath = installedPath;
Process.Start(installedPath, "-startup");
Main.Context.API.StartProcess(installedPath, arguments: "-startup");
return true;
}

View file

@ -506,6 +506,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Arguments = Constants.WindowsIndexingOptions
};
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start(psi);
}

View file

@ -1,20 +1,19 @@
using System;
using System.Collections.Generic;
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
{
@ -455,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 +498,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 +512,14 @@ 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" : "" };
Main.StartProcess(Process.Start, info);
_ = Task.Run(() => Main.Context.API.StartProcess(
command,
arguments: string.Empty,
useShellExecute: true,
verb: elevated ? "runas" : ""));
}
internal static bool IfAppCanRunElevated(XmlNode appNode)

View file

@ -1,21 +1,21 @@
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.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 +196,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 +205,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
return result;
}
private void Launch(bool runAsAdmin = false)
{
_ = Task.Run(() => Main.Context.API.StartProcess(
FullPath,
workingDirectory: ParentDirectory,
arguments: string.Empty,
useShellExecute: true,
verb: runAsAdmin ? "runas" : ""));
}
public List<Result> ContextMenus(IPublicAPI api)
{
@ -240,15 +241,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;
},

View file

@ -81,7 +81,7 @@ namespace Flow.Launcher.Plugin.Shell
!c.SpecialKeyState.AltPressed &&
!c.SpecialKeyState.WinPressed;
Execute(Process.Start, PrepareProcessStartInfo(m, runAsAdministrator));
Execute(StartProcess, PrepareProcessStartInfo(m, runAsAdministrator));
return true;
},
CopyText = m
@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Shell
!c.SpecialKeyState.AltPressed &&
!c.SpecialKeyState.WinPressed;
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
return true;
},
CopyText = m.Key
@ -151,7 +151,7 @@ namespace Flow.Launcher.Plugin.Shell
!c.SpecialKeyState.AltPressed &&
!c.SpecialKeyState.WinPressed;
Execute(Process.Start, PrepareProcessStartInfo(cmd, runAsAdministrator));
Execute(StartProcess, PrepareProcessStartInfo(cmd, runAsAdministrator));
return true;
},
CopyText = cmd
@ -176,7 +176,7 @@ namespace Flow.Launcher.Plugin.Shell
!c.SpecialKeyState.AltPressed &&
!c.SpecialKeyState.WinPressed;
Execute(Process.Start, PrepareProcessStartInfo(m.Key, runAsAdministrator));
Execute(StartProcess, PrepareProcessStartInfo(m.Key, runAsAdministrator));
return true;
},
CopyText = m.Key
@ -328,6 +328,17 @@ namespace Flow.Launcher.Plugin.Shell
return info;
}
private static Process StartProcess(ProcessStartInfo info)
{
Context.API.StartProcess(
info.FileName,
workingDirectory: info.WorkingDirectory,
argumentList: info.ArgumentList,
useShellExecute: info.UseShellExecute,
verb: info.Verb);
return null;
}
private void Execute(Func<ProcessStartInfo, Process> startProcess, ProcessStartInfo info)
{
try
@ -464,7 +475,7 @@ namespace Flow.Launcher.Plugin.Shell
Title = Localize.flowlauncher_plugin_cmd_run_as_administrator(),
Action = c =>
{
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
Execute(StartProcess, PrepareProcessStartInfo(selectedResult.Title, true));
return true;
},
IcoPath = "Images/admin.png",

View file

@ -219,6 +219,7 @@ namespace Flow.Launcher.Plugin.Sys
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON);
else
// No need to de-elevate since we already have message box asking for confirmation
Process.Start("shutdown", "/s /t 0");
}
return true;
@ -244,6 +245,7 @@ namespace Flow.Launcher.Plugin.Sys
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON);
else
// No need to de-elevate since we already have message box asking for confirmation
Process.Start("shutdown", "/r /t 0");
}
return true;
@ -271,6 +273,7 @@ namespace Flow.Launcher.Plugin.Sys
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON);
else
// No need to de-elevate since we already have message box asking for confirmation
Process.Start("shutdown", "/r /o /t 0");
}
return true;
@ -335,6 +338,7 @@ namespace Flow.Launcher.Plugin.Sys
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"),
Action = c =>
{
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start("control.exe", "srchadmin.dll");
return true;
}
@ -368,6 +372,7 @@ namespace Flow.Launcher.Plugin.Sys
CopyText = recycleBinFolder,
Action = c =>
{
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start("explorer", recycleBinFolder);
return true;
}

View file

@ -198,6 +198,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
try
{
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start(processStartInfo);
return true;
}
@ -207,6 +208,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
{
processStartInfo.UseShellExecute = true;
processStartInfo.Verb = "runas";
// No need to de-elevate since we are opening windows settings which cannot bring security risks
Process.Start(processStartInfo);
return true;
}