-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApp.xaml.cs
198 lines (171 loc) · 8.3 KB
/
App.xaml.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using SupernoteDesktopClient.Core;
using SupernoteDesktopClient.Core.Win32Api;
using SupernoteDesktopClient.Services;
using SupernoteDesktopClient.Services.Contracts;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Wpf.Ui.Mvvm.Contracts;
using Wpf.Ui.Mvvm.Services;
namespace SupernoteDesktopClient
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
private static readonly Mutex _appMutex = new Mutex(true, "C5FDA39A-40DA-4C77-842B-0C878F0D73C2");
private static readonly string _logsPath = Path.Combine(FileSystemManager.GetApplicationFolder(), "Logs");
// The.NET Generic Host provides dependency injection, configuration, logging, and other services.
// https://docs.microsoft.com/dotnet/core/extensions/generic-host
// https://docs.microsoft.com/dotnet/core/extensions/dependency-injection
// https://docs.microsoft.com/dotnet/core/extensions/configuration
// https://docs.microsoft.com/dotnet/core/extensions/logging
private static readonly IHost _host = Host
.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
// App Host
services.AddHostedService<ApplicationHostService>();
// Framework services
services.AddSingleton<IPageService, PageService>();
services.AddSingleton<IThemeService, ThemeService>();
services.AddSingleton<ITaskBarService, TaskBarService>();
services.AddSingleton<ISnackbarService, SnackbarService>();
services.AddSingleton<IDialogService, DialogService>();
// Custom services
services.AddSingleton<IUsbHubDetector, UsbHubDetector>();
services.AddSingleton<IMediaDeviceService, MediaDeviceService>();
services.AddKeyedSingleton<ISyncService, UsbSyncService>(SyncMode.UsbSync);
services.AddKeyedSingleton<ISyncService, WifiSyncService>(SyncMode.WifiSync);
// Service containing navigation, same as INavigationWindow... but without window
services.AddSingleton<INavigationService, NavigationService>();
// Main window with navigation
services.AddScoped<INavigationWindow, Views.Windows.MainWindow>();
services.AddScoped<ViewModels.MainWindowViewModel>();
// Views and ViewModels
services.AddScoped<Views.Pages.AboutPage>();
services.AddScoped<ViewModels.AboutViewModel>();
services.AddScoped<Views.Pages.DashboardPage>();
services.AddScoped<ViewModels.DashboardViewModel>();
services.AddScoped<Views.Pages.ExplorerPage>();
services.AddScoped<ViewModels.ExplorerViewModel>();
services.AddScoped<Views.Pages.SettingsPage>();
services.AddScoped<ViewModels.SettingsViewModel>();
services.AddScoped<Views.Pages.SyncPage>();
services.AddScoped<ViewModels.SyncViewModel>();
})
.UseSerilog()
.Build();
/// <summary>
/// Occurs when the application is loading.
/// </summary>
private async void OnStartup(object sender, StartupEventArgs e)
{
ForceSingleInstance();
ConfigureLogging();
SetupUnhandledExceptionHandling();
DiagnosticLogger.Log($"Sdc {ApplicationManager.GetAssemblyVersion()} started...");
await _host.StartAsync();
}
/// <summary>
/// Occurs when the application is closing.
/// </summary>
private async void OnExit(object sender, ExitEventArgs e)
{
DiagnosticLogger.Log($"Sdc {ApplicationManager.GetAssemblyVersion()} exited...");
// flush all log items before exit
Log.CloseAndFlush();
await _host.StopAsync();
_host.Dispose();
}
private static void ForceSingleInstance()
{
if (_appMutex.WaitOne(TimeSpan.Zero, true) == true)
{
_appMutex.ReleaseMutex();
// show splash window
SplashScreen splash = new SplashScreen(@"assets\spash.png");
splash.Show(true, true);
}
else
{
Process[] processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
{
if (processes.Length > 0)
{
NativeMethods.ShowWindowEx(processes[0].MainWindowHandle, NativeMethods.SW_RESTORE_WINDOW);
NativeMethods.SetForegroundWindowEx(processes[0].MainWindowHandle);
}
}
Application.Current.Shutdown();
}
}
private static void ConfigureLogging()
{
// configure logging
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Logger(p => p
.Filter.ByExcluding(p => p.Properties.ContainsKey("IsDiag"))
.WriteTo.File(Path.Combine(_logsPath, "Sdc-.log"),
outputTemplate: "{Timestamp:MM/dd/yyyy HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7))
.WriteTo.Logger(p => p
.Filter.ByIncludingOnly(p => p.Properties.ContainsKey("IsDiag"))
.WriteTo.File(Path.Combine(_logsPath, "Sdc-Diag-.log"),
outputTemplate: "{Timestamp:MM/dd/yyyy HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7))
.CreateLogger();
}
private void SetupUnhandledExceptionHandling()
{
// handler for all exceptions from all threads - can recover
AppDomain.CurrentDomain.UnhandledException += delegate (object sender, UnhandledExceptionEventArgs e)
{
ShowExceptionAndExit(e.ExceptionObject as Exception, "AppDomain.CurrentDomain.UnhandledException");
};
// handler for exceptions from each AppDomain that uses a task scheduler for async operations - can recover
TaskScheduler.UnobservedTaskException += delegate (object sender, UnobservedTaskExceptionEventArgs e)
{
ShowExceptionAndExit(e.Exception, "TaskScheduler.UnobservedTaskException");
};
// handler for all exceptions from a single dispatcher thread - cannot recover
Current.DispatcherUnhandledException += delegate (object sender, DispatcherUnhandledExceptionEventArgs e)
{
// If we are debugging, let Visual Studio handle the exception and take us to the code that threw it
if (Debugger.IsAttached == false)
{
e.Handled = true;
ShowExceptionAndExit(e.Exception, "Current.DispatcherUnhandledException");
}
};
}
private void ShowExceptionAndExit(Exception ex, string exceptionType)
{
Log.Error("Fatal application exception: {EX}", ex);
string errorMessage = $"A fatal application error occurred: {ex.Message}\n\nPlease, check error logs at:\n{_logsPath} for more details.\n\nApplication will close now.";
MessageBox.Show(errorMessage, $"Sdc - Fatal Error: {exceptionType}", MessageBoxButton.OK, MessageBoxImage.Error);
try
{
SettingsManager.Instance.Save();
}
catch (Exception)
{
// ignore we are exiting anyway
}
Application.Current.Shutdown();
}
}
}