Repository: microsoft/PowerToys
Pull request #39039: [CmdPal] Launch cmd pal with retry
Author: login: vanzue
Labels: name: Product-Command Palette
description: Refers to the Command Palette utility
Created: 2025-04-23T06:30:15Z | Merged: 2025-04-27T05:47:56Z

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
   - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries
   - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder
   - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects
   - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
### Logs for first time launch without error:
[2025-04-24 11:13:06.443568] [p-41992] [t-43748] [info] Register C:\PowerToys\ARM64\Debug\WinUI3Apps\CmdPal\AppPackages\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\Microsoft.CmdPal.UI_0.0.1.0_arm64_Debug.msix package completed.
[2025-04-24 11:13:15.538459] [p-41992] [t-43748] [trace] Try to launch
[2025-04-24 11:13:24.362630] [p-41992] [t-43480] [info] CmdPal launched successfully after 0 retries.

### Logs for first time launch with error:
[2025-04-24 11:13:01.715143] [p-41992] [t-43748] [info] Dependency already satisfied: C:\PowerToys\ARM64\Debug\WinUI3Apps\CmdPal\AppPackages\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\Dependencies\ARM64\Microsoft.VCLibs.ARM64.Debug.14.00.Desktop.appx
[2025-04-24 11:13:06.443568] [p-41992] [t-43748] [info] Register C:\PowerToys\ARM64\Debug\WinUI3Apps\CmdPal\AppPackages\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\Microsoft.CmdPal.UI_0.0.1.0_arm64_Debug.msix package completed.
[2025-04-24 11:13:15.538459] [p-41992] [t-43748] [trace] Try to launch
[2025-04-24 11:13:24.362630] [p-41992] [t-43480] [info] CmdPal launched successfully after 0 retries.
[2025-04-24 11:18:09.597108] [p-50284] [t-9788] [info] CmdPal logger is initialized
[2025-04-24 11:18:10.071036] [p-50284] [t-9788] [trace] CmdPal::enable()
[2025-04-24 11:18:13.585333] [p-50284] [t-9788] [trace] Try to launch
[2025-04-24 11:18:16.450335] [p-50284] [t-47608] [error] Failed to launch process. The operation was canceled by the user.
[2025-04-24 11:18:19.060699] [p-50284] [t-47608] [error] Failed to launch process. The operation was canceled by the user.
[2025-04-24 11:18:23.215114] [p-50284] [t-47608] [error] Failed to launch process. The operation was canceled by the user.
[2025-04-24 11:20:34.103856] [p-20208] [t-13020] [info] CmdPal logger is initialized
[2025-04-24 11:20:34.305567] [p-20208] [t-13020] [trace] CmdPal::enable()
[2025-04-24 11:21:19.493065] [p-20208] [t-13020] [trace] Try to launch
[2025-04-24 11:21:28.307382] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:21:29.339283] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:21:31.373919] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:21:35.409065] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:21:43.470662] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:21:59.524466] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:22:31.596675] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:23:35.642414] [p-20208] [t-26820] [error] Failed to launch process. The system cannot find the file specified.
[2025-04-24 11:25:47.615733] [p-20208] [t-26820] [error] Failed to launch process. The operation was canceled by the user.
[2025-04-24 11:30:06.046710] [p-20208] [t-26820] [error] Failed to launch process. The operation was canceled by the user.
[2025-04-24 11:30:06.056510] [p-20208] [t-26820] [error] CmdPal launch failed after 10 retries.



Diff:
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj
index 5bd12f316e06..2d70013009e8 100644
--- a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj
+++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj
@@ -2,7 +2,7 @@
 <Project DefaultTargets="Build"
   xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
-  <Import Project="..\Microsoft.CmdPal.UI\CmdPal.pre.props" Condition="Exists('..\Microsoft.CmdPal.UI\CmdPal.pre.prop')" />
+  <Import Project="..\Microsoft.CmdPal.UI\CmdPal.pre.props" Condition="Exists('..\Microsoft.CmdPal.UI\CmdPal.pre.props')" />
   <PropertyGroup Label="Globals">
     <VCProjectVersion>17.0</VCProjectVersion>
     <Keyword>Win32Proj</Keyword>
