@@ -46,6 +46,8 @@ public partial class App : Application
4646
4747 private readonly ISettingsManager _settingsManager ;
4848
49+ private readonly IHostApplicationLifetime _appLifetime ;
50+
4951 public App ( )
5052 {
5153 var builder = Host . CreateApplicationBuilder ( ) ;
@@ -119,6 +121,7 @@ public App()
119121 _logger = ( ILogger < App > ) _services . GetService ( typeof ( ILogger < App > ) ) ! ;
120122 _uriHandler = ( IUriHandler ) _services . GetService ( typeof ( IUriHandler ) ) ! ;
121123 _settingsManager = ( ISettingsManager ) _services . GetService ( typeof ( ISettingsManager ) ) ! ;
124+ _appLifetime = ( IHostApplicationLifetime ) _services . GetRequiredService < IHostApplicationLifetime > ( ) ;
122125
123126 InitializeComponent ( ) ;
124127 }
@@ -140,71 +143,73 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
140143 {
141144 _logger . LogInformation ( "new instance launched" ) ;
142145
143- // Load the credentials in the background.
144- var credentialManagerCts = new CancellationTokenSource ( TimeSpan . FromSeconds ( 15 ) ) ;
146+ _ = InitializeServicesAsync ( _appLifetime . ApplicationStopping ) ;
147+
148+ // Prevent the TrayWindow from closing, just hide it.
149+ var trayWindow = _services . GetRequiredService < TrayWindow > ( ) ;
150+ trayWindow . Closed += ( _ , closedArgs ) =>
151+ {
152+ if ( ! _handleWindowClosed ) return ;
153+ closedArgs . Handled = true ;
154+ trayWindow . AppWindow . Hide ( ) ;
155+ } ;
156+ }
157+
158+ /// <summary>
159+ /// Loads stored VPN credentials, reconnects the RPC controller,
160+ /// and (optionally) starts the VPN tunnel on application launch.
161+ /// </summary>
162+ private async Task InitializeServicesAsync ( CancellationToken cancellationToken = default )
163+ {
145164 var credentialManager = _services . GetRequiredService < ICredentialManager > ( ) ;
146- credentialManager . LoadCredentials ( credentialManagerCts . Token ) . ContinueWith ( t =>
165+ var rpcController = _services . GetRequiredService < IRpcController > ( ) ;
166+
167+ using var credsCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
168+ credsCts . CancelAfter ( TimeSpan . FromSeconds ( 15 ) ) ;
169+
170+ Task loadCredsTask = credentialManager . LoadCredentials ( credsCts . Token ) ;
171+ Task reconnectTask = rpcController . Reconnect ( cancellationToken ) ;
172+
173+ try
147174 {
148- if ( t . Exception != null )
149- {
150- _logger . LogError ( t . Exception , "failed to load credentials" ) ;
151- #if DEBUG
152- Debug . WriteLine ( t . Exception ) ;
153- Debugger . Break ( ) ;
154- #endif
155- }
175+ await Task . WhenAll ( loadCredsTask , reconnectTask ) ;
176+ }
177+ catch ( Exception )
178+ {
179+ if ( loadCredsTask . IsFaulted )
180+ _logger . LogError ( loadCredsTask . Exception ! . GetBaseException ( ) ,
181+ "Failed to load credentials" ) ;
156182
157- credentialManagerCts . Dispose ( ) ;
158- } ) ;
183+ if ( reconnectTask . IsFaulted )
184+ _logger . LogError ( reconnectTask . Exception ! . GetBaseException ( ) ,
185+ "Failed to connect to VPN service" ) ;
159186
187+ return ;
188+ }
160189
161- // Start connecting to the manager in the background.
162- var rpcController = _services . GetRequiredService < IRpcController > ( ) ;
163- _ = rpcController . Reconnect ( CancellationToken . None ) . ContinueWith ( t =>
190+ if ( _settingsManager . ConnectOnLaunch )
164191 {
165- if ( t . Exception != null )
192+ try
166193 {
167- _logger . LogError ( t . Exception , "failed to connect to VPN service" ) ;
168- #if DEBUG
169- Debug . WriteLine ( t . Exception ) ;
170- Debugger . Break ( ) ;
171- #endif
172- return ;
194+ await rpcController . StartVpn ( cancellationToken ) ;
173195 }
174- if ( _settingsManager . ConnectOnLaunch )
196+ catch ( Exception ex )
175197 {
176- _logger . LogInformation ( "RPC lifecycle is disconnected, but ConnectOnLaunch is enabled; attempting to connect" ) ;
177- _ = rpcController . StartVpn ( CancellationToken . None ) . ContinueWith ( connectTask =>
178- {
179- if ( connectTask . Exception != null )
180- {
181- _logger . LogError ( connectTask . Exception , "failed to connect on launch" ) ;
182- }
183- } ) ;
198+ _logger . LogError ( ex , "Failed to connect on launch" ) ;
184199 }
185- } ) ;
200+ }
186201
187202 // Initialize file sync.
188203 var syncSessionCts = new CancellationTokenSource ( TimeSpan . FromSeconds ( 10 ) ) ;
189204 var syncSessionController = _services . GetRequiredService < ISyncSessionController > ( ) ;
190- _ = syncSessionController . RefreshState ( syncSessionCts . Token ) . ContinueWith ( t =>
205+ try
191206 {
192- if ( t . IsCanceled || t . Exception != null )
193- {
194- _logger . LogError ( t . Exception , "failed to refresh sync state (canceled = {canceled})" , t . IsCanceled ) ;
195- }
196-
197- syncSessionCts . Dispose ( ) ;
198- } , CancellationToken . None ) ;
199-
200- // Prevent the TrayWindow from closing, just hide it.
201- var trayWindow = _services . GetRequiredService < TrayWindow > ( ) ;
202- trayWindow . Closed += ( _ , closedArgs ) =>
207+ await syncSessionController . RefreshState ( syncSessionCts . Token ) ;
208+ }
209+ catch ( Exception ex )
203210 {
204- if ( ! _handleWindowClosed ) return ;
205- closedArgs . Handled = true ;
206- trayWindow . AppWindow . Hide ( ) ;
207- } ;
211+ _logger . LogError ( $ "Failed to refresh sync session state { ex . Message } ", ex ) ;
212+ }
208213 }
209214
210215 public void OnActivated ( object ? sender , AppActivationArguments args )
0 commit comments