// 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);
}
}