@@ -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