@@ -63,50 +63,22 @@ public App()
6363 protected override void OnStartup ( StartupEventArgs e )
6464 {
6565 // Parse command line arguments early so special startup modes can short-circuit normal flow.
66- bool startMinimized = false ;
67- bool isAutostart = false ;
68- bool isSmokeTest = false ;
69- bool registerLaunchTask = false ;
70- bool launchedViaTask = false ;
71- #if DEBUG
72- bool isTestMode = false ;
73- #endif
66+ var startupMode = StartupMode . Parse ( e . Args ) ;
7467 bool effectiveStartMinimized = false ;
7568 ApplicationSettingsModel ? loadedSettings = null ;
7669
77- foreach ( var arg in e . Args )
70+ effectiveStartMinimized = startupMode . StartMinimized ;
71+
72+ if ( startupMode . IsSmokeTest )
7873 {
79- switch ( arg . ToLowerInvariant ( ) )
80- {
81- #if DEBUG
82- case "--test" :
83- isTestMode = true ;
84- break ;
85- #endif
86- case "--smoke-test" :
87- isSmokeTest = true ;
88- break ;
89- case "--start-minimized" :
90- startMinimized = true ;
91- break ;
92- case "--autostart" :
93- isAutostart = true ;
94- break ;
95- case "--startup" : // Alternative startup argument
96- isAutostart = true ;
97- startMinimized = true ;
98- break ;
99- case RegisterLaunchTaskArgument :
100- registerLaunchTask = true ;
101- break ;
102- case LaunchedViaTaskArgument :
103- launchedViaTask = true ;
104- break ;
105- }
74+ var smokeLogger = this . ServiceProvider . GetRequiredService < ILogger < App > > ( ) ;
75+ var smokeTestResult = this . RunSmokeTestWithTimeout ( smokeLogger , TimeSpan . FromSeconds ( 10 ) ) ;
76+ Environment . ExitCode = smokeTestResult ;
77+ this . Shutdown ( smokeTestResult ) ;
78+ Environment . Exit ( smokeTestResult ) ;
79+ return ;
10680 }
10781
108- effectiveStartMinimized = startMinimized ;
109-
11082 // Set up global exception handlers first
11183 AppDomain . CurrentDomain . UnhandledException += this . OnUnhandledException ;
11284 this . DispatcherUnhandledException += this . OnDispatcherUnhandledException ;
@@ -130,14 +102,14 @@ protected override void OnStartup(StartupEventArgs e)
130102 }
131103 else
132104 {
133- if ( launchedViaTask )
105+ if ( startupMode . LaunchedViaTask )
134106 {
135107 logger . LogError ( "Application was launched via managed task marker but is still not elevated." ) ;
136108 }
137109#if DEBUG
138- else if ( ! isSmokeTest && ! isTestMode )
110+ else if ( ! startupMode . IsTestMode )
139111#else
140- else if ( ! isSmokeTest )
112+ else
141113#endif
142114 {
143115 var launchedElevatedInstance = Task . Run ( async ( ) => await elevatedTaskService . TryRunLaunchTaskAsync ( ) ) . GetAwaiter ( ) . GetResult ( ) ;
@@ -148,7 +120,7 @@ protected override void OnStartup(StartupEventArgs e)
148120 return ;
149121 }
150122
151- if ( ! registerLaunchTask )
123+ if ( ! startupMode . RegisterLaunchTask )
152124 {
153125 logger . LogInformation ( "Managed elevated launch task is unavailable. Requesting one-time elevation to bootstrap persistent launch." ) ;
154126 var restartInitiated = Task . Run ( async ( ) => await elevationService . RestartWithElevation ( new [ ] { RegisterLaunchTaskArgument } ) ) . GetAwaiter ( ) . GetResult ( ) ;
@@ -160,43 +132,38 @@ protected override void OnStartup(StartupEventArgs e)
160132 }
161133
162134#if DEBUG
163- if ( ! isSmokeTest && ! isTestMode )
135+ if ( ! startupMode . IsTestMode )
164136#else
165- if ( ! isSmokeTest )
137+ if ( true )
166138#endif
167139 {
168140 logger . LogError ( "ThreadPilot requires administrator privileges and cannot continue without elevation." ) ;
169141 this . ShowElevationRequiredMessage ( ) ;
170142 this . Shutdown ( 1 ) ;
171143 return ;
172144 }
173-
174- logger . LogWarning ( "Application is running without administrator privileges in smoke test mode." ) ;
175145 }
176146
177147 // Enforce single-instance after elevation bootstrap logic to avoid mutex races during handoff.
178- if ( ! isSmokeTest )
148+ bool createdNew ;
149+ this . singleInstanceMutex = new Mutex ( initiallyOwned : true , name : "Global\\ ThreadPilot_SingleInstance" , createdNew : out createdNew ) ;
150+ if ( ! createdNew )
179151 {
180- bool createdNew ;
181- this . singleInstanceMutex = new Mutex ( initiallyOwned : true , name : "Global\\ ThreadPilot_SingleInstance" , createdNew : out createdNew ) ;
182- if ( ! createdNew )
183- {
184- System . Windows . MessageBox . Show (
185- "ThreadPilot is already running." ,
186- "Instance already open" ,
187- MessageBoxButton . OK ,
188- MessageBoxImage . Information ) ;
152+ System . Windows . MessageBox . Show (
153+ "ThreadPilot is already running." ,
154+ "Instance already open" ,
155+ MessageBoxButton . OK ,
156+ MessageBoxImage . Information ) ;
189157
190- this . Shutdown ( ) ;
191- return ;
192- }
158+ this . Shutdown ( ) ;
159+ return ;
193160 }
194161
195162 base . OnStartup ( e ) ;
196163
197164 // Check for test mode
198165#if DEBUG
199- if ( isTestMode )
166+ if ( startupMode . IsTestMode )
200167 {
201168 // Run in console test mode
202169 AllocConsole ( ) ;
@@ -209,13 +176,6 @@ protected override void OnStartup(StartupEventArgs e)
209176 }
210177#endif
211178
212- if ( isSmokeTest )
213- {
214- var smokeTestResult = Task . Run ( async ( ) => await this . RunSmokeTestAsync ( logger ) ) . GetAwaiter ( ) . GetResult ( ) ;
215- this . Shutdown ( smokeTestResult ) ;
216- return ;
217- }
218-
219179 try
220180 {
221181 var settingsService = this . ServiceProvider . GetRequiredService < IApplicationSettingsService > ( ) ;
@@ -226,7 +186,7 @@ protected override void OnStartup(StartupEventArgs e)
226186 var settings = settingsService . Settings ;
227187 loadedSettings = settings ;
228188 localizationService . ApplyLanguage ( settings . Language ) ;
229- effectiveStartMinimized = startMinimized || settings . StartMinimized ;
189+ effectiveStartMinimized = startupMode . StartMinimized || settings . StartMinimized ;
230190 var useDarkTheme = settings . HasUserThemePreference
231191 ? settings . UseDarkTheme
232192 : themeService . GetSystemUsesDarkTheme ( ) ;
@@ -258,7 +218,7 @@ protected override void OnStartup(StartupEventArgs e)
258218 throw new InvalidOperationException ( "MainWindow could not be created" ) ;
259219 }
260220
261- var startupWindowBehavior = StartupWindowBehavior . Resolve ( isAutostart , effectiveStartMinimized ) ;
221+ var startupWindowBehavior = StartupWindowBehavior . Resolve ( startupMode . IsAutostart , effectiveStartMinimized ) ;
262222 var showStartupSuggestion = loadedSettings != null
263223 && StartupMinimizedSuggestionPolicy . ShouldShow ( loadedSettings , startupWindowBehavior ) ;
264224 mainWindow . ConfigureStartupMode (
@@ -301,16 +261,33 @@ protected override void OnStartup(StartupEventArgs e)
301261 }
302262 }
303263
304- private async Task < int > RunSmokeTestAsync ( ILogger logger )
264+ private int RunSmokeTestWithTimeout ( ILogger logger , TimeSpan timeout )
265+ {
266+ var smokeTestTask = Task . Run ( ( ) => this . RunSmokeTest ( logger ) ) ;
267+ if ( smokeTestTask . Wait ( timeout ) )
268+ {
269+ return smokeTestTask . GetAwaiter ( ) . GetResult ( ) ;
270+ }
271+
272+ logger . LogError ( "ThreadPilot smoke test timed out after {TimeoutSeconds} seconds" , timeout . TotalSeconds ) ;
273+ return 2 ;
274+ }
275+
276+ private int RunSmokeTest ( ILogger logger )
305277 {
306278 try
307279 {
308280 logger . LogInformation ( "Starting ThreadPilot smoke test" ) ;
309281
310- var settingsService = this . ServiceProvider . GetRequiredService < IApplicationSettingsService > ( ) ;
311- await settingsService . LoadSettingsAsync ( ) . ConfigureAwait ( false ) ;
312- _ = this . ServiceProvider . GetRequiredService < ProcessViewModel > ( ) ;
313- _ = this . ServiceProvider . GetRequiredService < PowerPlanViewModel > ( ) ;
282+ _ = this . ServiceProvider . GetRequiredService < ILoggerFactory > ( ) ;
283+ _ = this . ServiceProvider . GetRequiredService < IApplicationSettingsService > ( ) ;
284+ _ = this . ServiceProvider . GetRequiredService < IThemeService > ( ) ;
285+ _ = this . ServiceProvider . GetRequiredService < ILocalizationService > ( ) ;
286+
287+ if ( ! System . IO . Directory . Exists ( AppContext . BaseDirectory ) )
288+ {
289+ throw new InvalidOperationException ( "Application base directory was not found." ) ;
290+ }
314291
315292 logger . LogInformation ( "ThreadPilot smoke test completed successfully" ) ;
316293 return 0 ;
@@ -322,6 +299,55 @@ private async Task<int> RunSmokeTestAsync(ILogger logger)
322299 }
323300 }
324301
302+ private readonly struct StartupMode
303+ {
304+ public bool StartMinimized { get ; init ; }
305+
306+ public bool IsAutostart { get ; init ; }
307+
308+ public bool IsSmokeTest { get ; init ; }
309+
310+ public bool RegisterLaunchTask { get ; init ; }
311+
312+ public bool LaunchedViaTask { get ; init ; }
313+
314+ public bool IsTestMode { get ; init ; }
315+
316+ public static StartupMode Parse ( IEnumerable < string > args )
317+ {
318+ var mode = default ( StartupMode ) ;
319+ foreach ( var arg in args )
320+ {
321+ switch ( arg . ToLowerInvariant ( ) )
322+ {
323+ case "--test" :
324+ mode = mode with { IsTestMode = true } ;
325+ break ;
326+ case "--smoke-test" :
327+ mode = mode with { IsSmokeTest = true } ;
328+ break ;
329+ case "--start-minimized" :
330+ mode = mode with { StartMinimized = true } ;
331+ break ;
332+ case "--autostart" :
333+ mode = mode with { IsAutostart = true } ;
334+ break ;
335+ case "--startup" :
336+ mode = mode with { IsAutostart = true , StartMinimized = true } ;
337+ break ;
338+ case RegisterLaunchTaskArgument :
339+ mode = mode with { RegisterLaunchTask = true } ;
340+ break ;
341+ case LaunchedViaTaskArgument :
342+ mode = mode with { LaunchedViaTask = true } ;
343+ break ;
344+ }
345+ }
346+
347+ return mode ;
348+ }
349+ }
350+
325351 protected override void OnExit ( ExitEventArgs e )
326352 {
327353 AppDomain . CurrentDomain . UnhandledException -= this . OnUnhandledException ;
0 commit comments