2525
2626namespace Flow . Launcher
2727{
28- public partial class App : IDisposable , ISingleInstanceApp
28+ public partial class App : IAsyncDisposable , ISingleInstanceApp
2929 {
30+ #region Public Properties
31+
3032 public static IPublicAPI API { get ; private set ; }
33+ public static CancellationTokenSource NativeThreadCTS { get ; private set ; }
34+
35+ #endregion
36+
37+ #region Private Fields
38+
3139 private static bool _disposed ;
40+ private MainWindow _mainWindow ;
41+ private readonly MainViewModel _mainVM ;
3242 private readonly Settings _settings ;
3343
44+ // To prevent two disposals running at the same time.
45+ private static readonly object _disposingLock = new ( ) ;
46+
47+ #endregion
48+
49+ #region Constructor
50+
3451 public App ( )
3552 {
3653 // Initialize settings
@@ -78,34 +95,47 @@ public App()
7895 {
7996 API = Ioc . Default . GetRequiredService < IPublicAPI > ( ) ;
8097 _settings . Initialize ( ) ;
98+ _mainVM = Ioc . Default . GetRequiredService < MainViewModel > ( ) ;
8199 }
82100 catch ( Exception e )
83101 {
84102 ShowErrorMsgBoxAndFailFast ( "Cannot initialize api and settings, please open new issue in Flow.Launcher" , e ) ;
85103 return ;
86104 }
87- }
88105
89- private static void ShowErrorMsgBoxAndFailFast ( string message , Exception e )
90- {
91- // Firstly show users the message
92- MessageBox . Show ( e . ToString ( ) , message , MessageBoxButton . OK , MessageBoxImage . Error ) ;
106+ // Local function
107+ static void ShowErrorMsgBoxAndFailFast ( string message , Exception e )
108+ {
109+ // Firstly show users the message
110+ MessageBox . Show ( e . ToString ( ) , message , MessageBoxButton . OK , MessageBoxImage . Error ) ;
93111
94- // Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
95- Environment . FailFast ( message , e ) ;
112+ // Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
113+ Environment . FailFast ( message , e ) ;
114+ }
96115 }
97116
117+ #endregion
118+
119+ #region Main
120+
98121 [ STAThread ]
99122 public static void Main ( )
100123 {
124+ NativeThreadCTS = new CancellationTokenSource ( ) ;
125+
101126 if ( SingleInstance < App > . InitializeAsFirstInstance ( ) )
102127 {
103- using var application = new App ( ) ;
128+ var application = new App ( ) ;
104129 application . InitializeComponent ( ) ;
105130 application . Run ( ) ;
131+ application . DisposeAsync ( ) . AsTask ( ) . GetAwaiter ( ) . GetResult ( ) ;
106132 }
107133 }
108134
135+ #endregion
136+
137+ #region App Events
138+
109139#pragma warning disable VSTHRD100 // Avoid async void methods
110140
111141 private async void OnStartup ( object sender , StartupEventArgs e )
@@ -136,11 +166,11 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
136166 await PluginManager . InitializePluginsAsync ( ) ;
137167 await imageLoadertask ;
138168
139- var window = new MainWindow ( ) ;
169+ _mainWindow = new MainWindow ( ) ;
140170
141171 Log . Info ( $ "|App.OnStartup|Dependencies Info:{ ErrorReporting . DependenciesInfo ( ) } ") ;
142172
143- Current . MainWindow = window ;
173+ Current . MainWindow = _mainWindow ;
144174 Current . MainWindow . Title = Constant . FlowLauncher ;
145175
146176 HotKeyMapper . Initialize ( ) ;
@@ -157,8 +187,7 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
157187 AutoUpdates ( ) ;
158188
159189 API . SaveAppAllSettings ( ) ;
160- Log . Info (
161- "|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- " ) ;
190+ Log . Info ( "|App.OnStartup|End Flow Launcher startup ----------------------------------------------------" ) ;
162191 } ) ;
163192 }
164193
@@ -191,7 +220,6 @@ private void AutoStartup()
191220 }
192221 }
193222
194- //[Conditional("RELEASE")]
195223 private void AutoUpdates ( )
196224 {
197225 _ = Task . Run ( async ( ) =>
@@ -209,11 +237,30 @@ private void AutoUpdates()
209237 } ) ;
210238 }
211239
240+ #endregion
241+
242+ #region Register Events
243+
212244 private void RegisterExitEvents ( )
213245 {
214- AppDomain . CurrentDomain . ProcessExit += ( s , e ) => Dispose ( ) ;
215- Current . Exit += ( s , e ) => Dispose ( ) ;
216- Current . SessionEnding += ( s , e ) => Dispose ( ) ;
246+ AppDomain . CurrentDomain . ProcessExit += ( s , e ) =>
247+ {
248+ Log . Info ( "|App.RegisterExitEvents|Process Exit" ) ;
249+ _ = DisposeAsync ( ) ;
250+ } ;
251+
252+ Current . Exit += ( s , e ) =>
253+ {
254+ NativeThreadCTS . Cancel ( ) ;
255+ Log . Info ( "|App.RegisterExitEvents|Application Exit" ) ;
256+ _ = DisposeAsync ( ) ;
257+ } ;
258+
259+ Current . SessionEnding += ( s , e ) =>
260+ {
261+ Log . Info ( "|App.RegisterExitEvents|Session Ending" ) ;
262+ _ = DisposeAsync ( ) ;
263+ } ;
217264 }
218265
219266 /// <summary>
@@ -234,20 +281,62 @@ private static void RegisterAppDomainExceptions()
234281 AppDomain . CurrentDomain . UnhandledException += ErrorReporting . UnhandledExceptionHandle ;
235282 }
236283
237- public void Dispose ( )
284+ #endregion
285+
286+ #region IAsyncDisposable
287+
288+ protected virtual async ValueTask DisposeAsync ( bool disposing )
238289 {
239- // if sessionending is called, exit proverbially be called when log off / shutdown
240- // but if sessionending is not called, exit won't be called when log off / shutdown
241- if ( ! _disposed )
290+ // Prevent two disposes at the same time.
291+ lock ( _disposingLock )
242292 {
243- API . SaveAppAllSettings ( ) ;
293+ if ( ! disposing )
294+ {
295+ return ;
296+ }
297+
298+ if ( _disposed )
299+ {
300+ return ;
301+ }
302+
244303 _disposed = true ;
245304 }
305+
306+ await Stopwatch . NormalAsync ( "|App.Dispose|Dispose cost" , async ( ) =>
307+ {
308+ Log . Info ( "|App.Dispose|Begin Flow Launcher dispose ----------------------------------------------------" ) ;
309+
310+ if ( disposing )
311+ {
312+ API ? . SaveAppAllSettings ( ) ;
313+ await PluginManager . DisposePluginsAsync ( ) ;
314+
315+ // Dispose needs to be called on the main Windows thread, since some resources owned by the thread need to be disposed.
316+ await _mainWindow ? . Dispatcher . InvokeAsync ( DisposeAsync ) ;
317+ _mainVM ? . Dispose ( ) ;
318+ }
319+
320+ Log . Info ( "|App.Dispose|End Flow Launcher dispose ----------------------------------------------------" ) ;
321+ } ) ;
246322 }
247323
324+ public async ValueTask DisposeAsync ( )
325+ {
326+ // Do not change this code. Put cleanup code in 'DisposeAsync(bool disposing)' method
327+ await DisposeAsync ( disposing : true ) ;
328+ GC . SuppressFinalize ( this ) ;
329+ }
330+
331+ #endregion
332+
333+ #region ISingleInstanceApp
334+
248335 public void OnSecondAppStarted ( )
249336 {
250337 Ioc . Default . GetRequiredService < MainViewModel > ( ) . Show ( ) ;
251338 }
339+
340+ #endregion
252341 }
253342}
0 commit comments