Bootstrapper.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using CefSharp;
  2. using FluentValidation;
  3. using Microsoft.Win32;
  4. using NLog;
  5. using Stylet;
  6. using StyletIoC;
  7. using SyncTrayzor.Localization;
  8. using SyncTrayzor.NotifyIcon;
  9. using SyncTrayzor.Pages;
  10. using SyncTrayzor.Properties;
  11. using SyncTrayzor.Services;
  12. using SyncTrayzor.Services.Config;
  13. using SyncTrayzor.Services.UpdateChecker;
  14. using SyncTrayzor.SyncThing;
  15. using SyncTrayzor.SyncThing.ApiClient;
  16. using SyncTrayzor.SyncThing.EventWatcher;
  17. using SyncTrayzor.Utils;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.Globalization;
  22. using System.Linq;
  23. using System.Text;
  24. using System.Threading;
  25. using System.Threading.Tasks;
  26. using System.Windows;
  27. using System.Windows.Threading;
  28. namespace SyncTrayzor
  29. {
  30. public class Bootstrapper : Bootstrapper<ShellViewModel>
  31. {
  32. private bool exiting;
  33. protected override void ConfigureIoC(IStyletIoCBuilder builder)
  34. {
  35. builder.Bind<IApplicationState>().ToInstance(new ApplicationState(this.Application));
  36. builder.Bind<IConfigurationProvider>().To<ConfigurationProvider>().InSingletonScope();
  37. builder.Bind<IAutostartProvider>().To<AutostartProvider>().InSingletonScope();
  38. builder.Bind<ConfigurationApplicator>().ToSelf().InSingletonScope();
  39. builder.Bind<ISyncThingApiClientFactory>().To<SyncThingApiClientFactory>();
  40. builder.Bind<ISyncThingEventWatcherFactory>().To<SyncThingEventWatcherFactory>();
  41. builder.Bind<ISyncThingProcessRunner>().To<SyncThingProcessRunner>().InSingletonScope();
  42. builder.Bind<ISyncThingManager>().To<SyncThingManager>().InSingletonScope();
  43. builder.Bind<ISyncThingConnectionsWatcherFactory>().To<SyncThingConnectionsWatcherFactory>();
  44. builder.Bind<INotifyIconManager>().To<NotifyIconManager>().InSingletonScope();
  45. builder.Bind<IWatchedFolderMonitor>().To<WatchedFolderMonitor>().InSingletonScope();
  46. builder.Bind<IGithubApiClient>().To<GithubApiClient>().InSingletonScope();
  47. builder.Bind<IUpdateChecker>().To<UpdateChecker>().InSingletonScope();
  48. builder.Bind(typeof(IModelValidator<>)).To(typeof(FluentModelValidator<>));
  49. builder.Bind(typeof(IValidator<>)).ToAllImplementations(this.Assemblies);
  50. }
  51. protected override void Configure()
  52. {
  53. var pathConfiguration = Settings.Default.PathConfiguration;
  54. pathConfiguration.Transform(EnvVarTransformer.Transform);
  55. GlobalDiagnosticsContext.Set("LogFilePath", pathConfiguration.LogFilePath);
  56. var configurationProvider = this.Container.Get<IConfigurationProvider>();
  57. configurationProvider.Initialize(pathConfiguration, Settings.Default.DefaultUserConfiguration);
  58. var configuration = this.Container.Get<IConfigurationProvider>().Load();
  59. // Has to be done before the VMs are fetched from the container
  60. var languageArg = this.Args.FirstOrDefault(x => x.StartsWith("-culture="));
  61. if (languageArg != null)
  62. Thread.CurrentThread.CurrentUICulture = new CultureInfo(languageArg.Substring("-culture=".Length));
  63. else if (!configuration.UseComputerCulture)
  64. Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
  65. var autostartProvider = this.Container.Get<IAutostartProvider>();
  66. #if DEBUG
  67. autostartProvider.IsEnabled = false;
  68. #endif
  69. if (autostartProvider.CanWrite)
  70. {
  71. // If it's not in portable mode, and if we had to create config (i.e. it's the first start ever), then enable autostart
  72. // Else, keep the config as it was, but update the path to us (if we're not in debug)
  73. if (Settings.Default.EnableAutostartOnFirstStart && configurationProvider.HadToCreateConfiguration)
  74. autostartProvider.SetAutoStart(new AutostartConfiguration() { AutoStart = true, StartMinimized = true });
  75. else
  76. autostartProvider.UpdatePathToSelf();
  77. }
  78. var notifyIconManager = this.Container.Get<INotifyIconManager>();
  79. notifyIconManager.Setup((INotifyIconDelegate)this.RootViewModel);
  80. this.Container.Get<ConfigurationApplicator>().ApplyConfiguration();
  81. this.Container.Get<MemoryUsageLogger>().Enabled = true;
  82. // Horrible workaround for a CefSharp crash on logout/shutdown
  83. // https://github.com/cefsharp/CefSharp/issues/800#issuecomment-75058534
  84. this.Application.SessionEnding += (o, e) => Process.GetCurrentProcess().Kill();
  85. if (configurationProvider.Load().NotifyOfNewVersions)
  86. {
  87. SystemEvents.PowerModeChanged += (o, e) =>
  88. {
  89. if (e.Mode == PowerModes.Resume)
  90. this.Container.Get<IUpdateChecker>().CheckForUpdatesAsync();
  91. };
  92. }
  93. MessageBoxViewModel.ButtonLabels = new Dictionary<MessageBoxResult, string>()
  94. {
  95. { MessageBoxResult.Cancel, Localizer.Translate("Generic_Dialog_Cancel") },
  96. { MessageBoxResult.No, Localizer.Translate("Generic_Dialog_No") },
  97. { MessageBoxResult.OK, Localizer.Translate("Generic_Dialog_OK") },
  98. { MessageBoxResult.Yes, Localizer.Translate("Generic_Dialog_Yes") },
  99. };
  100. }
  101. protected override void Launch()
  102. {
  103. if (this.Args.Contains("-minimized"))
  104. this.Container.Get<INotifyIconManager>().EnsureIconVisible();
  105. else
  106. base.Launch();
  107. }
  108. protected override void OnLaunch()
  109. {
  110. var config = this.Container.Get<IConfigurationProvider>().Load();
  111. if (config.StartSyncthingAutomatically && !this.Args.Contains("-noautostart"))
  112. ((ShellViewModel)this.RootViewModel).Start();
  113. // We don't care if this fails
  114. if (config.NotifyOfNewVersions)
  115. this.Container.Get<IUpdateChecker>().CheckForUpdatesAsync();
  116. }
  117. protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e)
  118. {
  119. var logger = LogManager.GetCurrentClassLogger();
  120. logger.Error("An unhandled exception occurred", e.Exception);
  121. // If we're shutting down, we're not going to be able to display an error dialog....
  122. // We've logged it. Nothing else we can do.
  123. if (this.exiting)
  124. return;
  125. try
  126. {
  127. var windowManager = this.Container.Get<IWindowManager>();
  128. var configurationException = e.Exception as ConfigurationException;
  129. if (configurationException != null)
  130. {
  131. windowManager.ShowMessageBox(String.Format("Configuration Error: {0}", configurationException.Message), "Configuration Error", MessageBoxButton.OK, MessageBoxImage.Error);
  132. e.Handled = true;
  133. }
  134. else
  135. {
  136. var vm = this.Container.Get<UnhandledExceptionViewModel>();
  137. vm.Exception = e.Exception;
  138. windowManager.ShowDialog(vm);
  139. }
  140. }
  141. catch (Exception exception)
  142. {
  143. // Don't re-throw. Nasty stuff happens if we throw an exception while trying to handle an unhandled exception
  144. // For starters, the event log shows the wrong exception - this one, instead of the root cause
  145. logger.Error("Unhandled exception while trying to display unhandled exception window", exception);
  146. }
  147. }
  148. protected override void OnExit(ExitEventArgs e)
  149. {
  150. this.exiting = true;
  151. // Try and be nice and close SyncTrayzor gracefully, before the Dispose call on SyncThingProcessRunning kills it dead
  152. this.Container.Get<ISyncThingManager>().StopAsync().Wait(500);
  153. }
  154. }
  155. }