/ src / modules / cmdpal / Microsoft.CmdPal.UI / App.xaml.cs
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 ManagedCommon;
  6  using Microsoft.CmdPal.Core.Common;
  7  using Microsoft.CmdPal.Core.Common.Helpers;
  8  using Microsoft.CmdPal.Core.Common.Services;
  9  using Microsoft.CmdPal.Core.ViewModels;
 10  using Microsoft.CmdPal.Ext.Apps;
 11  using Microsoft.CmdPal.Ext.Bookmarks;
 12  using Microsoft.CmdPal.Ext.Calc;
 13  using Microsoft.CmdPal.Ext.ClipboardHistory;
 14  using Microsoft.CmdPal.Ext.Indexer;
 15  using Microsoft.CmdPal.Ext.Registry;
 16  using Microsoft.CmdPal.Ext.RemoteDesktop;
 17  using Microsoft.CmdPal.Ext.Shell;
 18  using Microsoft.CmdPal.Ext.System;
 19  using Microsoft.CmdPal.Ext.TimeDate;
 20  using Microsoft.CmdPal.Ext.WebSearch;
 21  using Microsoft.CmdPal.Ext.WindowsServices;
 22  using Microsoft.CmdPal.Ext.WindowsSettings;
 23  using Microsoft.CmdPal.Ext.WindowsTerminal;
 24  using Microsoft.CmdPal.Ext.WindowWalker;
 25  using Microsoft.CmdPal.Ext.WinGet;
 26  using Microsoft.CmdPal.UI.Helpers;
 27  using Microsoft.CmdPal.UI.Services;
 28  using Microsoft.CmdPal.UI.ViewModels;
 29  using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
 30  using Microsoft.CmdPal.UI.ViewModels.Models;
 31  using Microsoft.CmdPal.UI.ViewModels.Services;
 32  using Microsoft.CommandPalette.Extensions;
 33  using Microsoft.Extensions.DependencyInjection;
 34  using Microsoft.PowerToys.Telemetry;
 35  using Microsoft.UI.Dispatching;
 36  using Microsoft.UI.Xaml;
 37  
 38  // To learn more about WinUI, the WinUI project structure,
 39  // and more about our project templates, see: http://aka.ms/winui-project-info.
 40  namespace Microsoft.CmdPal.UI;
 41  
 42  /// <summary>
 43  /// Provides application-specific behavior to supplement the default Application class.
 44  /// </summary>
 45  public partial class App : Application, IDisposable
 46  {
 47      private readonly GlobalErrorHandler _globalErrorHandler = new();
 48  
 49      /// <summary>
 50      /// Gets the current <see cref="App"/> instance in use.
 51      /// </summary>
 52      public static new App Current => (App)Application.Current;
 53  
 54      public Window? AppWindow { get; private set; }
 55  
 56      public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
 57  
 58      /// <summary>
 59      /// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
 60      /// </summary>
 61      public IServiceProvider Services { get; }
 62  
 63      /// <summary>
 64      /// Initializes a new instance of the <see cref="App"/> class.
 65      /// Initializes the singleton application object.  This is the first line of authored code
 66      /// executed, and as such is the logical equivalent of main() or WinMain().
 67      /// </summary>
 68      public App()
 69      {
 70  #if !CMDPAL_DISABLE_GLOBAL_ERROR_HANDLER
 71          _globalErrorHandler.Register(this, GlobalErrorHandler.Options.Default);
 72  #endif
 73  
 74          Services = ConfigureServices();
 75  
 76          IconCacheProvider.Initialize(Services);
 77  
 78          this.InitializeComponent();
 79  
 80          // Ensure types used in XAML are preserved for AOT compilation
 81          TypePreservation.PreserveTypes();
 82  
 83          NativeEventWaiter.WaitForEventLoop(
 84              "Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () =>
 85              {
 86                  EtwTrace?.Dispose();
 87                  AppWindow?.Close();
 88                  Environment.Exit(0);
 89              });
 90  
 91          // Connect the PT logging to the core project's logging.
 92          // This way, log statements from the core project will be captured by the PT logs
 93          var logWrapper = new LogWrapper();
 94          CoreLogger.InitializeLogger(logWrapper);
 95      }
 96  
 97      /// <summary>
 98      /// Invoked when the application is launched.
 99      /// </summary>
100      /// <param name="args">Details about the launch request and process.</param>
101      protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
102      {
103          AppWindow = new MainWindow();
104  
105          var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
106          ((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
107      }
108  
109      /// <summary>
110      /// Configures the services for the application
111      /// </summary>
112      private static ServiceProvider ConfigureServices()
113      {
114          // TODO: It's in the Labs feed, but we can use Sergio's AOT-friendly source generator for this: https://github.com/CommunityToolkit/Labs-Windows/discussions/463
115          ServiceCollection services = new();
116  
117          // Root services
118          services.AddSingleton(TaskScheduler.FromCurrentSynchronizationContext());
119          var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
120  
121          AddBuiltInCommands(services);
122  
123          AddCoreServices(services);
124  
125          AddUIServices(services, dispatcherQueue);
126  
127          return services.BuildServiceProvider();
128      }
129  
130      private static void AddBuiltInCommands(ServiceCollection services)
131      {
132          // Built-in Commands. Order matters - this is the order they'll be presented by default.
133          var allApps = new AllAppsCommandProvider();
134          var files = new IndexerCommandsProvider();
135          files.SuppressFallbackWhen(ShellCommandsProvider.SuppressFileFallbackIf);
136          services.AddSingleton<ICommandProvider>(allApps);
137  
138          services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
139          services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
140          services.AddSingleton<ICommandProvider>(files);
141          services.AddSingleton<ICommandProvider, BookmarksCommandProvider>(_ => BookmarksCommandProvider.CreateWithDefaultStore());
142  
143          services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
144          services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
145          services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
146  
147          // GH #38440: Users might not have WinGet installed! Or they might have
148          // a ridiculously old version. Or might be running as admin.
149          // We shouldn't explode in the App ctor if we fail to instantiate an
150          // instance of PackageManager, which will happen in the static ctor
151          // for WinGetStatics
152          try
153          {
154              var winget = new WinGetExtensionCommandsProvider();
155              winget.SetAllLookup(
156                  query => allApps.LookupAppByPackageFamilyName(query, requireSingleMatch: true),
157                  query => allApps.LookupAppByProductCode(query, requireSingleMatch: true));
158              services.AddSingleton<ICommandProvider>(winget);
159          }
160          catch (Exception ex)
161          {
162              Logger.LogError("Couldn't load winget");
163              Logger.LogError(ex.ToString());
164          }
165  
166          services.AddSingleton<ICommandProvider, WindowsTerminalCommandsProvider>();
167          services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
168          services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
169          services.AddSingleton<ICommandProvider, WindowsServicesCommandsProvider>();
170          services.AddSingleton<ICommandProvider, BuiltInsCommandProvider>();
171          services.AddSingleton<ICommandProvider, TimeDateCommandsProvider>();
172          services.AddSingleton<ICommandProvider, SystemCommandExtensionProvider>();
173          services.AddSingleton<ICommandProvider, RemoteDesktopCommandProvider>();
174      }
175  
176      private static void AddUIServices(ServiceCollection services, DispatcherQueue dispatcherQueue)
177      {
178          // Models
179          var sm = SettingsModel.LoadSettings();
180          services.AddSingleton(sm);
181          var state = AppStateModel.LoadState();
182          services.AddSingleton(state);
183  
184          // Services
185          services.AddSingleton<ICommandProviderCache, DefaultCommandProviderCache>();
186          services.AddSingleton<TopLevelCommandManager>();
187          services.AddSingleton<AliasManager>();
188          services.AddSingleton<HotkeyManager>();
189  
190          services.AddSingleton<MainWindowViewModel>();
191          services.AddSingleton<TrayIconService>();
192  
193          services.AddSingleton<IThemeService, ThemeService>();
194          services.AddSingleton<ResourceSwapper>();
195  
196          services.AddIconServices(dispatcherQueue);
197      }
198  
199      private static void AddCoreServices(ServiceCollection services)
200      {
201          // Core services
202          services.AddSingleton<IExtensionService, ExtensionService>();
203          services.AddSingleton<IRunHistoryService, RunHistoryService>();
204  
205          services.AddSingleton<IRootPageService, PowerToysRootPageService>();
206          services.AddSingleton<IAppHostService, PowerToysAppHostService>();
207          services.AddSingleton<ITelemetryService, TelemetryForwarder>();
208  
209          // ViewModels
210          services.AddSingleton<ShellViewModel>();
211          services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
212      }
213  
214      public void Dispose()
215      {
216          _globalErrorHandler.Dispose();
217          EtwTrace.Dispose();
218          GC.SuppressFinalize(this);
219      }
220  }