// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using ManagedCommon; using Microsoft.CmdPal.Common; using Microsoft.CmdPal.Common.Helpers; using Microsoft.CmdPal.Common.Services; using Microsoft.CmdPal.Common.Text; using Microsoft.CmdPal.Ext.Apps; using Microsoft.CmdPal.Ext.Bookmarks; using Microsoft.CmdPal.Ext.Calc; using Microsoft.CmdPal.Ext.ClipboardHistory; using Microsoft.CmdPal.Ext.Indexer; using Microsoft.CmdPal.Ext.PerformanceMonitor; using Microsoft.CmdPal.Ext.Registry; using Microsoft.CmdPal.Ext.RemoteDesktop; using Microsoft.CmdPal.Ext.Shell; using Microsoft.CmdPal.Ext.System; using Microsoft.CmdPal.Ext.TimeDate; using Microsoft.CmdPal.Ext.WebSearch; using Microsoft.CmdPal.Ext.WindowsServices; using Microsoft.CmdPal.Ext.WindowsSettings; using Microsoft.CmdPal.Ext.WindowsTerminal; using Microsoft.CmdPal.Ext.WindowWalker; using Microsoft.CmdPal.Ext.WinGet; using Microsoft.CmdPal.UI.Helpers; using Microsoft.CmdPal.UI.Services; using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands; using Microsoft.CmdPal.UI.ViewModels.Dock; using Microsoft.CmdPal.UI.ViewModels.Models; using Microsoft.CmdPal.UI.ViewModels.Services; using Microsoft.CommandPalette.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. namespace Microsoft.CmdPal.UI; /// /// Provides application-specific behavior to supplement the default Application class. /// public partial class App : Application, IDisposable { private readonly GlobalErrorHandler _globalErrorHandler = new(); /// /// Gets the current instance in use. /// public static new App Current => (App)Application.Current; public Window? AppWindow { get; private set; } public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); /// /// Gets the instance to resolve application services. /// public IServiceProvider Services { get; } /// /// Initializes a new instance of the class. /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// public App() { var appInfoService = new ApplicationInfoService(); #if !CMDPAL_DISABLE_GLOBAL_ERROR_HANDLER _globalErrorHandler.Register(this, GlobalErrorHandler.Options.Default, appInfoService); #endif Services = ConfigureServices(appInfoService); IconProvider.Initialize(Services); this.InitializeComponent(); // Ensure types used in XAML are preserved for AOT compilation TypePreservation.PreserveTypes(); NativeEventWaiter.WaitForEventLoop( "Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () => { EtwTrace?.Dispose(); AppWindow?.Close(); Environment.Exit(0); }); // Connect the PT logging to the core project's logging. // This way, log statements from the core project will be captured by the PT logs var logWrapper = new LogWrapper(); CoreLogger.InitializeLogger(logWrapper); // Now that CoreLogger is initialized, initialize the logger delegate in ApplicationInfoService appInfoService.SetLogDirectory(() => Logger.CurrentVersionLogDirectoryPath); } /// /// Invoked when the application is launched. /// /// Details about the launch request and process. protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { AppWindow = new MainWindow(); var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs(); ((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs); } /// /// Configures the services for the application /// private static ServiceProvider ConfigureServices(IApplicationInfoService appInfoService) { // 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 ServiceCollection services = new(); // Root services services.AddSingleton(TaskScheduler.FromCurrentSynchronizationContext()); var dispatcherQueue = DispatcherQueue.GetForCurrentThread(); AddBuiltInCommands(services, appInfoService.ConfigDirectory); AddCoreServices(services, appInfoService); AddUIServices(services, dispatcherQueue); return services.BuildServiceProvider(); } private static void AddBuiltInCommands(ServiceCollection services, string configDirectory) { var providerLoadGuard = new ProviderLoadGuard(configDirectory); // Built-in Commands. Order matters - this is the order they'll be presented by default. var allApps = new AllAppsCommandProvider(); var files = new IndexerCommandsProvider(); files.SuppressFallbackWhen(ShellCommandsProvider.SuppressFileFallbackIf); services.AddSingleton(allApps); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(files); services.AddSingleton(_ => BookmarksCommandProvider.CreateWithDefaultStore()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); // GH #38440: Users might not have WinGet installed! Or they might have // a ridiculously old version. Or might be running as admin. // We shouldn't explode in the App ctor if we fail to instantiate an // instance of PackageManager, which will happen in the static ctor // for WinGetStatics try { var winget = new WinGetExtensionCommandsProvider(); winget.SetAllLookup( query => allApps.LookupAppByPackageFamilyName(query, requireSingleMatch: true), query => allApps.LookupAppByProductCode(query, requireSingleMatch: true)); services.AddSingleton(winget); } catch (Exception ex) { Logger.LogError("Couldn't load winget", ex); } services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); var performanceMonitorSoftDisabled = providerLoadGuard.IsProviderDisabled(PerformanceMonitorCommandsProvider.ProviderIdValue); if (performanceMonitorSoftDisabled) { Logger.LogWarning("Performance monitor is temporarily disabled after repeated crashes. Loading placeholder pages instead of activating performance counters."); } if (!performanceMonitorSoftDisabled) { providerLoadGuard.Enter(PerformanceMonitorCommandsProvider.ProviderLoadGuardBlockId, PerformanceMonitorCommandsProvider.ProviderIdValue); } try { var performanceMonitor = new PerformanceMonitorCommandsProvider(performanceMonitorSoftDisabled); services.AddSingleton(performanceMonitor); if (!performanceMonitorSoftDisabled) { providerLoadGuard.Exit(PerformanceMonitorCommandsProvider.ProviderLoadGuardBlockId); } } catch (Exception ex) { if (!performanceMonitorSoftDisabled) { providerLoadGuard.Exit(PerformanceMonitorCommandsProvider.ProviderLoadGuardBlockId); } Logger.LogError("Couldn't load performance monitor", ex); } } private static void AddUIServices(ServiceCollection services, DispatcherQueue dispatcherQueue) { // Models & persistence services services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); // Services services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddIconServices(dispatcherQueue); } private static void AddCoreServices(ServiceCollection services, IApplicationInfoService appInfoService) { // Core services services.AddSingleton(appInfoService); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton( _ => new FuzzyMatcherProvider(new PrecomputedFuzzyMatcherOptions(), new PinyinFuzzyMatcherOptions())); // ViewModels services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } public void Dispose() { _globalErrorHandler.Dispose(); EtwTrace.Dispose(); GC.SuppressFinalize(this); } }