Skip to content

Commit c143aa4

Browse files
authored
Merge pull request #3218 from Jack251970/logon_task
Support Logon Task for Faster Startup Experience
2 parents 9aadc3a + fe48427 commit c143aa4

File tree

7 files changed

+198
-9
lines changed

7 files changed

+198
-9
lines changed

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ public SearchPrecisionScore QuerySearchPrecision
256256
public bool EnableUpdateLog { get; set; }
257257

258258
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
259+
public bool UseLogonTaskForStartup { get; set; } = false;
259260
public bool HideOnStartup { get; set; } = true;
260261
bool _hideNotifyIcon { get; set; }
261262
public bool HideNotifyIcon

Flow.Launcher/App.xaml.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,14 @@ private void AutoStartup()
136136
{
137137
try
138138
{
139-
Helper.AutoStartup.Enable();
139+
if (_settings.UseLogonTaskForStartup)
140+
{
141+
Helper.AutoStartup.EnableViaLogonTask();
142+
}
143+
else
144+
{
145+
Helper.AutoStartup.EnableViaRegistry();
146+
}
140147
}
141148
catch (Exception e)
142149
{

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
105105
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
106106
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
107+
<PackageReference Include="TaskScheduler" Version="2.11.0" />
107108
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
108109
</ItemGroup>
109110

Flow.Launcher/Helper/AutoStartup.cs

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Security.Principal;
25
using Flow.Launcher.Infrastructure;
36
using Flow.Launcher.Infrastructure.Logger;
47
using Microsoft.Win32;
8+
using Microsoft.Win32.TaskScheduler;
59

610
namespace Flow.Launcher.Helper;
711

812
public class AutoStartup
913
{
1014
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
15+
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
16+
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
1117

1218
public static bool IsEnabled
1319
{
1420
get
1521
{
22+
// Check if logon task is enabled
23+
if (CheckLogonTask())
24+
{
25+
return true;
26+
}
27+
28+
// Check if registry is enabled
1629
try
1730
{
1831
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
@@ -28,12 +41,74 @@ public static bool IsEnabled
2841
}
2942
}
3043

31-
public static void Disable()
44+
private static bool CheckLogonTask()
45+
{
46+
using var taskService = new TaskService();
47+
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
48+
if (task != null)
49+
{
50+
try
51+
{
52+
// Check if the action is the same as the current executable path
53+
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
54+
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
55+
{
56+
UnscheduleLogonTask();
57+
ScheduleLogonTask();
58+
}
59+
60+
return true;
61+
}
62+
catch (Exception e)
63+
{
64+
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
65+
}
66+
}
67+
68+
return false;
69+
}
70+
71+
public static void DisableViaLogonTaskAndRegistry()
72+
{
73+
Disable(true);
74+
Disable(false);
75+
}
76+
77+
public static void EnableViaLogonTask()
78+
{
79+
Enable(true);
80+
}
81+
82+
public static void EnableViaRegistry()
83+
{
84+
Enable(false);
85+
}
86+
87+
public static void ChangeToViaLogonTask()
88+
{
89+
Disable(false);
90+
Enable(true);
91+
}
92+
93+
public static void ChangeToViaRegistry()
94+
{
95+
Disable(true);
96+
Enable(false);
97+
}
98+
99+
private static void Disable(bool logonTask)
32100
{
33101
try
34102
{
35-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
36-
key?.DeleteValue(Constant.FlowLauncher, false);
103+
if (logonTask)
104+
{
105+
UnscheduleLogonTask();
106+
}
107+
else
108+
{
109+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
110+
key?.DeleteValue(Constant.FlowLauncher, false);
111+
}
37112
}
38113
catch (Exception e)
39114
{
@@ -42,17 +117,74 @@ public static void Disable()
42117
}
43118
}
44119

45-
internal static void Enable()
120+
private static void Enable(bool logonTask)
46121
{
47122
try
48123
{
49-
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
50-
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
124+
if (logonTask)
125+
{
126+
ScheduleLogonTask();
127+
}
128+
else
129+
{
130+
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
131+
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
132+
}
51133
}
52134
catch (Exception e)
53135
{
54136
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
55137
throw;
56138
}
57139
}
140+
141+
private static bool ScheduleLogonTask()
142+
{
143+
using var td = TaskService.Instance.NewTask();
144+
td.RegistrationInfo.Description = LogonTaskDesc;
145+
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
146+
td.Actions.Add(Constant.ExecutablePath);
147+
148+
if (IsCurrentUserIsAdmin())
149+
{
150+
td.Principal.RunLevel = TaskRunLevel.Highest;
151+
}
152+
153+
td.Settings.StopIfGoingOnBatteries = false;
154+
td.Settings.DisallowStartIfOnBatteries = false;
155+
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
156+
157+
try
158+
{
159+
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
160+
return true;
161+
}
162+
catch (Exception e)
163+
{
164+
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
165+
return false;
166+
}
167+
}
168+
169+
private static bool UnscheduleLogonTask()
170+
{
171+
using var taskService = new TaskService();
172+
try
173+
{
174+
taskService.RootFolder.DeleteTask(LogonTaskName);
175+
return true;
176+
}
177+
catch (Exception e)
178+
{
179+
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
180+
return false;
181+
}
182+
}
183+
184+
private static bool IsCurrentUserIsAdmin()
185+
{
186+
var identity = WindowsIdentity.GetCurrent();
187+
var principal = new WindowsPrincipal(identity);
188+
return principal.IsInRole(WindowsBuiltInRole.Administrator);
189+
}
58190
}

Flow.Launcher/Languages/en.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
<system:String x:Key="portableMode">Portable Mode</system:String>
4848
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
4949
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
50+
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
51+
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
5052
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
5153
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
5254
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>

Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,20 @@ public bool StartFlowLauncherOnSystemStartup
4242
try
4343
{
4444
if (value)
45-
AutoStartup.Enable();
45+
{
46+
if (UseLogonTaskForStartup)
47+
{
48+
AutoStartup.EnableViaLogonTask();
49+
}
50+
else
51+
{
52+
AutoStartup.EnableViaRegistry();
53+
}
54+
}
4655
else
47-
AutoStartup.Disable();
56+
{
57+
AutoStartup.DisableViaLogonTaskAndRegistry();
58+
}
4859
}
4960
catch (Exception e)
5061
{
@@ -54,6 +65,34 @@ public bool StartFlowLauncherOnSystemStartup
5465
}
5566
}
5667

68+
public bool UseLogonTaskForStartup
69+
{
70+
get => Settings.UseLogonTaskForStartup;
71+
set
72+
{
73+
Settings.UseLogonTaskForStartup = value;
74+
75+
if (StartFlowLauncherOnSystemStartup)
76+
{
77+
try
78+
{
79+
if (UseLogonTaskForStartup)
80+
{
81+
AutoStartup.ChangeToViaLogonTask();
82+
}
83+
else
84+
{
85+
AutoStartup.ChangeToViaRegistry();
86+
}
87+
}
88+
catch (Exception e)
89+
{
90+
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
91+
e.Message);
92+
}
93+
}
94+
}
95+
}
5796

5897
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
5998
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");

Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
OnContent="{DynamicResource enable}" />
3737
</cc:Card>
3838

39+
<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
40+
<ui:ToggleSwitch
41+
IsOn="{Binding UseLogonTaskForStartup}"
42+
OffContent="{DynamicResource disable}"
43+
OnContent="{DynamicResource enable}" />
44+
</cc:Card>
45+
3946
<cc:Card
4047
Title="{DynamicResource hideOnStartup}"
4148
Icon="&#xed1a;"

0 commit comments

Comments
 (0)