@@ -53,7 +53,6 @@
       <PreprocessorDefinitions>
         EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;
         %(PreprocessorDefinitions);
-        $(CommandPaletteBranding)
       </PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(CommandPaletteBranding)'=='' or '$(CommandPaletteBranding)'=='Dev'">
         IS_DEV_BRANDING;%(PreprocessorDefinitions)
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
index be3eb6a3b7bd..bff7279b6804 100644
--- a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
+++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
@@ -3,6 +3,7 @@
 
 #include <interface/powertoy_module_interface.h>
 
+#include <atomic>
 #include <common/logger/logger.h>
 #include <common/utils/logger_helper.h>
 #include <common/SettingsAPI/settings_helpers.h>
@@ -10,10 +11,11 @@
 #include <common/utils/resources.h>
 #include <common/utils/package.h>
 #include <common/utils/process_path.h>
+#include <common/utils/winapi_error.h>
 #include <common/interop/shared_constants.h>
 #include <Psapi.h>
 #include <TlHelp32.h>
-#include <common/utils/winapi_error.h>
+#include <thread>
 
 HINSTANCE g_hInst_cmdPal = 0;
 
@@ -37,8 +39,6 @@ BOOL APIENTRY DllMain(HMODULE hInstance,
 class CmdPal : public PowertoyModuleIface
 {
 private:
-    bool m_enabled = false;
-
     std::wstring app_name;
 
     //contains the non localized key of the powertoy
@@ -46,7 +46,10 @@ class CmdPal : public PowertoyModuleIface
 
     HANDLE m_hTerminateEvent;
 
-    void LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
+    // Track if this is the first call to enable
+    bool firstEnableCall = true;
+
+    static bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated, bool silentFail)
     {
         std::wstring dir = std::filesystem::path(appPath).parent_path();
 
@@ -54,6 +57,10 @@ class CmdPal : public PowertoyModuleIface
         sei.cbSize = sizeof(SHELLEXECUTEINFO);
         sei.hwnd = nullptr;
         sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
+        if (silentFail)
+        {
+            sei.fMask = sei.fMask | SEE_MASK_FLAG_NO_UI;
+        }
         sei.lpVerb = elevated ? L"runas" : L"open";
         sei.lpFile = appPath.c_str();
         sei.lpParameters = commandLineArgs.c_str();
@@ -64,7 +71,11 @@ class CmdPal : public PowertoyModuleIface
         {
             std::wstring error = get_last_error_or_default(GetLastError());
             Logger::error(L"Failed to launch process. {}", error);
+            return false;
         }
+
+        m_launched.store(true);
+        return true;
     }
 
     std::vector<DWORD> GetProcessesIdByName(const std::wstring& processName)
@@ -122,6 +133,9 @@ class CmdPal : public PowertoyModuleIface
     }
 
 public:
