33
44namespace SwitchifyPc . Windows . Startup ;
55
6+ public sealed record StartupTaskQueryDetails (
7+ string ? LastRunResult ,
8+ bool ? Enabled ) ;
9+
610public sealed class WindowsStartupTask : IStartupTask
711{
812 public const string StartupTaskName = "Switchify PC" ;
@@ -19,22 +23,21 @@ public WindowsStartupTask(CommandRunner? commandRunner = null, Action<string>? w
1923
2024 public async Task < StartupTaskSnapshot > GetAsync ( string taskName )
2125 {
22- string ? lastRunResult = await QueryLastRunResultAsync ( taskName ) ;
26+ StartupTaskQueryDetails details = await QueryTaskDetailsAsync ( taskName ) ;
2327
2428 try
2529 {
2630 CommandResult result = await commandRunner ( "schtasks.exe" , [ "/Query" , "/TN" , taskName , "/XML" ] ) ;
27- StartupTaskSnapshot snapshot = ParseTaskXml ( result . Stdout , lastRunResult ) ;
28- return snapshot with { LastRunResult = lastRunResult } ;
31+ return ParseTaskXml ( result . Stdout , details . LastRunResult , details . Enabled ) ;
2932 }
3033 catch ( Exception error ) when ( IsMissingTaskError ( error ) )
3134 {
32- return new StartupTaskSnapshot ( false , false , null , [ ] , lastRunResult ) ;
35+ return new StartupTaskSnapshot ( false , false , null , [ ] , details . LastRunResult ) ;
3336 }
3437 catch ( Exception error ) when ( error is System . Xml . XmlException or InvalidOperationException )
3538 {
3639 warn ( "Switchify startup task could not be read." ) ;
37- return new StartupTaskSnapshot ( true , false , null , [ ] , lastRunResult ) ;
40+ return new StartupTaskSnapshot ( true , false , null , [ ] , details . LastRunResult ) ;
3841 }
3942 }
4043
@@ -81,7 +84,7 @@ public static string StartupCommandFor(string executablePath, IReadOnlyList<stri
8184 return string . Join ( " " , new [ ] { $ "\" { executablePath } \" " } . Concat ( args ) ) ;
8285 }
8386
84- public static StartupTaskSnapshot ParseTaskXml ( string xml , string ? lastRunResult = null )
87+ public static StartupTaskSnapshot ParseTaskXml ( string xml , string ? lastRunResult = null , bool ? enabledOverride = null )
8588 {
8689 XDocument document = XDocument . Parse ( xml ) ;
8790 XElement root = document . Root ?? throw new InvalidOperationException ( "Task XML is empty." ) ;
@@ -95,41 +98,64 @@ public static StartupTaskSnapshot ParseTaskXml(string xml, string? lastRunResult
9598 command = splitCommand ;
9699 arguments = splitArguments ;
97100 }
101+ else
102+ {
103+ command = UnquoteCommand ( command ) ;
104+ }
98105
99106 return new StartupTaskSnapshot (
100107 Exists : true ,
101- Enabled : string . Equals ( enabledText , "true" , StringComparison . OrdinalIgnoreCase ) ,
108+ Enabled : enabledOverride ?? ( enabledText is null || string . Equals ( enabledText , "true" , StringComparison . OrdinalIgnoreCase ) ) ,
102109 ExecutablePath : string . IsNullOrWhiteSpace ( command ) ? null : command ,
103110 Arguments : arguments ,
104111 LastRunResult : lastRunResult ) ;
105112 }
106113
107114 public static string ? ParseLastRunResult ( string output )
108115 {
116+ return ParseTaskDetails ( output ) . LastRunResult ;
117+ }
118+
119+ public static StartupTaskQueryDetails ParseTaskDetails ( string output )
120+ {
121+ string ? lastRunResult = null ;
122+ bool ? enabled = null ;
123+
109124 foreach ( string line in output . Split ( [ "\r \n " , "\n " ] , StringSplitOptions . RemoveEmptyEntries ) )
110125 {
111126 int separator = line . IndexOf ( ':' , StringComparison . Ordinal ) ;
112127 if ( separator <= 0 ) continue ;
113128
114129 string key = line [ ..separator ] . Trim ( ) ;
115- if ( ! key . Equals ( "Last Result" , StringComparison . OrdinalIgnoreCase ) ) continue ;
116-
117- return line [ ( separator + 1 ) ..] . Trim ( ) ;
130+ string value = line [ ( separator + 1 ) ..] . Trim ( ) ;
131+ if ( key . Equals ( "Last Result" , StringComparison . OrdinalIgnoreCase ) )
132+ {
133+ lastRunResult = value ;
134+ }
135+ else if ( key . Equals ( "Scheduled Task State" , StringComparison . OrdinalIgnoreCase ) )
136+ {
137+ enabled = value switch
138+ {
139+ _ when value . Equals ( "Enabled" , StringComparison . OrdinalIgnoreCase ) => true ,
140+ _ when value . Equals ( "Disabled" , StringComparison . OrdinalIgnoreCase ) => false ,
141+ _ => enabled
142+ } ;
143+ }
118144 }
119145
120- return null ;
146+ return new StartupTaskQueryDetails ( lastRunResult , enabled ) ;
121147 }
122148
123- private async Task < string ? > QueryLastRunResultAsync ( string taskName )
149+ private async Task < StartupTaskQueryDetails > QueryTaskDetailsAsync ( string taskName )
124150 {
125151 try
126152 {
127153 CommandResult result = await commandRunner ( "schtasks.exe" , [ "/Query" , "/TN" , taskName , "/FO" , "LIST" , "/V" ] ) ;
128- return ParseLastRunResult ( result . Stdout ) ;
154+ return ParseTaskDetails ( result . Stdout ) ;
129155 }
130156 catch
131157 {
132- return null ;
158+ return new StartupTaskQueryDetails ( null , null ) ;
133159 }
134160 }
135161
@@ -169,6 +195,14 @@ private static bool TrySplitCombinedCommand(string? command, out string? executa
169195 return true ;
170196 }
171197
198+ private static string ? UnquoteCommand ( string ? command )
199+ {
200+ if ( string . IsNullOrWhiteSpace ( command ) ) return command ;
201+ return command . Length >= 2 && command . StartsWith ( '"' ) && command . EndsWith ( '"' )
202+ ? command [ 1 ..^ 1 ]
203+ : command ;
204+ }
205+
172206 private static bool IsMissingTaskError ( Exception error )
173207 {
174208 string output = error . Message . ToLowerInvariant ( ) ;
0 commit comments