using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; using System.Windows; using System.Windows.Markup; using System.Windows.Threading; using Artemis.Core; using Artemis.Core.Ninject; using Artemis.Core.Services; using Artemis.UI.Ninject; using Artemis.UI.Screens; using Artemis.UI.Services; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Artemis.UI.Stylet; using Artemis.UI.Utilities; using Ninject; using Serilog; using SharpVectors.Dom.Resources; using Stylet; namespace Artemis.UI { public class Bootstrapper : NinjectBootstrapper { private ICoreService _core; public static List StartupArguments { get; private set; } protected override void OnExit(ExitEventArgs e) { // Stop the Artemis core _core?.Dispose(); base.OnExit(e); } protected override void Launch() { Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested; Core.Utilities.RestartRequested += UtilitiesOnRestartRequested; Core.Utilities.PrepareFirstLaunch(); ILogger logger = Kernel.Get(); IViewManager viewManager = Kernel.Get(); StartupArguments = Args.ToList(); // Create the Artemis core try { _core = Kernel.Get(); } catch (Exception e) { HandleFatalException(e, logger); throw; } // Ensure WPF uses the right culture in binding converters FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); // Create and bind the root view, this is a tray icon so don't show it with the window manager Execute.OnUIThread(() => { UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel); ((TrayViewModel) RootViewModel).SetTaskbarIcon(view); }); // Initialize the core async so the UI can show the progress Task.Run(() => { try { _core.StartupArguments = StartupArguments; _core.IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); _core.Initialize(); } catch (Exception e) { HandleFatalException(e, logger); throw; } }); Kernel.Get().RegisterInputProvider(); } protected override void ConfigureIoC(IKernel kernel) { // This is kinda needed for the VM factories in the Shared UI but perhaps there's a less global solution kernel.Settings.InjectNonPublic = true; // Load the UI modules kernel.Load(); kernel.Load(); // Load the core assembly's module kernel.Load(); base.ConfigureIoC(kernel); } protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e) { ILogger logger = Kernel.Get(); logger.Fatal(e.Exception, "Unhandled exception"); IDialogService dialogService = Kernel.Get(); try { dialogService.ShowExceptionDialog("Artemis encountered an error", e.Exception); } catch (Exception) { // We did our best eh.. trap exceptions during exception display here to avoid an infinite loop } // Don't shut down, is that a good idea? Depends on the exception of course.. e.Handled = true; } private void UtilitiesOnShutdownRequested(object sender, EventArgs e) { // Use PowerShell to kill the process after 2 sec just in case ProcessStartInfo info = new() { Arguments = "-Command \"& {Start-Sleep -s 2; (Get-Process 'Artemis.UI').kill()}", WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, FileName = "PowerShell.exe" }; Process.Start(info); Execute.OnUIThread(() => Application.Current.Shutdown()); } private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e) { string args = Args.Any() ? "-ArgumentList " + string.Join(',', Args.Select(a => "'" + a + "'")) : ""; if (e.Elevate) { ProcessStartInfo info = new() { Arguments = $"-Command \"& {{Start-Sleep -Milliseconds {(int) e.Delay.TotalMilliseconds}; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '{Constants.ExecutablePath}' {args} -Verb RunAs}}\"", WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, FileName = "PowerShell.exe" }; Process.Start(info); } else { ProcessUtilities.RunAsDesktopUser(Constants.ExecutablePath); } Execute.OnUIThread(() => Application.Current.Shutdown()); } private void HandleFatalException(Exception e, ILogger logger) { logger.Fatal(e, "Fatal exception during initialization, shutting down."); // Can't use a pretty exception dialog here since the UI might not even be visible Execute.OnUIThread(() => { Kernel.Get().ShowMessageBox(e.Message + "\n\n Please refer the log file for more details.", "Fatal exception during initialization", MessageBoxButton.OK, MessageBoxImage.Error ); Environment.Exit(1); }); } } }