Compare commits

...

179 commits

Author SHA1 Message Date
Jack Ye
1df228510b
Merge 6b2b7ac304 into 24f6c90f72 2026-03-10 14:06:39 +08:00
Jack Ye
24f6c90f72
Merge pull request #4333 from Flow-Launcher/dependabot/nuget/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Svg.Skia-3.5.0
Some checks are pending
Build / build (push) Waiting to run
Bump Svg.Skia from 3.4.1 to 3.5.0
2026-03-10 11:52:19 +08:00
dependabot[bot]
3e493263c3
Bump Svg.Skia from 3.4.1 to 3.5.0
---
updated-dependencies:
- dependency-name: Svg.Skia
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 23:05:08 +00:00
Jack Ye
916b4ff091
Merge pull request #4312 from Flow-Launcher/Theme_Enhancement
Some checks are pending
Build / build (push) Waiting to run
Improve code quality
2026-03-09 15:15:41 +08:00
Jack Ye
dad8f3e695
Merge branch 'dev' into Theme_Enhancement 2026-03-09 12:05:48 +08:00
Jeremy Wu
7a46fac41a
Merge pull request #4329 from Flow-Launcher/merge_back_2_1_1
Merge v2.1.1 back to dev
2026-03-09 15:00:27 +11:00
Jeremy
79f3bfab96 merge back v2.1.1 from master to dev 2026-03-09 14:50:29 +11:00
Jeremy Wu
0d9cc7ef83
Release 2.1.1 (#4327)
Some checks failed
Publish Default Plugins / publish (push) Has been cancelled
Build / build (push) Has been cancelled
2026-03-09 11:23:36 +08:00
Jack Ye
0ddcc5c1c6
Merge pull request #4319 from Flow-Launcher/FreezeResources1
Freeze SolidColorBrush to improve performance
2026-03-09 11:11:59 +08:00
Jack251970
7766d7053d Rename GetFreezeSolidColorBrush to GetFrozenSolidColorBrush
Renamed the method GetFreezeSolidColorBrush to GetFrozenSolidColorBrush in ThemeHelper.cs for improved naming consistency. Updated all references in Theme.cs accordingly. No changes to functionality.
2026-03-08 23:49:09 +08:00
Jack251970
36e15d3809 Refactor: move CopyStyle to ThemeHelper class
Moved the CopyStyle method from Theme to a new static ThemeHelper class for better code organization and reusability. Updated all references in Theme to use ThemeHelper.CopyStyle. Added ThemeHelper.cs and necessary using directives.
2026-03-08 23:48:10 +08:00
Jack251970
ed14c45fd2 Refactor brush creation with ThemeHelper utility
Introduce ThemeHelper.GetFreezeSolidColorBrush to centralize and reuse SolidColorBrush creation and freezing logic. Refactor Theme.cs to use this helper, improving code clarity and maintainability. Also, use Brushes.Transparent directly where appropriate.
2026-03-08 23:46:08 +08:00
Jack251970
9abf136ea2 Improve SolidColorBrush usage and property checks in Theme
Refactor background brush creation to assign, freeze, and reuse
SolidColorBrush instances for better WPF performance. Replace
string-based property checks with type-safe Control.BackgroundProperty
comparisons for improved robustness.
2026-03-04 12:48:48 +08:00
Jack251970
6566871b15 Refactor theme blur and dark mode handling logic
Refactored Theme.cs to improve theme blur and dark mode handling:
- Replaced static IsBlurTheme() with IsThemeBlurEnabled(dict) for per-theme blur detection.
- Updated all blur checks to use the new method.
- Improved code style, readability, and comments.
- Moved system dark mode registry check to only run when needed.
- Ensured blur and dark mode are applied based on theme and user/system preferences.
2026-03-02 18:03:24 +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
33 changed files with 916 additions and 192 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

@ -124,8 +124,8 @@ namespace Flow.Launcher.Core.Resource
try
{
// Load a ResourceDictionary for the specified theme.
var themeName = _settings.Theme;
var dict = GetThemeResourceDictionary(themeName);
var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(theme);
// Apply font settings to the theme resource.
ApplyFontSettings(dict);
@ -292,10 +292,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
var fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
var fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
var fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
var fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -307,10 +307,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
var fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
var fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
var fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
var fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -395,7 +395,7 @@ namespace Flow.Launcher.Core.Resource
public List<ThemeData> GetAvailableThemes()
{
List<ThemeData> themes = new List<ThemeData>();
var themes = new List<ThemeData>();
foreach (var themeDirectory in _themeDirectories)
{
var filePaths = Directory
@ -410,8 +410,7 @@ namespace Flow.Launcher.Core.Resource
public bool ChangeTheme(string theme = null)
{
if (string.IsNullOrEmpty(theme))
theme = _settings.Theme;
if (string.IsNullOrEmpty(theme)) theme = _settings.Theme;
string path = GetThemePath(theme);
try
@ -426,13 +425,14 @@ namespace Flow.Launcher.Core.Resource
_settings.Theme = theme;
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
// Always allow re-loading default theme, in case of failure of switching to a new theme from default theme
if (_oldTheme != theme || theme == Constant.DefaultTheme)
{
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
}
BlurEnabled = IsBlurTheme();
// Check if blur is enabled
BlurEnabled = Win32Helper.IsBackdropSupported() && IsThemeBlurEnabled(resourceDict);
// Apply blur and drop shadow effect so that we do not need to call it again
_ = RefreshFrameAsync();
@ -667,21 +667,21 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
var hasBlur = IsThemeBlurEnabled(dict);
if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported())
{
// If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
if (backdropType is BackdropTypes.Mica or BackdropTypes.MicaAlt)
{
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Control.BackgroundProperty));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0))));
}
else if (backdropType == BackdropTypes.Acrylic)
{
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Control.BackgroundProperty));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, Brushes.Transparent));
}
// For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness.
//(This is to avoid issues when the window is forcibly changed to a rectangular shape during snap scenarios.)
var cornerRadiusSetter = windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty);
@ -689,7 +689,7 @@ namespace Flow.Launcher.Core.Resource
cornerRadiusSetter.Value = new CornerRadius(0);
else
windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0)));
// Apply the blur effect
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
ColorizeWindow(theme, backdropType);
@ -798,12 +798,12 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["WindowBorderStyle"] is Style originalStyle)
{
// Copy the original style, including the base style if it exists
CopyStyle(originalStyle, previewStyle);
ThemeHelper.CopyStyle(originalStyle, previewStyle);
}
// Apply background color (remove transparency in color)
Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor)));
var backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(backgroundColor)));
// The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues).
// The non-blur theme retains the previously set WindowBorderStyle.
@ -817,21 +817,6 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
}
private void CopyStyle(Style originalStyle, Style targetStyle)
{
// If the style is based on another style, copy the base style first
if (originalStyle.BasedOn != null)
{
CopyStyle(originalStyle.BasedOn, targetStyle);
}
// Copy the setters from the original style
foreach (var setter in originalStyle.Setters.OfType<Setter>())
{
targetStyle.Setters.Add(new Setter(setter.Property, setter.Value));
}
}
private void ColorizeWindow(string theme, BackdropTypes backdropType)
{
var dict = GetThemeResourceDictionary(theme);
@ -841,20 +826,16 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
var hasBlur = IsThemeBlurEnabled(dict);
// SystemBG value check (Auto, Light, Dark)
string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
var systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
// Check the user's ColorScheme setting
string colorScheme = _settings.ColorScheme;
// Check system dark mode setting (read AppsUseLightTheme value)
int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
bool isSystemDark = themeValue == 0;
var colorScheme = _settings.ColorScheme;
// Final decision on whether to use dark mode
bool useDarkMode = false;
var useDarkMode = false;
// If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value
if (systemBG == "Dark")
@ -869,11 +850,20 @@ namespace Flow.Launcher.Core.Resource
{
// If systemBG is "Auto", decide based on ColorScheme
if (colorScheme == "Dark")
{
useDarkMode = true;
}
else if (colorScheme == "Light")
{
useDarkMode = false;
}
else
{
// Check system dark mode setting (read AppsUseLightTheme value)
var themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
var isSystemDark = themeValue == 0;
useDarkMode = isSystemDark; // Auto (based on system setting)
}
}
// Apply DWM Dark Mode
@ -915,25 +905,20 @@ namespace Flow.Launcher.Core.Resource
else
{
// Only set the background to transparent if the theme supports blur
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
if (backdropType is BackdropTypes.Mica or BackdropTypes.MicaAlt)
{
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
mainWindow.Background = ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0));
}
else
{
mainWindow.Background = new SolidColorBrush(selectedBG);
mainWindow.Background = ThemeHelper.GetFrozenSolidColorBrush(selectedBG);
}
}
}
private static bool IsBlurTheme()
private static bool IsThemeBlurEnabled(ResourceDictionary dict)
{
if (!Win32Helper.IsBackdropSupported()) // Windows 11 미만이면 무조건 false
return false;
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
return resource is bool b && b;
return dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool enabled && enabled;
}
#endregion

View file

@ -0,0 +1,30 @@
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace Flow.Launcher.Core.Resource;
public static class ThemeHelper
{
public static void CopyStyle(Style originalStyle, Style targetStyle)
{
// If the style is based on another style, copy the base style first
if (originalStyle.BasedOn != null)
{
CopyStyle(originalStyle.BasedOn, targetStyle);
}
// Copy the setters from the original style
foreach (var setter in originalStyle.Setters.OfType<Setter>())
{
targetStyle.Setters.Add(new Setter(setter.Property, setter.Value));
}
}
public static SolidColorBrush GetFrozenSolidColorBrush(Color color)
{
var brush = new SolidColorBrush(color);
brush.Freeze();
return brush;
}
}

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

@ -110,7 +110,7 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
<PackageReference Include="Svg.Skia" Version="3.4.1" />
<PackageReference Include="Svg.Skia" Version="3.5.0" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>

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;
}

View file

@ -1,4 +1,4 @@
version: '2.1.0.{build}'
version: '2.1.1.{build}'
# Do not build on tags because we create a release on merge to master. Otherwise will upload artifacts twice changing the hash, as well as triggering duplicate GitHub release action & NuGet deployments.
skip_tags: true