From 980ca46cdb0602c5686ade5265800c300dcfc400 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 6 May 2025 22:30:47 -0500 Subject: [PATCH] CmdPal: URI activate, rather than using shell:AppsFolder (#39269) * Use a URI handler for launching this is a test * I think we can review this * Add a settings URI too --- .../cmdpal/CmdPalModuleInterface/dllmain.cpp | 7 ++-- .../cmdpal/Microsoft.CmdPal.UI/App.xaml.cs | 20 ++-------- .../Microsoft.CmdPal.UI/MainWindow.xaml.cs | 40 ++++++++++++++++++- .../Microsoft.CmdPal.UI/NativeMethods.txt | 3 +- .../Package-Dev.appxmanifest | 7 ++++ .../Microsoft.CmdPal.UI/Package.appxmanifest | 8 ++++ .../Pages/ShellPage.xaml.cs | 17 +++++--- .../cmdpal/Microsoft.CmdPal.UI/Program.cs | 36 ++++++++++++++++- .../Properties/launchSettings.json | 3 +- 9 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp index bff7279b68..48bfcd1411 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp +++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp @@ -217,10 +217,9 @@ public: CmdPal::m_enabled.store(true); std::wstring packageName = L"Microsoft.CommandPalette"; - std::wstring launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette_8wekyb3d8bbwe!App"; + std::wstring launchPath = L"x-cmdpal://background"; #ifdef IS_DEV_BRANDING packageName = L"Microsoft.CommandPalette.Dev"; - launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App"; #endif if (!package::GetRegisteredPackage(packageName, false).has_value()) @@ -269,7 +268,7 @@ public: if (!firstEnableCall) { Logger::trace("Not first attempt, try to launch"); - LaunchApp(launchPath, L"RunFromPT", false /*no elevated*/, false /*error pop up*/); + LaunchApp(launchPath, L"", false /*no elevated*/, false /*error pop up*/); } else { @@ -297,7 +296,7 @@ public: int retry = 0; do { - auto launch_result = LaunchApp(path, L"RunFromPT", false, retry < max_retry); + auto launch_result = LaunchApp(path, L"", false, retry < max_retry); if (launch_result) { Logger::info(L"CmdPal launched successfully after {} retries.", retry); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs index 74b25d35c4..ba4a298665 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs @@ -73,26 +73,12 @@ public partial class App : Application /// Invoked when the application is launched. /// /// Details about the launch request and process. - protected override void OnLaunched(LaunchActivatedEventArgs args) + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { AppWindow = new MainWindow(); - var cmdArgs = Environment.GetCommandLineArgs(); - - var runFromPT = false; - foreach (var arg in cmdArgs) - { - if (arg == "RunFromPT") - { - runFromPT = true; - break; - } - } - - if (!runFromPT) - { - AppWindow.Activate(); - } + var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs(); + ((MainWindow)AppWindow).HandleLaunch(activatedEventArgs); } /// diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index 53635c30e5..d4d868b581 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -19,6 +19,8 @@ using Microsoft.UI.Composition.SystemBackdrops; using Microsoft.UI.Input; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; +using Microsoft.Windows.AppLifecycle; +using Windows.ApplicationModel.Activation; using Windows.Foundation; using Windows.Graphics; using Windows.UI; @@ -240,7 +242,7 @@ public sealed partial class MainWindow : WindowEx, unsafe { BOOL value = false; - PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, (void*)&value, (uint)sizeof(BOOL)); + PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, &value, (uint)sizeof(BOOL)); } PInvoke.SetForegroundWindow(hwnd); @@ -320,7 +322,7 @@ public sealed partial class MainWindow : WindowEx, unsafe { BOOL value = true; - PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, (void*)&value, (uint)sizeof(BOOL)); + PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, &value, (uint)sizeof(BOOL)); } } @@ -423,6 +425,40 @@ public sealed partial class MainWindow : WindowEx, } } + public void HandleLaunch(AppActivationArguments? activatedEventArgs) + { + if (activatedEventArgs == null) + { + Summon(string.Empty); + return; + } + + if (activatedEventArgs.Kind == Microsoft.Windows.AppLifecycle.ExtendedActivationKind.Protocol) + { + if (activatedEventArgs.Data is IProtocolActivatedEventArgs protocolArgs) + { + if (protocolArgs.Uri.ToString() is string uri) + { + // was the URI "x-cmdpal://background" ? + if (uri.StartsWith("x-cmdpal://background", StringComparison.OrdinalIgnoreCase)) + { + // we're running, we don't want to activate our window. bail + return; + } + else if (uri.StartsWith("x-cmdpal://settings", StringComparison.OrdinalIgnoreCase)) + { + WeakReferenceMessenger.Default.Send(new()); + return; + } + } + + return; + } + } + + Activate(); + } + public void Summon(string commandId) => // The actual showing and hiding of the window will be done by the diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt index 7350bbbc12..c39ed38300 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt @@ -39,6 +39,7 @@ WM_RBUTTONUP WM_LBUTTONUP WM_LBUTTONDBLCLK +MessageBox DwmGetWindowAttribute DwmSetWindowAttribute -DWM_CLOAKED_APP \ No newline at end of file +DWM_CLOAKED_APP diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Package-Dev.appxmanifest b/src/modules/cmdpal/Microsoft.CmdPal.UI/Package-Dev.appxmanifest index 0d8114eb0c..f8a16a433b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Package-Dev.appxmanifest +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Package-Dev.appxmanifest @@ -70,6 +70,13 @@ DisplayName="ms-resource:StartupTaskNameDev" /> + + + Assets\StoreLogo.png + Command Palette Dev URI scheme + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest b/src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest index 9dec756883..4020c10ead 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest @@ -70,6 +70,14 @@ DisplayName="ms-resource:StartupTaskName" /> + + + + Assets\StoreLogo.png + Command Palette URI scheme + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs index 09adffd4fe..4bce9de184 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs @@ -418,15 +418,20 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, { _ = DispatcherQueue.TryEnqueue(() => { - if (_settingsWindow == null) - { - _settingsWindow = new SettingsWindow(); - } - - _settingsWindow.Activate(); + OpenSettings(); }); } + public void OpenSettings() + { + if (_settingsWindow == null) + { + _settingsWindow = new SettingsWindow(); + } + + _settingsWindow.Activate(); + } + public void Receive(ShowDetailsMessage message) { // TERRIBLE HACK TODO GH #245 diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Program.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Program.cs index 2e1f916115..ffe2f45176 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Program.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Program.cs @@ -2,10 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.InteropServices; using ManagedCommon; using Microsoft.CmdPal.UI.Events; using Microsoft.PowerToys.Telemetry; using Microsoft.Windows.AppLifecycle; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; namespace Microsoft.CmdPal.UI; @@ -30,7 +34,33 @@ internal sealed class Program return 0; } - Logger.InitializeLogger("\\CmdPal\\Logs\\"); + try + { + Logger.InitializeLogger("\\CmdPal\\Logs\\"); + } + catch (COMException e) + { + // This is unexpected. For the sake of debugging: + // pop a message box + PInvoke.MessageBox( + (HWND)IntPtr.Zero, + $"Failed to initialize the logger. COMException: \r{e.Message}", + "Command Palette", + MESSAGEBOX_STYLE.MB_OK | MESSAGEBOX_STYLE.MB_ICONERROR); + return 0; + } + catch (Exception e2) + { + // This is unexpected. For the sake of debugging: + // pop a message box + PInvoke.MessageBox( + (HWND)IntPtr.Zero, + $"Failed to initialize the logger. Unknown Exception: \r{e2.Message}", + "Command Palette", + MESSAGEBOX_STYLE.MB_OK | MESSAGEBOX_STYLE.MB_ICONERROR); + return 0; + } + Logger.LogDebug($"Starting at {DateTime.UtcNow}"); PowerToysTelemetry.Log.WriteEvent(new CmdPalProcessStarted()); @@ -79,7 +109,9 @@ internal sealed class Program if (thisApp.AppWindow is not null and MainWindow mainWindow) { - mainWindow.Summon(string.Empty); + mainWindow.HandleLaunch(args); + + // mainWindow.Summon(string.Empty); } } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Properties/launchSettings.json b/src/modules/cmdpal/Microsoft.CmdPal.UI/Properties/launchSettings.json index 68daa60f79..febacfc92e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Properties/launchSettings.json +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Properties/launchSettings.json @@ -2,7 +2,8 @@ "profiles": { "Microsoft.CmdPal.UI (Package)": { "commandName": "MsixPackage", - "nativeDebugging": false + "nativeDebugging": false, + "doNotLaunchApp": false }, "Microsoft.CmdPal.UI (Unpackaged)": { "commandName": "Project"