@@ -46,6 +46,8 @@ public partial class App : Application
46
46
47
47
private readonly ISettingsManager _settingsManager ;
48
48
49
+ private readonly IHostApplicationLifetime _appLifetime ;
50
+
49
51
public App ( )
50
52
{
51
53
var builder = Host . CreateApplicationBuilder ( ) ;
@@ -119,6 +121,7 @@ public App()
119
121
_logger = ( ILogger < App > ) _services . GetService ( typeof ( ILogger < App > ) ) ! ;
120
122
_uriHandler = ( IUriHandler ) _services . GetService ( typeof ( IUriHandler ) ) ! ;
121
123
_settingsManager = ( ISettingsManager ) _services . GetService ( typeof ( ISettingsManager ) ) ! ;
124
+ _appLifetime = ( IHostApplicationLifetime ) _services . GetRequiredService < IHostApplicationLifetime > ( ) ;
122
125
123
126
InitializeComponent ( ) ;
124
127
}
@@ -140,71 +143,73 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
140
143
{
141
144
_logger . LogInformation ( "new instance launched" ) ;
142
145
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
+ {
145
164
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
147
174
{
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" ) ;
156
182
157
- credentialManagerCts . Dispose ( ) ;
158
- } ) ;
183
+ if ( reconnectTask . IsFaulted )
184
+ _logger . LogError ( reconnectTask . Exception ! . GetBaseException ( ) ,
185
+ "Failed to connect to VPN service" ) ;
159
186
187
+ return ;
188
+ }
160
189
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 )
164
191
{
165
- if ( t . Exception != null )
192
+ try
166
193
{
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 ) ;
173
195
}
174
- if ( _settingsManager . ConnectOnLaunch )
196
+ catch ( Exception ex )
175
197
{
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" ) ;
184
199
}
185
- } ) ;
200
+ }
186
201
187
202
// Initialize file sync.
188
203
var syncSessionCts = new CancellationTokenSource ( TimeSpan . FromSeconds ( 10 ) ) ;
189
204
var syncSessionController = _services . GetRequiredService < ISyncSessionController > ( ) ;
190
- _ = syncSessionController . RefreshState ( syncSessionCts . Token ) . ContinueWith ( t =>
205
+ try
191
206
{
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 )
203
210
{
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
+ }
208
213
}
209
214
210
215
public void OnActivated ( object ? sender , AppActivationArguments args )
0 commit comments