App.xaml.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Globalization;
  7  using System.Threading;
  8  using System.Windows;
  9  using Common.UI;
 10  using ManagedCommon;
 11  using Microsoft.PowerToys.Telemetry;
 12  using WorkspacesEditor.Telemetry;
 13  using WorkspacesEditor.Utils;
 14  using WorkspacesEditor.ViewModels;
 15  
 16  namespace WorkspacesEditor
 17  {
 18      /// <summary>
 19      /// Interaction logic for App.xaml
 20      /// </summary>
 21      public partial class App : Application, IDisposable
 22      {
 23          private static Mutex _instanceMutex;
 24  
 25          public static WorkspacesEditorIO WorkspacesEditorIO { get; private set; }
 26  
 27          private MainWindow _mainWindow;
 28  
 29          private MainViewModel _mainViewModel;
 30  
 31          public static ThemeManager ThemeManager { get; set; }
 32  
 33          private bool _isDisposed;
 34  
 35          private ETWTrace etwTrace = new ETWTrace();
 36  
 37          public App()
 38          {
 39              PowerToysTelemetry.Log.WriteEvent(new WorkspacesEditorStartEvent() { TimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
 40  
 41              WorkspacesEditorIO = new WorkspacesEditorIO();
 42          }
 43  
 44          private void OnStartup(object sender, StartupEventArgs e)
 45          {
 46              Logger.InitializeLogger("\\Workspaces\\Logs");
 47              AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
 48  
 49              var languageTag = LanguageHelper.LoadLanguage();
 50  
 51              if (!string.IsNullOrEmpty(languageTag))
 52              {
 53                  try
 54                  {
 55                      System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(languageTag);
 56                  }
 57                  catch (CultureNotFoundException ex)
 58                  {
 59                      Logger.LogError("CultureNotFoundException: " + ex.Message);
 60                  }
 61              }
 62  
 63              const string appName = "Local\\PowerToys_Workspaces_Editor_InstanceMutex";
 64              bool createdNew;
 65              _instanceMutex = new Mutex(true, appName, out createdNew);
 66              if (!createdNew)
 67              {
 68                  Logger.LogWarning("Another instance of Workspaces Editor is already running. Exiting this instance.");
 69                  _instanceMutex = null;
 70                  Shutdown(0);
 71                  return;
 72              }
 73  
 74              if (PowerToys.GPOWrapperProjection.GPOWrapper.GetConfiguredWorkspacesEnabledValue() == PowerToys.GPOWrapperProjection.GpoRuleConfigured.Disabled)
 75              {
 76                  Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
 77                  Shutdown(0);
 78                  return;
 79              }
 80  
 81              var args = e?.Args;
 82              int powerToysRunnerPid;
 83              if (args?.Length > 0)
 84              {
 85                  _ = int.TryParse(args[0], out powerToysRunnerPid);
 86  
 87                  Logger.LogInfo($"WorkspacesEditor started from the PowerToys Runner. Runner pid={powerToysRunnerPid}");
 88                  RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
 89                  {
 90                      Logger.LogInfo("PowerToys Runner exited. Exiting WorkspacesEditor");
 91                      Dispatcher.Invoke(Shutdown);
 92                  });
 93              }
 94  
 95              ThemeManager = new ThemeManager(this);
 96  
 97              if (_mainViewModel == null)
 98              {
 99                  _mainViewModel = new MainViewModel(WorkspacesEditorIO);
100              }
101  
102              var parseResult = WorkspacesEditorIO.ParseWorkspaces(_mainViewModel);
103  
104              // normal start of editor
105              if (_mainWindow == null)
106              {
107                  _mainWindow = new MainWindow(_mainViewModel);
108              }
109  
110              // reset main window owner to keep it on the top
111              _mainWindow.ShowActivated = true;
112              _mainWindow.Topmost = true;
113              _mainWindow.Show();
114  
115              // we can reset topmost flag after it's opened
116              _mainWindow.Topmost = false;
117          }
118  
119          private void OnExit(object sender, ExitEventArgs e)
120          {
121              if (_instanceMutex != null)
122              {
123                  _instanceMutex.ReleaseMutex();
124              }
125  
126              Dispose();
127              Environment.Exit(0);
128          }
129  
130          private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
131          {
132              Logger.LogError("Unhandled exception occurred", args.ExceptionObject as Exception);
133          }
134  
135          protected virtual void Dispose(bool disposing)
136          {
137              if (!_isDisposed)
138              {
139                  if (disposing)
140                  {
141                      ThemeManager?.Dispose();
142                      _instanceMutex?.Dispose();
143                      etwTrace?.Dispose();
144                  }
145  
146                  _isDisposed = true;
147              }
148          }
149  
150          public void Dispose()
151          {
152              // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
153              Dispose(disposing: true);
154              GC.SuppressFinalize(this);
155          }
156      }
157  }