+    static std::atomic<bool> m_enabled;
+    static std::atomic<bool> m_launched;
+
     CmdPal()
     {
         app_name = L"CmdPal";
@@ -133,10 +147,7 @@ class CmdPal : public PowertoyModuleIface
 
     ~CmdPal()
     {
-        if (m_enabled)
-        {
-        }
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
     }
 
     // Destroy the powertoy and free memory
@@ -203,15 +214,18 @@ class CmdPal : public PowertoyModuleIface
     {
         Logger::trace("CmdPal::enable()");
 
-        m_enabled = true;
+        CmdPal::m_enabled.store(true);
 
-        try
-        {
-            std::wstring packageName = L"Microsoft.CommandPalette";
+        std::wstring packageName = L"Microsoft.CommandPalette";
+        std::wstring launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette_8wekyb3d8bbwe!App";
 #ifdef IS_DEV_BRANDING
-            packageName = L"Microsoft.CommandPalette.Dev";
+        packageName = L"Microsoft.CommandPalette.Dev";
+        launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App";
 #endif
-            if (!package::GetRegisteredPackage(packageName, false).has_value())
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
+        {
+            try
             {
                 Logger::info(L"CmdPal not installed. Installing...");
 
@@ -238,28 +252,34 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
-        try
+
+        if (!firstEnableCall)
         {
-#ifdef IS_DEV_BRANDING
-            LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-            LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+            Logger::trace("Not first attempt, try to launch");
+            LaunchApp(launchPath, L"RunFromPT", false /*no elevated*/, false /*error pop up*/);
         }
-        catch (std::exception& e)
+        else
         {
-            std::string errorMessage{ "Exception thrown while trying to launch CmdPal: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
-            throw;
+            // If first time enable, do retry launch.
+            Logger::trace("First attempt, try to launch");
+            std::thread launchThread(&CmdPal::RetryLaunch, launchPath);
+            launchThread.detach();
         }
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
@@ -267,7 +287,44 @@ class CmdPal : public PowertoyModuleIface
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = 9; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry < max_retry);
+            if (launch_result)
+            {
+                Logger::info(L"CmdPal launched successfully after {} retries.", retry);
+                return;
+            }
+            else
+            {
+                Logger::error(L"Retry {} launch CmdPal launch failed.", retry);
+            }
+
+            // When we got max retry, we don't need to wait for the next retry.
+            if (retry < max_retry)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+            ++retry;
+        } while (retry <= max_retry && m_enabled.load() && !m_launched.load());
+
+        if (!m_enabled.load() || m_launched.load())
+        {
+            Logger::error(L"Retry cancelled. CmdPal is disabled or already launched.");
+        }
+        else
+        {
+            Logger::error(L"CmdPal launch failed after {} attempts.", retry);
+        }
     }
 
     virtual bool on_hotkey(size_t) override
@@ -282,11 +339,14 @@ class CmdPal : public PowertoyModuleIface
 
     virtual bool is_enabled() override
     {
-        return m_enabled;
+        return CmdPal::m_enabled.load();
     }
 };
 
+std::atomic<bool> CmdPal::m_enabled{ false };
+std::atomic<bool> CmdPal::m_launched{ false };
+
 extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
 {
     return new CmdPal();
-}
+}
\ No newline at end of file


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR introduces a retry mechanism for launching CmdPal, ensuring improved robustness when the initial launch attempt fails.  
- Added a new static RetryLaunch method with exponential backoff delays.  
- Refactored launch and enable logic by using atomic booleans and isolating first-time enable behavior.


<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp:50**
* [nitpick] Consider renaming 'firstEnableCall' to 'isFirstEnableCall' to clearly indicate its boolean nature and improve code readability.
```
bool firstEnableCall = true;
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T05:10:06Z
user: login: yeelam-gordon
body: Looks good overall, let's talk in case for the detail of comment here.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T05:47:31Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T06:38:04Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T06:42:22Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T06:44:07Z
user: login: yeelam-gordon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T06:53:05Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T07:10:34Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T07:15:05Z
user: login: yeelam-gordon
body: Talk with Kai offline, he will make major change we agree on, and just need one more reviewer to review later on
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T10:20:26Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T12:15:10Z
user: login: vanzue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T12:15:24Z
user: login: shuaiyuanxx
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T03:45:56Z
user: login: moooyo
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T05:22:44Z
user: login: yeelam-gordon
body: Good Changes, thanks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T08:00:44Z

Review comments:
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: Let's keep original code on launch normal in case it is !firstEnableCall
I do have reason on it and you will see later on.
The major purpose is keep logic for "Retry" only a special case for firstTimeRetry, 
created_at: 2025-04-24T04:56:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: We should check if it is still "enable", as well as "launched successfully once" in the past here before call launchApp.
created_at: 2025-04-24T05:00:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
+            if (launch_result)
+            {
+                Logger::info(L"CmdPal launched successfully after {} retries.", retry);
+                m_launched.store(true);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: I will suggest to move this one to LaunchApp method.
As the result, if there is other calling LaunchApp other than this "First time RetryMethod", then we can stop.
created_at: 2025-04-24T05:05:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -122,6 +132,9 @@ class CmdPal : public PowertoyModuleIface
     }
 
 public:
+    static std::atomic<bool> m_enabled;
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: Given, it is just a bool, we don't have problem on concurrent read here.
For concurrent write, we also don't have, thus I think we don't need to make it as atomic here.

instead, they can be just like the firstEnableCall
created_at: 2025-04-24T05:07:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: Let's name this as "FirstTimeRetryLaunch", we can get rid of the bool here.
created_at: 2025-04-24T05:08:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -122,6 +132,9 @@ class CmdPal : public PowertoyModuleIface
     }
 
 public:
+    static std::atomic<bool> m_enabled;
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: I don't think raw bool will make sure thread visibility on change, but atomic<bool> will
created_at: 2025-04-24T06:38:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: What do you mean? This method should be called in only if it's by enable(). And first time attempt is safe. The second time will check enable in line 311
created_at: 2025-04-24T06:42:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
+            if (launch_result)
+            {
+                Logger::info(L"CmdPal launched successfully after {} retries.", retry);
+                m_launched.store(true);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: I mean they are for different purpose with different logic unit.  Why we should mix them into one?
created_at: 2025-04-24T06:44:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: yeelam-gordon
body: line 311 is not right after the std::this_thread::sleep_for.
NOTE: line 311 make senses now.

And here, right after "std::this_thread::sleep_for", we should check again, since the world can be changed already.
created_at: 2025-04-24T06:52:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: Make sense, check should be right after sleep.
created_at: 2025-04-24T07:10:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: Retry covers logic for retry or not retry. That function covers logic for handling whether retry and retry count management. 
We need to check whether retry, and I put logic inside retry, this keeps code here clean to me
created_at: 2025-04-24T07:15:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
+            if (launch_result)
+            {
+                Logger::info(L"CmdPal launched successfully after {} retries.", retry);
+                m_launched.store(true);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: Done, thanks!
created_at: 2025-04-24T12:15:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,27 +255,62 @@ class CmdPal : public PowertoyModuleIface
                     }
                 }
             }
+            catch (std::exception& e)
+            {
+                std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+                errorMessage += e.what();
+                Logger::error(errorMessage);
+            }
         }
-        catch (std::exception& e)
+
+        if (!package::GetRegisteredPackage(packageName, false).has_value())
         {
-            std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
-            errorMessage += e.what();
-            Logger::error(errorMessage);
+            Logger::error("Cmdpal is not registered, quit..");
+            return;
         }
 
-#if _DEBUG
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#else
-        LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false);
-#endif
+        Logger::trace("Try to launch");
+
+        std::thread launchThread(&CmdPal::RetryLaunch, firstEnableCall, launchPath);
+        launchThread.detach();
+
+        firstEnableCall = false;
     }
 
     virtual void disable()
     {
         Logger::trace("CmdPal::disable()");
         TerminateCmdPal();
 
-        m_enabled = false;
+        CmdPal::m_enabled.store(false);
+    }
+
+    static void RetryLaunch(bool first_time_launch, std::wstring path)
+    {
+        const int base_delay_milliseconds = 1000;
+        int max_retry = first_time_launch ? 9 : 1; // 2**9 - 1 seconds. Control total wait time within 10 min.
+        int retry = 0;
+        do
+        {
+            if (retry > 0)
+            {
+                int delay = base_delay_milliseconds * (1 << (retry - 1));
+                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+            }
+
+            auto launch_result = LaunchApp(path, L"RunFromPT", false, retry == max_retry - 1);
path: src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
user: login: vanzue
body: Done, Thanks for review!
created_at: 2025-04-24T12:15:24Z
author_association: CONTRIBUTOR

PR comments: