CustomAction.cpp
   1  #include "pch.h"
   2  #include "resource.h"
   3  #include "RcResource.h"
   4  #include <ProjectTelemetry.h>
   5  #include <spdlog/sinks/base_sink.h>
   6  #include <filesystem>
   7  #include <string_view>
   8  
   9  #include "../../src/common/logger/logger.h"
  10  #include "../../src/common/utils/gpo.h"
  11  #include "../../src/common/utils/MsiUtils.h"
  12  #include "../../src/common/utils/modulesRegistry.h"
  13  #include "../../src/common/updating/installer.h"
  14  #include "../../src/common/version/version.h"
  15  #include "../../src/common/Telemetry/EtwTrace/EtwTrace.h"
  16  #include "../../src/common/utils/package.h"
  17  #include "../../src/common/utils/clean_video_conference.h"
  18  
  19  #include <winrt/Windows.ApplicationModel.h>
  20  #include <winrt/Windows.Foundation.h>
  21  #include <winrt/Windows.Management.Deployment.h>
  22  #include <winrt/Windows.Security.Credentials.h>
  23  
  24  #include <wtsapi32.h>
  25  #include <processthreadsapi.h>
  26  #include <UserEnv.h>
  27  #include <winnt.h>
  28  
  29  using namespace std;
  30  
  31  HINSTANCE DLL_HANDLE = nullptr;
  32  
  33  TRACELOGGING_DEFINE_PROVIDER(
  34      g_hProvider,
  35      "Microsoft.PowerToys",
  36      // {38e8889b-9731-53f5-e901-e8a7c1753074}
  37      (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
  38      TraceLoggingOptionProjectTelemetry());
  39  
  40  const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
  41  const DWORD USERNAME_LEN = UNLEN + 1;                // User Name + '\0'
  42  
  43  static const wchar_t *POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
  44  static const wchar_t *POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
  45  
  46  constexpr inline const wchar_t *DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
  47  constexpr inline const wchar_t *DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
  48  
  49  #define TraceLoggingWriteWrapper(provider, eventName, ...)   \
  50      if (isDataDiagnosticEnabled())                           \
  51      {                                                        \
  52          trace.UpdateState(true);                             \
  53          TraceLoggingWrite(provider, eventName, __VA_ARGS__); \
  54          trace.Flush();                                       \
  55          trace.UpdateState(false);                            \
  56      }
  57  
  58  static Shared::Trace::ETWTrace trace{L"PowerToys_Installer"};
  59  
  60  inline bool isDataDiagnosticEnabled()
  61  {
  62      HKEY key{};
  63      if (RegOpenKeyExW(HKEY_CURRENT_USER,
  64                        DataDiagnosticsRegKey,
  65                        0,
  66                        KEY_READ,
  67                        &key) != ERROR_SUCCESS)
  68      {
  69          return false;
  70      }
  71  
  72      DWORD isDataDiagnosticsEnabled = 0;
  73      DWORD size = sizeof(isDataDiagnosticsEnabled);
  74  
  75      if (RegGetValueW(
  76              HKEY_CURRENT_USER,
  77              DataDiagnosticsRegKey,
  78              DataDiagnosticsRegValueName,
  79              RRF_RT_REG_DWORD,
  80              nullptr,
  81              &isDataDiagnosticsEnabled,
  82              &size) != ERROR_SUCCESS)
  83      {
  84          RegCloseKey(key);
  85          return false;
  86      }
  87      RegCloseKey(key);
  88  
  89      return isDataDiagnosticsEnabled == 1;
  90  }
  91  
  92  HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring &installationDir)
  93  {
  94      DWORD len = 0;
  95      wchar_t _[1];
  96      MsiGetPropertyW(hInstall, L"CustomActionData", _, &len);
  97      len += 1;
  98      installationDir.resize(len);
  99      HRESULT hr = MsiGetPropertyW(hInstall, L"CustomActionData", installationDir.data(), &len);
 100      if (installationDir.length())
 101      {
 102          installationDir.resize(installationDir.length() - 1);
 103      }
 104      ExitOnFailure(hr, "Failed to get INSTALLFOLDER property.");
 105  LExit:
 106      return hr;
 107  }
 108  
 109  BOOL IsLocalSystem()
 110  {
 111      HANDLE hToken;
 112      UCHAR bTokenUser[sizeof(TOKEN_USER) + 8 + 4 * SID_MAX_SUB_AUTHORITIES];
 113      PTOKEN_USER pTokenUser = (PTOKEN_USER)bTokenUser;
 114      ULONG cbTokenUser;
 115      SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
 116      PSID pSystemSid;
 117      BOOL bSystem;
 118  
 119      // open process token
 120      if (!OpenProcessToken(GetCurrentProcess(),
 121                            TOKEN_QUERY,
 122                            &hToken))
 123          return FALSE;
 124  
 125      // retrieve user SID
 126      if (!GetTokenInformation(hToken, TokenUser, pTokenUser,
 127                               sizeof(bTokenUser), &cbTokenUser))
 128      {
 129          CloseHandle(hToken);
 130          return FALSE;
 131      }
 132  
 133      CloseHandle(hToken);
 134  
 135      // allocate LocalSystem well-known SID
 136      if (!AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID,
 137                                    0, 0, 0, 0, 0, 0, 0, &pSystemSid))
 138          return FALSE;
 139  
 140      // compare the user SID from the token with the LocalSystem SID
 141      bSystem = EqualSid(pTokenUser->User.Sid, pSystemSid);
 142  
 143      FreeSid(pSystemSid);
 144  
 145      return bSystem;
 146  }
 147  
 148  BOOL ImpersonateLoggedInUserAndDoSomething(std::function<bool(HANDLE userToken)> action)
 149  {
 150      HRESULT hr = S_OK;
 151      HANDLE hUserToken = NULL;
 152      DWORD dwSessionId;
 153      ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId);
 154      auto rv = WTSQueryUserToken(dwSessionId, &hUserToken);
 155  
 156      if (rv == 0)
 157      {
 158          hr = E_ABORT;
 159          ExitOnFailure(hr, "Failed to query user token");
 160      }
 161  
 162      HANDLE hUserTokenDup;
 163      if (DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation, TOKEN_TYPE::TokenPrimary, &hUserTokenDup) == 0)
 164      {
 165          CloseHandle(hUserToken);
 166          CloseHandle(hUserTokenDup);
 167          hr = E_ABORT;
 168          ExitOnFailure(hr, "Failed to duplicate user token");
 169      }
 170  
 171      if (ImpersonateLoggedOnUser(hUserTokenDup))
 172      {
 173          if (!action(hUserTokenDup))
 174          {
 175              hr = E_ABORT;
 176              ExitOnFailure(hr, "Failed to execute action");
 177          }
 178  
 179          RevertToSelf();
 180          CloseHandle(hUserToken);
 181          CloseHandle(hUserTokenDup);
 182      }
 183      else
 184      {
 185          hr = E_ABORT;
 186          ExitOnFailure(hr, "Failed to duplicate user token");
 187      }
 188  
 189  LExit:
 190      return SUCCEEDED(hr);
 191  }
 192  
 193  static std::filesystem::path GetUserPowerShellModulesPath()
 194  {
 195      PWSTR myDocumentsBlockPtr;
 196  
 197      if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocumentsBlockPtr)))
 198      {
 199          const std::wstring myDocuments{myDocumentsBlockPtr};
 200          CoTaskMemFree(myDocumentsBlockPtr);
 201          return std::filesystem::path(myDocuments) / "PowerShell" / "Modules";
 202      }
 203      else
 204      {
 205          CoTaskMemFree(myDocumentsBlockPtr);
 206          return {};
 207      }
 208  }
 209  
 210  UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
 211  {
 212      HRESULT hr = S_OK;
 213      UINT er = ERROR_SUCCESS;
 214      std::wstring installationFolder, path, args;
 215      std::wstring commandLine;
 216  
 217      hr = WcaInitialize(hInstall, "LaunchPowerToys");
 218      ExitOnFailure(hr, "Failed to initialize");
 219      hr = getInstallFolder(hInstall, installationFolder);
 220      ExitOnFailure(hr, "Failed to get installFolder.");
 221  
 222      path = installationFolder;
 223      path += L"\\PowerToys.exe";
 224  
 225      args = L"--dont-elevate";
 226  
 227      commandLine = L"\"" + path + L"\" ";
 228      commandLine += args;
 229  
 230      BOOL isSystemUser = IsLocalSystem();
 231  
 232      if (isSystemUser)
 233      {
 234  
 235          auto action = [&commandLine](HANDLE userToken)
 236          {
 237              STARTUPINFO startupInfo = { 0 };
 238              startupInfo.cb = sizeof(STARTUPINFO);
 239              startupInfo.wShowWindow = SW_SHOWNORMAL;
 240              PROCESS_INFORMATION processInformation;
 241  
 242              PVOID lpEnvironment = NULL;
 243              CreateEnvironmentBlock(&lpEnvironment, userToken, FALSE);
 244  
 245              CreateProcessAsUser(
 246                  userToken,
 247                  NULL,
 248                  commandLine.data(),
 249                  NULL,
 250                  NULL,
 251                  FALSE,
 252                  CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
 253                  lpEnvironment,
 254                  NULL,
 255                  &startupInfo,
 256                  &processInformation);
 257  
 258              if (!CloseHandle(processInformation.hProcess))
 259              {
 260                  return false;
 261              }
 262              if (!CloseHandle(processInformation.hThread))
 263              {
 264                  return false;
 265              }
 266  
 267              return true;
 268          };
 269  
 270          if (!ImpersonateLoggedInUserAndDoSomething(action))
 271          {
 272              hr = E_ABORT;
 273              ExitOnFailure(hr, "ImpersonateLoggedInUserAndDoSomething failed");
 274          }
 275      }
 276      else
 277      {
 278          STARTUPINFO startupInfo = { 0 };
 279          startupInfo.cb = sizeof(STARTUPINFO);
 280          startupInfo.wShowWindow = SW_SHOWNORMAL;
 281  
 282          PROCESS_INFORMATION processInformation;
 283  
 284          // Start the resizer
 285          CreateProcess(
 286              NULL,
 287              commandLine.data(),
 288              NULL,
 289              NULL,
 290              TRUE,
 291              0,
 292              NULL,
 293              NULL,
 294              &startupInfo,
 295              &processInformation);
 296  
 297          if (!CloseHandle(processInformation.hProcess))
 298          {
 299              hr = E_ABORT;
 300              ExitOnFailure(hr, "Failed to close process handle");
 301          }
 302          if (!CloseHandle(processInformation.hThread))
 303          {
 304              hr = E_ABORT;
 305              ExitOnFailure(hr, "Failed to close thread handle");
 306          }
 307      }
 308  
 309  LExit:
 310      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 311      return WcaFinalize(er);
 312  }
 313  
 314  UINT __stdcall CheckGPOCA(MSIHANDLE hInstall)
 315  {
 316      HRESULT hr = S_OK;
 317  
 318      hr = WcaInitialize(hInstall, "CheckGPOCA");
 319      ExitOnFailure(hr, "Failed to initialize");
 320  
 321      LPWSTR currentScope = nullptr;
 322      hr = WcaGetProperty(L"InstallScope", &currentScope);
 323  
 324      if (std::wstring{currentScope} == L"perUser")
 325      {
 326          if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled)
 327          {
 328              PMSIHANDLE hRecord = MsiCreateRecord(0);
 329              MsiRecordSetString(hRecord, 0, TEXT("The system administrator has disabled per-user installation."));
 330              MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
 331              hr = E_ABORT;
 332          }
 333      }
 334  
 335  LExit:
 336      UINT er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 337      return WcaFinalize(er);
 338  }
 339  
 340  // We've deprecated Video Conference Mute. This Custom Action cleans up any stray registry entry for the driver dll.
 341  UINT __stdcall CleanVideoConferenceRegistryCA(MSIHANDLE hInstall)
 342  {
 343      HRESULT hr = S_OK;
 344      UINT er = ERROR_SUCCESS;
 345      hr = WcaInitialize(hInstall, "CleanVideoConferenceRegistry");
 346      ExitOnFailure(hr, "Failed to initialize");
 347      clean_video_conference();
 348  LExit:
 349      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 350      return WcaFinalize(er);
 351  }
 352  
 353  UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
 354  {
 355      HRESULT hr = S_OK;
 356      UINT er = ERROR_SUCCESS;
 357      std::wstring installationFolder;
 358      bool failedToApply = false;
 359  
 360      hr = WcaInitialize(hInstall, "ApplyModulesRegistryChangeSets");
 361      ExitOnFailure(hr, "Failed to initialize");
 362      hr = getInstallFolder(hInstall, installationFolder);
 363      ExitOnFailure(hr, "Failed to get installFolder.");
 364  
 365      for (const auto &changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
 366      {
 367          if (!changeSet.apply())
 368          {
 369              Logger::error(L"Couldn't apply registry changeSet");
 370              failedToApply = true;
 371          }
 372      }
 373  
 374      if (!failedToApply)
 375      {
 376          Logger::info(L"All registry changeSets applied successfully");
 377      }
 378  LExit:
 379      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 380      return WcaFinalize(er);
 381  }
 382  
 383  UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
 384  {
 385      HRESULT hr = S_OK;
 386      UINT er = ERROR_SUCCESS;
 387      std::wstring installationFolder;
 388  
 389      hr = WcaInitialize(hInstall, "UndoModulesRegistryChangeSets"); // original func name is too long
 390      ExitOnFailure(hr, "Failed to initialize");
 391      hr = getInstallFolder(hInstall, installationFolder);
 392      ExitOnFailure(hr, "Failed to get installFolder.");
 393      for (const auto &changeSet : getAllModulesChangeSets(installationFolder))
 394      {
 395          changeSet.unApply();
 396      }
 397  
 398      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
 399  
 400      ExitOnFailure(hr, "Failed to extract msix");
 401  
 402  LExit:
 403      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 404      return WcaFinalize(er);
 405  }
 406  
 407  const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
 408  const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
 409  
 410  UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
 411  {
 412      HRESULT hr = S_OK;
 413      UINT er = ERROR_SUCCESS;
 414      std::wstring installationFolder;
 415  
 416      hr = WcaInitialize(hInstall, "InstallDSCModuleCA");
 417      ExitOnFailure(hr, "Failed to initialize");
 418  
 419      hr = getInstallFolder(hInstall, installationFolder);
 420      ExitOnFailure(hr, "Failed to get installFolder.");
 421  
 422      {
 423          const auto baseModulesPath = GetUserPowerShellModulesPath();
 424          if (baseModulesPath.empty())
 425          {
 426              hr = E_FAIL;
 427              ExitOnFailure(hr, "Unable to determine Powershell modules path");
 428          }
 429  
 430          const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / (get_product_version(false) + L".0");
 431  
 432          std::error_code errorCode;
 433          std::filesystem::create_directories(modulesPath, errorCode);
 434          if (errorCode)
 435          {
 436              hr = E_FAIL;
 437              ExitOnFailure(hr, "Unable to create Powershell modules folder");
 438          }
 439  
 440          for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
 441          {
 442              std::filesystem::copy_file(std::filesystem::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, std::filesystem::copy_options::overwrite_existing, errorCode);
 443  
 444              if (errorCode)
 445              {
 446                  hr = E_FAIL;
 447                  ExitOnFailure(hr, "Unable to copy Powershell modules file");
 448              }
 449          }
 450      }
 451  
 452  LExit:
 453      if (SUCCEEDED(hr))
 454      {
 455          er = ERROR_SUCCESS;
 456          Logger::info(L"DSC module was installed!");
 457      }
 458      else
 459      {
 460          er = ERROR_INSTALL_FAILURE;
 461          Logger::error(L"Couldn't install DSC module!");
 462      }
 463  
 464      return WcaFinalize(er);
 465  }
 466  
 467  UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
 468  {
 469      HRESULT hr = S_OK;
 470      UINT er = ERROR_SUCCESS;
 471  
 472      hr = WcaInitialize(hInstall, "UninstallDSCModuleCA");
 473      ExitOnFailure(hr, "Failed to initialize");
 474  
 475      {
 476          const auto baseModulesPath = GetUserPowerShellModulesPath();
 477          if (baseModulesPath.empty())
 478          {
 479              hr = E_FAIL;
 480              ExitOnFailure(hr, "Unable to determine Powershell modules path");
 481          }
 482  
 483          const auto powerToysModulePath = baseModulesPath / L"Microsoft.PowerToys.Configure";
 484          const auto versionedModulePath = powerToysModulePath / (get_product_version(false) + L".0");
 485  
 486          std::error_code errorCode;
 487  
 488          for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
 489          {
 490              std::filesystem::remove(versionedModulePath / filename, errorCode);
 491  
 492              if (errorCode)
 493              {
 494                  hr = E_FAIL;
 495                  ExitOnFailure(hr, "Unable to delete DSC file");
 496              }
 497          }
 498  
 499          for (const auto *modulePath : {&versionedModulePath, &powerToysModulePath})
 500          {
 501              std::filesystem::remove(*modulePath, errorCode);
 502  
 503              if (errorCode)
 504              {
 505                  hr = E_FAIL;
 506                  ExitOnFailure(hr, "Unable to delete DSC folder");
 507              }
 508          }
 509      }
 510  
 511  LExit:
 512      if (SUCCEEDED(hr))
 513      {
 514          er = ERROR_SUCCESS;
 515          Logger::info(L"DSC module was uninstalled!");
 516      }
 517      else
 518      {
 519          er = ERROR_INSTALL_FAILURE;
 520          Logger::error(L"Couldn't uninstall DSC module!");
 521      }
 522  
 523      return WcaFinalize(er);
 524  }
 525  
 526  UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
 527  {
 528      HRESULT hr = S_OK;
 529      UINT er = ERROR_SUCCESS;
 530      hr = WcaInitialize(hInstall, "InstallEmbeddedMSIXCA");
 531      ExitOnFailure(hr, "Failed to initialize");
 532  
 533      if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE))
 534      {
 535          Logger::info(L"Extracted MSIX");
 536          // TODO: Use to activate embedded MSIX
 537          const auto msix_path = std::filesystem::temp_directory_path() / "hello_package.msix";
 538          if (!msix->saveAsFile(msix_path))
 539          {
 540              ExitOnFailure(hr, "Failed to save msix");
 541          }
 542          Logger::info(L"Saved MSIX");
 543          using namespace winrt::Windows::Management::Deployment;
 544          using namespace winrt::Windows::Foundation;
 545  
 546          Uri msix_uri{msix_path.wstring()};
 547          PackageManager pm;
 548          auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
 549          if (!result)
 550          {
 551              ExitOnFailure(hr, "Failed to AddPackage");
 552          }
 553  
 554          Logger::info(L"MSIX[s] were installed!");
 555      }
 556      else
 557      {
 558          ExitOnFailure(hr, "Failed to extract msix");
 559      }
 560  
 561  LExit:
 562      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 563      return WcaFinalize(er);
 564  }
 565  
 566  UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
 567  {
 568      HRESULT hr = S_OK;
 569      UINT er = ERROR_SUCCESS;
 570      using namespace winrt::Windows::Management::Deployment;
 571      using namespace winrt::Windows::Foundation;
 572      // TODO: This must be replaced with the actual publisher and package name
 573      const wchar_t package_name[] = L"46b35c25-b593-48d5-aeb1-d3e9c3b796e9";
 574      const wchar_t publisher[] = L"CN=yuyoyuppe";
 575      PackageManager pm;
 576  
 577      hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
 578      ExitOnFailure(hr, "Failed to initialize");
 579  
 580      for (const auto &p : pm.FindPackagesForUser({}, package_name, publisher))
 581      {
 582          auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
 583          if (result)
 584          {
 585              Logger::info(L"MSIX was uninstalled!");
 586          }
 587          else
 588          {
 589              Logger::error(L"Couldn't uninstall MSIX!");
 590          }
 591      }
 592  
 593  LExit:
 594      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 595      return WcaFinalize(er);
 596  }
 597  
 598  UINT __stdcall InstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
 599  {
 600      HRESULT hr = S_OK;
 601      UINT er = ERROR_SUCCESS;
 602      LPWSTR customActionData = nullptr;
 603      std::wstring installFolderPath;
 604      std::wstring installScope;
 605      std::wstring msixPath;
 606      std::wstring data;
 607      size_t delimiterPos;
 608      bool isMachineLevel = false;
 609      
 610      hr = WcaInitialize(hInstall, "InstallPackageIdentityMSIXCA");
 611      ExitOnFailure(hr, "Failed to initialize");
 612  
 613      hr = WcaGetProperty(L"CustomActionData", &customActionData);
 614      ExitOnFailure(hr, "Failed to get CustomActionData property");
 615      
 616      // Parse CustomActionData: "[INSTALLFOLDER];[InstallScope]"
 617      data = customActionData;
 618      delimiterPos = data.find(L';');
 619      installFolderPath = data.substr(0, delimiterPos);
 620      installScope = data.substr(delimiterPos + 1);
 621      
 622      // Check if this is a machine-level installation
 623      if (installScope == L"perMachine")
 624      {
 625          isMachineLevel = true;
 626      }
 627      
 628      Logger::info(L"Installing PackageIdentity MSIX - perUser: {}", !isMachineLevel);
 629  
 630      // Construct path to PackageIdentity MSIX
 631      msixPath = installFolderPath;
 632      msixPath += L"PowerToysSparse.msix";
 633  
 634      if (std::filesystem::exists(msixPath))
 635      {
 636          using namespace winrt::Windows::Management::Deployment;
 637          using namespace winrt::Windows::Foundation;
 638  
 639          try
 640          {
 641              
 642              std::wstring externalLocation = installFolderPath; // External content location (PowerToys install folder)
 643              Uri externalUri{ externalLocation };               // External location URI for sparse package content
 644              Uri packageUri{ msixPath };                        // The MSIX file URI
 645              
 646              PackageManager packageManager;
 647              
 648              if (isMachineLevel)
 649              {
 650                  // Machine-level installation
 651                  
 652                  StagePackageOptions stageOptions;
 653                  stageOptions.ExternalLocationUri(externalUri);
 654                  
 655                  auto stageResult = packageManager.StagePackageByUriAsync(packageUri, stageOptions).get();
 656                  
 657                  uint32_t stageErrorCode = static_cast<uint32_t>(stageResult.ExtendedErrorCode());
 658                  if (stageErrorCode == 0)
 659                  {
 660                      std::wstring packageFamilyName = L"Microsoft.PowerToys.SparseApp_8wekyb3d8bbwe";
 661                      
 662                      try
 663                      {
 664                          auto provisionResult = packageManager.ProvisionPackageForAllUsersAsync(packageFamilyName).get();
 665                          uint32_t provisionErrorCode = static_cast<uint32_t>(provisionResult.ExtendedErrorCode());
 666                          
 667                          if (provisionErrorCode != 0)
 668                          {
 669                              Logger::error(L"Machine-level provisioning failed: 0x{:08X}", provisionErrorCode);
 670                          }
 671                      }
 672                      catch (const winrt::hresult_error& ex)
 673                      {
 674                          Logger::error(L"Provisioning exception: HRESULT 0x{:08X}", static_cast<uint32_t>(ex.code()));
 675                      }
 676                  }
 677                  else
 678                  {
 679                      Logger::error(L"Package staging failed: 0x{:08X}", stageErrorCode);
 680                  }
 681              }
 682              else
 683              {
 684                  AddPackageOptions addOptions;
 685                  addOptions.ExternalLocationUri(externalUri);
 686                  
 687                  auto addResult = packageManager.AddPackageByUriAsync(packageUri, addOptions).get();
 688                  
 689                  if (!addResult.IsRegistered())
 690                  {
 691                      uint32_t errorCode = static_cast<uint32_t>(addResult.ExtendedErrorCode());
 692                      Logger::error(L"Per-user installation failed: 0x{:08X}", errorCode);
 693                  }
 694              }
 695          }
 696          catch (const std::exception& ex)
 697          {
 698              Logger::error(L"PackageIdentity MSIX installation failed - Exception: {}", 
 699                           winrt::to_hstring(ex.what()).c_str());
 700          }
 701      }
 702      else
 703      {
 704          Logger::error(L"PackageIdentity MSIX not found: " + msixPath);
 705      }
 706  
 707  LExit:
 708      ReleaseStr(customActionData);
 709      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 710      return WcaFinalize(er);
 711  }
 712  
 713  UINT __stdcall UninstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
 714  {
 715      HRESULT hr = S_OK;
 716      UINT er = ERROR_SUCCESS;
 717      using namespace winrt::Windows::Management::Deployment; 
 718      using namespace winrt::Windows::Foundation;
 719      
 720      LPWSTR installScope = nullptr;
 721      bool isMachineLevel = false;
 722      
 723      PackageManager pm;
 724  
 725      hr = WcaInitialize(hInstall, "UninstallPackageIdentityMSIXCA");
 726      ExitOnFailure(hr, "Failed to initialize");
 727      
 728      // Check if this was a machine-level installation
 729      hr = WcaGetProperty(L"InstallScope", &installScope);
 730      if (SUCCEEDED(hr) && installScope && wcscmp(installScope, L"perMachine") == 0)
 731      {
 732          isMachineLevel = true;
 733      }
 734      
 735      Logger::info(L"Uninstalling PackageIdentity MSIX - perUser: {}", !isMachineLevel);
 736  
 737      try
 738      {
 739          std::wstring packageFamilyName = L"Microsoft.PowerToys.SparseApp_8wekyb3d8bbwe";
 740          
 741          if (isMachineLevel)
 742          {
 743              // Machine-level uninstallation: deprovision + remove for all users
 744              
 745              // First deprovision the package
 746              try
 747              {
 748                  auto deprovisionResult = pm.DeprovisionPackageForAllUsersAsync(packageFamilyName).get();
 749                  if (deprovisionResult.IsRegistered())
 750                  {
 751                      Logger::warn(L"Machine-level deprovisioning completed with warnings");
 752                  }
 753              }
 754              catch (const winrt::hresult_error& ex)
 755              {
 756                  Logger::warn(L"Machine-level deprovisioning failed: HRESULT 0x{:08X}", static_cast<uint32_t>(ex.code()));
 757              }
 758              
 759              // Then remove packages for all users
 760              auto packages = pm.FindPackagesForUserWithPackageTypes({}, packageFamilyName, PackageTypes::Main);
 761              for (const auto& package : packages)
 762              {
 763                  try
 764                  {
 765                      auto machineResult = pm.RemovePackageAsync(package.Id().FullName(), RemovalOptions::RemoveForAllUsers).get();
 766                      if (machineResult.IsRegistered())
 767                      {
 768                          uint32_t errorCode = static_cast<uint32_t>(machineResult.ExtendedErrorCode());
 769                          Logger::error(L"Machine-level removal failed: 0x{:08X} - {}", errorCode, machineResult.ErrorText());
 770                      }
 771                  }
 772                  catch (const winrt::hresult_error& ex)
 773                  {
 774                      Logger::error(L"Machine-level removal exception: HRESULT 0x{:08X}", static_cast<uint32_t>(ex.code()));
 775                  }
 776              }
 777          }
 778          else
 779          {
 780              // Per-user uninstallation: standard removal
 781              
 782              auto packages = pm.FindPackagesForUserWithPackageTypes({}, packageFamilyName, PackageTypes::Main);
 783              for (const auto& package : packages)
 784              {
 785                  auto userResult = pm.RemovePackageAsync(package.Id().FullName()).get();
 786                  if (userResult.IsRegistered())
 787                  {
 788                      uint32_t errorCode = static_cast<uint32_t>(userResult.ExtendedErrorCode());
 789                      Logger::error(L"Per-user removal failed: 0x{:08X} - {}", errorCode, userResult.ErrorText());
 790                  }
 791              }
 792          }
 793      }
 794      catch (const std::exception& ex)
 795      {
 796          std::string errorMsg = "Failed to uninstall PackageIdentity MSIX: " + std::string(ex.what());
 797          Logger::error(errorMsg);
 798          // Don't fail the entire uninstallation if PackageIdentity fails
 799          Logger::warn(L"Continuing uninstallation despite PackageIdentity MSIX error");
 800      }
 801  
 802  LExit:
 803      ReleaseStr(installScope);
 804      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 805      return WcaFinalize(er);
 806  }
 807  
 808  UINT __stdcall RemoveWindowsServiceByName(std::wstring serviceName)
 809  {
 810      SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
 811  
 812      if (!hSCManager)
 813      {
 814          return ERROR_INSTALL_FAILURE;
 815      }
 816  
 817      SC_HANDLE hService = OpenService(hSCManager, serviceName.c_str(), SERVICE_STOP | DELETE);
 818      if (!hService)
 819      {
 820          CloseServiceHandle(hSCManager);
 821          return ERROR_INSTALL_FAILURE;
 822      }
 823  
 824      SERVICE_STATUS ss;
 825      if (ControlService(hService, SERVICE_CONTROL_STOP, &ss))
 826      {
 827          Sleep(1000);
 828          while (QueryServiceStatus(hService, &ss))
 829          {
 830              if (ss.dwCurrentState == SERVICE_STOP_PENDING)
 831              {
 832                  Sleep(1000);
 833              }
 834              else
 835              {
 836                  break;
 837              }
 838          }
 839      }
 840  
 841      BOOL deleteResult = DeleteService(hService);
 842      CloseServiceHandle(hService);
 843      CloseServiceHandle(hSCManager);
 844  
 845      if (!deleteResult)
 846      {
 847          return ERROR_INSTALL_FAILURE;
 848      }
 849  
 850      return ERROR_SUCCESS;
 851  }
 852  
 853  UINT __stdcall UnsetAdvancedPasteAPIKeyCA(MSIHANDLE hInstall)
 854  {
 855      HRESULT hr = S_OK;
 856      UINT er = ERROR_SUCCESS;
 857  
 858      try
 859      {
 860          hr = WcaInitialize(hInstall, "UnsetAdvancedPasteAPIKey");
 861          ExitOnFailure(hr, "Failed to initialize");
 862  
 863          winrt::Windows::Security::Credentials::PasswordVault vault;
 864  
 865          auto hasPrefix = [](std::wstring_view value, wchar_t const* prefix) {
 866              std::wstring_view prefixView{ prefix };
 867              return value.compare(0, prefixView.size(), prefixView) == 0;
 868          };
 869  
 870          const wchar_t* resourcePrefixes[] = {
 871              L"https://platform.openai.com/api-keys",
 872              L"https://azure.microsoft.com/products/ai-services/openai-service",
 873              L"https://azure.microsoft.com/products/ai-services/ai-inference",
 874              L"https://console.mistral.ai/account/api-keys",
 875              L"https://ai.google.dev/",
 876          };
 877  
 878          const wchar_t* usernamePrefixes[] = {
 879              L"PowerToys_AdvancedPaste_",
 880          };
 881  
 882          auto credentials = vault.RetrieveAll();
 883          for (auto const& credential : credentials)
 884          {
 885              bool shouldRemove = false;
 886  
 887              std::wstring resource{ credential.Resource() };
 888              for (auto const prefix : resourcePrefixes)
 889              {
 890                  if (hasPrefix(resource, prefix))
 891                  {
 892                      shouldRemove = true;
 893                      break;
 894                  }
 895              }
 896  
 897              if (!shouldRemove)
 898              {
 899                  std::wstring username{ credential.UserName() };
 900                  for (auto const prefix : usernamePrefixes)
 901                  {
 902                      if (hasPrefix(username, prefix))
 903                      {
 904                          shouldRemove = true;
 905                          break;
 906                      }
 907                  }
 908              }
 909  
 910              if (!shouldRemove)
 911              {
 912                  continue;
 913              }
 914  
 915              try
 916              {
 917                  vault.Remove(credential);
 918              }
 919              catch (...)
 920              {
 921              }
 922          }
 923      }
 924      catch (...)
 925      {
 926      }
 927  
 928  LExit:
 929      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 930      return WcaFinalize(er);
 931  }
 932  
 933  UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
 934  {
 935      HRESULT hr = S_OK;
 936      UINT er = ERROR_SUCCESS;
 937      std::wstring installationFolder;
 938      std::string command;
 939  
 940      hr = WcaInitialize(hInstall, "UninstallCommandNotFoundModule");
 941      ExitOnFailure(hr, "Failed to initialize");
 942  
 943      hr = getInstallFolder(hInstall, installationFolder);
 944      ExitOnFailure(hr, "Failed to get installFolder.");
 945  
 946  #ifdef _M_ARM64
 947      command = "powershell.exe";
 948      command += " ";
 949      command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted";
 950      command += " -Command ";
 951      command += "\"[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User'), 'Process');";
 952      command += "pwsh.exe -NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File '" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "'\"";
 953  #else
 954      command = "pwsh.exe";
 955      command += " ";
 956      command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
 957  #endif
 958  
 959      system(command.c_str());
 960  
 961  LExit:
 962      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 963      return WcaFinalize(er);
 964  }
 965  
 966  UINT __stdcall UpgradeCommandNotFoundModuleCA(MSIHANDLE hInstall)
 967  {
 968      HRESULT hr = S_OK;
 969      UINT er = ERROR_SUCCESS;
 970      std::wstring installationFolder;
 971      std::string command;
 972  
 973      hr = WcaInitialize(hInstall, "UpgradeCommandNotFoundModule");
 974      ExitOnFailure(hr, "Failed to initialize");
 975  
 976      hr = getInstallFolder(hInstall, installationFolder);
 977      ExitOnFailure(hr, "Failed to get installFolder.");
 978  
 979      command = "pwsh.exe";
 980      command += " ";
 981      command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\UpgradeModule.ps1" + "\"";
 982  
 983      system(command.c_str());
 984  
 985  LExit:
 986      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
 987      return WcaFinalize(er);
 988  }
 989  
 990  UINT __stdcall UninstallServicesCA(MSIHANDLE hInstall)
 991  {
 992      HRESULT hr = S_OK;
 993      UINT er = ERROR_SUCCESS;
 994      hr = WcaInitialize(hInstall, "UninstallServicesCA");
 995  
 996      ExitOnFailure(hr, "Failed to initialize");
 997  
 998      hr = RemoveWindowsServiceByName(L"PowerToys.MWB.Service");
 999  
1000  LExit:
1001      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1002      return WcaFinalize(er);
1003  }
1004  
1005  // Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards.
1006  // Based on the Task Scheduler Displaying Task Names and State example:
1007  // https://learn.microsoft.com/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
1008  UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
1009  {
1010      HRESULT hr = S_OK;
1011      UINT er = ERROR_SUCCESS;
1012  
1013      ITaskService *pService = nullptr;
1014      ITaskFolder *pTaskFolder = nullptr;
1015      IRegisteredTaskCollection *pTaskCollection = nullptr;
1016      ITaskFolder *pRootFolder = nullptr;
1017      LONG numTasks = 0;
1018  
1019      hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
1020      ExitOnFailure(hr, "Failed to initialize");
1021  
1022      Logger::info(L"RemoveScheduledTasksCA Initialized.");
1023  
1024      // COM and Security Initialization is expected to have been done by the MSI.
1025      // It couldn't be done in the DLL, anyway.
1026      // ------------------------------------------------------
1027      // Create an instance of the Task Service.
1028      hr = CoCreateInstance(CLSID_TaskScheduler,
1029                            nullptr,
1030                            CLSCTX_INPROC_SERVER,
1031                            IID_ITaskService,
1032                            reinterpret_cast<void **>(&pService));
1033      ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
1034  
1035      // Connect to the task service.
1036      hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
1037      ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
1038  
1039      // ------------------------------------------------------
1040      // Get the PowerToys task folder.
1041      hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
1042      if (FAILED(hr))
1043      {
1044          // Folder doesn't exist. No need to delete anything.
1045          Logger::info(L"The PowerToys scheduled task folder wasn't found. Nothing to delete.");
1046          hr = S_OK;
1047          ExitFunction();
1048      }
1049  
1050      // -------------------------------------------------------
1051      // Get the registered tasks in the folder.
1052      hr = pTaskFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection);
1053      ExitOnFailure(hr, "Cannot get the registered tasks: %x", hr);
1054  
1055      hr = pTaskCollection->get_Count(&numTasks);
1056      for (LONG i = 0; i < numTasks; i++)
1057      {
1058          // Delete all the tasks found.
1059          // If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
1060          IRegisteredTask *pRegisteredTask = nullptr;
1061          hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
1062          if (SUCCEEDED(hr))
1063          {
1064              BSTR taskName = nullptr;
1065              hr = pRegisteredTask->get_Name(&taskName);
1066              if (SUCCEEDED(hr))
1067              {
1068                  hr = pTaskFolder->DeleteTask(taskName, 0);
1069                  if (FAILED(hr))
1070                  {
1071                      Logger::error(L"Cannot delete the {} task: {}", taskName, hr);
1072                  }
1073                  SysFreeString(taskName);
1074              }
1075              else
1076              {
1077                  Logger::error(L"Cannot get the registered task name: {}", hr);
1078              }
1079              pRegisteredTask->Release();
1080          }
1081          else
1082          {
1083              Logger::error(L"Cannot get the registered task item at index={}: {}", i + 1, hr);
1084          }
1085      }
1086  
1087      // ------------------------------------------------------
1088      // Get the pointer to the root task folder and delete the PowerToys subfolder.
1089      hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
1090      ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
1091      hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), 0);
1092      pRootFolder->Release();
1093      ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
1094  
1095      Logger::info(L"Deleted the PowerToys Task Scheduler folder.");
1096  
1097  LExit:
1098      if (pService)
1099      {
1100          pService->Release();
1101      }
1102      if (pTaskFolder)
1103      {
1104          pTaskFolder->Release();
1105      }
1106      if (pTaskCollection)
1107      {
1108          pTaskCollection->Release();
1109      }
1110  
1111      if (!SUCCEEDED(hr))
1112      {
1113          PMSIHANDLE hRecord = MsiCreateRecord(0);
1114          MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
1115          MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
1116      }
1117  
1118      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1119      return WcaFinalize(er);
1120  }
1121  
1122  UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
1123  {
1124      HRESULT hr = S_OK;
1125      UINT er = ERROR_SUCCESS;
1126  
1127      hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA");
1128      ExitOnFailure(hr, "Failed to initialize");
1129  
1130      TraceLoggingWriteWrapper(
1131          g_hProvider,
1132          "Install_Success",
1133          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1134          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1135          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1136          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1137  
1138  LExit:
1139      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1140      return WcaFinalize(er);
1141  }
1142  
1143  UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall)
1144  {
1145      HRESULT hr = S_OK;
1146      UINT er = ERROR_SUCCESS;
1147  
1148      hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA");
1149      ExitOnFailure(hr, "Failed to initialize");
1150  
1151      TraceLoggingWriteWrapper(
1152          g_hProvider,
1153          "Install_Cancel",
1154          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1155          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1156          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1157          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1158  
1159  LExit:
1160      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1161      return WcaFinalize(er);
1162  }
1163  
1164  UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall)
1165  {
1166      HRESULT hr = S_OK;
1167      UINT er = ERROR_SUCCESS;
1168  
1169      hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA");
1170      ExitOnFailure(hr, "Failed to initialize");
1171  
1172      TraceLoggingWriteWrapper(
1173          g_hProvider,
1174          "Install_Fail",
1175          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1176          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1177          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1178          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1179  
1180  LExit:
1181      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1182      return WcaFinalize(er);
1183  }
1184  
1185  UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall)
1186  {
1187      HRESULT hr = S_OK;
1188      UINT er = ERROR_SUCCESS;
1189  
1190      hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA");
1191      ExitOnFailure(hr, "Failed to initialize");
1192  
1193      TraceLoggingWriteWrapper(
1194          g_hProvider,
1195          "UnInstall_Success",
1196          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1197          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1198          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1199          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1200  
1201  LExit:
1202      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1203      return WcaFinalize(er);
1204  }
1205  
1206  UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall)
1207  {
1208      HRESULT hr = S_OK;
1209      UINT er = ERROR_SUCCESS;
1210  
1211      hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA");
1212      ExitOnFailure(hr, "Failed to initialize");
1213  
1214      TraceLoggingWriteWrapper(
1215          g_hProvider,
1216          "UnInstall_Cancel",
1217          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1218          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1219          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1220          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1221  
1222  LExit:
1223      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1224      return WcaFinalize(er);
1225  }
1226  
1227  UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall)
1228  {
1229      HRESULT hr = S_OK;
1230      UINT er = ERROR_SUCCESS;
1231  
1232      hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA");
1233      ExitOnFailure(hr, "Failed to initialize");
1234  
1235      TraceLoggingWriteWrapper(
1236          g_hProvider,
1237          "UnInstall_Fail",
1238          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1239          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1240          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1241          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1242  
1243  LExit:
1244      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1245      return WcaFinalize(er);
1246  }
1247  
1248  UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall)
1249  {
1250      HRESULT hr = S_OK;
1251      UINT er = ERROR_SUCCESS;
1252  
1253      hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA");
1254      ExitOnFailure(hr, "Failed to initialize");
1255  
1256      TraceLoggingWriteWrapper(
1257          g_hProvider,
1258          "Repair_Cancel",
1259          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1260          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1261          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1262          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1263  
1264  LExit:
1265      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1266      return WcaFinalize(er);
1267  }
1268  
1269  UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall)
1270  {
1271      HRESULT hr = S_OK;
1272      UINT er = ERROR_SUCCESS;
1273  
1274      hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA");
1275      ExitOnFailure(hr, "Failed to initialize");
1276  
1277      TraceLoggingWriteWrapper(
1278          g_hProvider,
1279          "Repair_Fail",
1280          TraceLoggingWideString(get_product_version().c_str(), "Version"),
1281          ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
1282          TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
1283          TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
1284  
1285  LExit:
1286      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1287      return WcaFinalize(er);
1288  }
1289  
1290  UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
1291  {
1292      HRESULT hr = S_OK;
1293      UINT er = ERROR_SUCCESS;
1294      hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA");
1295      MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", L"");
1296  
1297      LPWSTR currentScope = nullptr;
1298      hr = WcaGetProperty(L"InstallScope", &currentScope);
1299  
1300      try
1301      {
1302          if (auto install_path = GetMsiPackageInstalledPath(std::wstring{currentScope} == L"perUser"))
1303          {
1304              MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
1305          }
1306      }
1307      catch (...)
1308      {
1309      }
1310      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1311      return WcaFinalize(er);
1312  }
1313  
1314  UINT __stdcall InstallCmdPalPackageCA(MSIHANDLE hInstall)
1315  {
1316      using namespace winrt::Windows::Foundation;
1317      using namespace winrt::Windows::Management::Deployment;
1318  
1319      HRESULT hr = S_OK;
1320      UINT er = ERROR_SUCCESS;
1321      std::wstring installationFolder;
1322  
1323      hr = WcaInitialize(hInstall, "InstallCmdPalPackage");
1324      hr = getInstallFolder(hInstall, installationFolder);
1325  
1326      try
1327      {
1328          auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
1329          auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
1330  
1331          if (!msix.empty())
1332          {
1333              auto msixPath = msix[0];
1334  
1335              if (!package::RegisterPackage(msixPath, dependencies))
1336              {
1337                  Logger::error(L"Failed to install CmdPal package");
1338                  er = ERROR_INSTALL_FAILURE;
1339              }
1340          }
1341      }
1342      catch (std::exception &e)
1343      {
1344          std::string errorMessage{"Exception thrown while trying to install CmdPal package: "};
1345          errorMessage += e.what();
1346          Logger::error(errorMessage);
1347  
1348          er = ERROR_INSTALL_FAILURE;
1349      }
1350  
1351      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1352      return WcaFinalize(er);
1353  }
1354  
1355  UINT __stdcall UnRegisterCmdPalPackageCA(MSIHANDLE hInstall)
1356  {
1357      using namespace winrt::Windows::Foundation;
1358      using namespace winrt::Windows::Management::Deployment;
1359  
1360      HRESULT hr = S_OK;
1361      UINT er = ERROR_SUCCESS;
1362  
1363      hr = WcaInitialize(hInstall, "UnRegisterCmdPalPackageCA");
1364  
1365      try
1366      {
1367          // Packages to unregister
1368          std::wstring packageToRemoveDisplayName {L"Microsoft.CommandPalette"};
1369  
1370          if (!package::UnRegisterPackage(packageToRemoveDisplayName))
1371          {
1372              Logger::error(L"Failed to unregister package: " + packageToRemoveDisplayName);
1373              er = ERROR_INSTALL_FAILURE;
1374          }
1375      }
1376      catch (std::exception &e)
1377      {
1378          std::string errorMessage{"Exception thrown while trying to unregister the CmdPal package: "};
1379          errorMessage += e.what();
1380          Logger::error(errorMessage);
1381  
1382          er = ERROR_INSTALL_FAILURE;
1383      }
1384  
1385      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1386      return WcaFinalize(er);
1387  }
1388  
1389  
1390  UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
1391  {
1392      using namespace winrt::Windows::Foundation;
1393      using namespace winrt::Windows::Management::Deployment;
1394  
1395      HRESULT hr = S_OK;
1396      UINT er = ERROR_SUCCESS;
1397  
1398      hr = WcaInitialize(hInstall, "UnRegisterContextMenuPackagesCA"); // original func name is too long
1399  
1400      try
1401      {
1402          // Packages to unregister
1403          const std::vector<std::wstring> packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}};
1404  
1405          for (auto const &package : packagesToRemoveDisplayName)
1406          {
1407              if (!package::UnRegisterPackage(package))
1408              {
1409                  Logger::error(L"Failed to unregister package: " + package);
1410                  er = ERROR_INSTALL_FAILURE;
1411              }
1412          }
1413      }
1414      catch (std::exception &e)
1415      {
1416          std::string errorMessage{"Exception thrown while trying to unregister sparse packages: "};
1417          errorMessage += e.what();
1418          Logger::error(errorMessage);
1419  
1420          er = ERROR_INSTALL_FAILURE;
1421      }
1422  
1423      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1424      return WcaFinalize(er);
1425  }
1426  
1427  
1428  UINT __stdcall CleanImageResizerRuntimeRegistryCA(MSIHANDLE hInstall)
1429  {
1430      HRESULT hr = S_OK;
1431      UINT er = ERROR_SUCCESS;
1432      hr = WcaInitialize(hInstall, "CleanImageResizerRuntimeRegistryCA");
1433  
1434      try
1435      {
1436          const wchar_t* CLSID_STR = L"{51B4D7E5-7568-4234-B4BB-47FB3C016A69}";
1437          const wchar_t* exts[] = { L".bmp", L".dib", L".gif", L".jfif", L".jpe", L".jpeg", L".jpg", L".jxr", L".png", L".rle", L".tif", L".tiff", L".wdp" };
1438  
1439          auto deleteKeyRecursive = [](HKEY root, const std::wstring &path) {
1440              RegDeleteTreeW(root, path.c_str());
1441          };
1442  
1443          // InprocServer32 chain root CLSID
1444          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
1445          // DragDrop handler
1446          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\ShellEx\\DragDropHandlers\\ImageResizer");
1447          // Extensions
1448          for (auto ext : exts)
1449          {
1450              deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\SystemFileAssociations\\" + std::wstring(ext) + L"\\ShellEx\\ContextMenuHandlers\\ImageResizer");
1451          }
1452          // Sentinel
1453          RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\ImageResizer");
1454      }
1455      catch (...)
1456      {
1457          er = ERROR_INSTALL_FAILURE;
1458      }
1459  
1460      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1461      return WcaFinalize(er);
1462  }
1463  
1464  UINT __stdcall CleanFileLocksmithRuntimeRegistryCA(MSIHANDLE hInstall)
1465  {
1466      HRESULT hr = S_OK;
1467      UINT er = ERROR_SUCCESS;
1468      hr = WcaInitialize(hInstall, "CleanFileLocksmithRuntimeRegistryCA");
1469      try
1470      {
1471          const wchar_t* CLSID_STR = L"{84D68575-E186-46AD-B0CB-BAEB45EE29C0}";
1472          auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
1473              RegDeleteTreeW(root, path.c_str());
1474          };
1475          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
1476          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
1477          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Drive\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
1478          RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\FileLocksmith");
1479      }
1480      catch (...)
1481      {
1482          er = ERROR_INSTALL_FAILURE;
1483      }
1484      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1485      return WcaFinalize(er);
1486  }
1487  
1488  UINT __stdcall CleanPowerRenameRuntimeRegistryCA(MSIHANDLE hInstall)
1489  {
1490      HRESULT hr = S_OK;
1491      UINT er = ERROR_SUCCESS;
1492      hr = WcaInitialize(hInstall, "CleanPowerRenameRuntimeRegistryCA");
1493      try
1494      {
1495          const wchar_t* CLSID_STR = L"{0440049F-D1DC-4E46-B27B-98393D79486B}";
1496          auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
1497              RegDeleteTreeW(root, path.c_str());
1498          };
1499          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
1500          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
1501          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
1502          RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\PowerRename");
1503      }
1504      catch (...)
1505      {
1506          er = ERROR_INSTALL_FAILURE;
1507      }
1508      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1509      return WcaFinalize(er);
1510  }
1511  
1512  UINT __stdcall CleanNewPlusRuntimeRegistryCA(MSIHANDLE hInstall)
1513  {
1514      HRESULT hr = S_OK;
1515      UINT er = ERROR_SUCCESS;
1516      hr = WcaInitialize(hInstall, "CleanNewPlusRuntimeRegistryCA");
1517      try
1518      {
1519          const wchar_t* CLSID_STR = L"{FF90D477-E32A-4BE8-8CC5-A502A97F5401}";
1520          auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
1521              RegDeleteTreeW(root, path.c_str());
1522          };
1523          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
1524          deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\NewPlusShellExtensionWin10");
1525          RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\NewPlus");
1526      }
1527      catch (...)
1528      {
1529          er = ERROR_INSTALL_FAILURE;
1530      }
1531      er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
1532      return WcaFinalize(er);
1533  }
1534  
1535  UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
1536  {
1537      HRESULT hr = S_OK;
1538      UINT er = ERROR_SUCCESS;
1539      hr = WcaInitialize(hInstall, "TerminateProcessesCA");
1540  
1541      std::vector<DWORD> processes;
1542      const size_t maxProcesses = 4096;
1543      DWORD bytes = maxProcesses * sizeof(processes[0]);
1544      processes.resize(maxProcesses);
1545  
1546      if (!EnumProcesses(processes.data(), bytes, &bytes))
1547      {
1548          return 1;
1549      }
1550      processes.resize(bytes / sizeof(processes[0]));
1551  
1552      std::array<std::wstring_view, 45> processesToTerminate = {
1553          L"PowerToys.PowerLauncher.exe",
1554          L"PowerToys.Settings.exe",
1555          L"PowerToys.AdvancedPaste.exe",
1556          L"PowerToys.Awake.exe",
1557          L"PowerToys.FancyZones.exe",
1558          L"PowerToys.FancyZonesEditor.exe",
1559          L"PowerToys.FileLocksmithUI.exe",
1560          L"PowerToys.MouseJumpUI.exe",
1561          L"PowerToys.ColorPickerUI.exe",
1562          L"PowerToys.AlwaysOnTop.exe",
1563          L"PowerToys.RegistryPreview.exe",
1564          L"PowerToys.Hosts.exe",
1565          L"PowerToys.PowerRename.exe",
1566          L"PowerToys.ImageResizer.exe",
1567          L"PowerToys.LightSwitchService.exe",
1568          L"PowerToys.PowerDisplay.exe",
1569          L"PowerToys.GcodeThumbnailProvider.exe",
1570          L"PowerToys.BgcodeThumbnailProvider.exe",
1571          L"PowerToys.PdfThumbnailProvider.exe",
1572          L"PowerToys.MonacoPreviewHandler.exe",
1573          L"PowerToys.MarkdownPreviewHandler.exe",
1574          L"PowerToys.StlThumbnailProvider.exe",
1575          L"PowerToys.SvgThumbnailProvider.exe",
1576          L"PowerToys.GcodePreviewHandler.exe",
1577          L"PowerToys.BgcodePreviewHandler.exe",
1578          L"PowerToys.QoiPreviewHandler.exe",
1579          L"PowerToys.PdfPreviewHandler.exe",
1580          L"PowerToys.QoiThumbnailProvider.exe",
1581          L"PowerToys.SvgPreviewHandler.exe",
1582          L"PowerToys.Peek.UI.exe",
1583          L"PowerToys.MouseWithoutBorders.exe",
1584          L"PowerToys.MouseWithoutBordersHelper.exe",
1585          L"PowerToys.MouseWithoutBordersService.exe",
1586          L"PowerToys.CropAndLock.exe",
1587          L"PowerToys.EnvironmentVariables.exe",
1588          L"PowerToys.QuickAccess.exe",
1589          L"PowerToys.WorkspacesSnapshotTool.exe",
1590          L"PowerToys.WorkspacesLauncher.exe",
1591          L"PowerToys.WorkspacesLauncherUI.exe",
1592          L"PowerToys.WorkspacesEditor.exe",
1593          L"PowerToys.WorkspacesWindowArranger.exe",
1594          L"Microsoft.CmdPal.UI.exe",
1595          L"Microsoft.CmdPal.Ext.PowerToys.exe",
1596          L"PowerToys.ZoomIt.exe",
1597          L"PowerToys.exe",
1598      };
1599  
1600      for (const auto procID : processes)
1601      {
1602          if (!procID)
1603          {
1604              continue;
1605          }
1606          wchar_t processName[MAX_PATH] = L"<unknown>";
1607  
1608          HANDLE hProcess{OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID)};
1609          if (!hProcess)
1610          {
1611              continue;
1612          }
1613          HMODULE hMod;
1614          DWORD cbNeeded;
1615  
1616          if (!EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
1617          {
1618              CloseHandle(hProcess);
1619              continue;
1620          }
1621          GetModuleBaseNameW(hProcess, hMod, processName, sizeof(processName) / sizeof(wchar_t));
1622  
1623          for (const auto processToTerminate : processesToTerminate)
1624          {
1625              if (processName == processToTerminate)
1626              {
1627                  const DWORD timeout = 500;
1628                  auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL
1629                  {
1630                      auto targetProcID = *reinterpret_cast<const DWORD *>(procIDPtr);
1631                      DWORD windowProcID = 0;
1632                      GetWindowThreadProcessId(hwnd, &windowProcID);
1633                      if (windowProcID == targetProcID)
1634                      {
1635                          DWORD_PTR _{};
1636                          SendMessageTimeoutA(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK, timeout, &_);
1637                      }
1638                      return TRUE;
1639                  };
1640                  EnumWindows(windowEnumerator, reinterpret_cast<LPARAM>(&procID));
1641                  Sleep(timeout);
1642                  TerminateProcess(hProcess, 0);
1643                  break;
1644              }
1645          }
1646          CloseHandle(hProcess);
1647      }
1648  
1649      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1650      return WcaFinalize(er);
1651  }
1652  
1653  UINT __stdcall SetBundleInstallLocationCA(MSIHANDLE hInstall)
1654  {
1655      HRESULT hr = S_OK;
1656      UINT er = ERROR_SUCCESS;
1657      
1658      // Declare all variables at the beginning to avoid goto issues
1659      std::wstring customActionData;
1660      std::wstring installationFolder;
1661      std::wstring bundleUpgradeCode;
1662      std::wstring installScope;
1663      bool isPerUser = false;
1664      size_t pos1 = std::wstring::npos;
1665      size_t pos2 = std::wstring::npos;
1666      std::vector<HKEY> keysToTry;
1667  
1668      hr = WcaInitialize(hInstall, "SetBundleInstallLocationCA");
1669      ExitOnFailure(hr, "Failed to initialize");
1670      
1671      // Parse CustomActionData: "installFolder;upgradeCode;installScope"
1672      hr = getInstallFolder(hInstall, customActionData);
1673      ExitOnFailure(hr, "Failed to get CustomActionData.");
1674      
1675      pos1 = customActionData.find(L';');
1676      if (pos1 == std::wstring::npos) 
1677      {
1678          hr = E_INVALIDARG;
1679          ExitOnFailure(hr, "Invalid CustomActionData format - missing first semicolon");
1680      }
1681      
1682      pos2 = customActionData.find(L';', pos1 + 1);
1683      if (pos2 == std::wstring::npos) 
1684      {
1685          hr = E_INVALIDARG;
1686          ExitOnFailure(hr, "Invalid CustomActionData format - missing second semicolon");
1687      }
1688      
1689      installationFolder = customActionData.substr(0, pos1);
1690      bundleUpgradeCode = customActionData.substr(pos1 + 1, pos2 - pos1 - 1);
1691      installScope = customActionData.substr(pos2 + 1);
1692      
1693      isPerUser = (installScope == L"perUser");
1694      
1695      // Use the appropriate registry based on install scope
1696      HKEY targetKey = isPerUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
1697      const wchar_t* keyName = isPerUser ? L"HKCU" : L"HKLM";
1698      
1699      WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Searching for Bundle in %ls registry", keyName);
1700      
1701      HKEY uninstallKey;
1702      LONG openResult = RegOpenKeyExW(targetKey, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &uninstallKey);
1703      if (openResult != ERROR_SUCCESS)
1704      {
1705          WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Failed to open uninstall key, error: %ld", openResult);
1706          goto LExit;
1707      }
1708      
1709      DWORD index = 0;
1710      wchar_t subKeyName[256];
1711      DWORD subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t);
1712      
1713      while (RegEnumKeyExW(uninstallKey, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
1714      {
1715          HKEY productKey;
1716          if (RegOpenKeyExW(uninstallKey, subKeyName, 0, KEY_READ | KEY_WRITE, &productKey) == ERROR_SUCCESS)
1717          {
1718              wchar_t upgradeCode[256];
1719              DWORD upgradeCodeSize = sizeof(upgradeCode);
1720              DWORD valueType;
1721              
1722              if (RegQueryValueExW(productKey, L"BundleUpgradeCode", nullptr, &valueType, 
1723                                 reinterpret_cast<LPBYTE>(upgradeCode), &upgradeCodeSize) == ERROR_SUCCESS)
1724              {
1725                  // Remove brackets from registry upgradeCode for comparison (bundleUpgradeCode doesn't have brackets)
1726                  std::wstring regUpgradeCode = upgradeCode;
1727                  if (!regUpgradeCode.empty() && regUpgradeCode.front() == L'{' && regUpgradeCode.back() == L'}')
1728                  {
1729                      regUpgradeCode = regUpgradeCode.substr(1, regUpgradeCode.length() - 2);
1730                  }
1731                  
1732                  if (_wcsicmp(regUpgradeCode.c_str(), bundleUpgradeCode.c_str()) == 0)
1733                  {
1734                      // Found matching Bundle, set InstallLocation
1735                      LONG setResult = RegSetValueExW(productKey, L"InstallLocation", 0, REG_SZ,
1736                                   reinterpret_cast<const BYTE*>(installationFolder.c_str()),
1737                                   static_cast<DWORD>((installationFolder.length() + 1) * sizeof(wchar_t)));
1738                      
1739                      if (setResult == ERROR_SUCCESS)
1740                      {
1741                          WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: InstallLocation set successfully");
1742                      }
1743                      else
1744                      {
1745                          WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Failed to set InstallLocation, error: %ld", setResult);
1746                      }
1747                      
1748                      RegCloseKey(productKey);
1749                      RegCloseKey(uninstallKey);
1750                      goto LExit;
1751                  }
1752              }
1753              RegCloseKey(productKey);
1754          }
1755          
1756          index++;
1757          subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t);
1758      }
1759      
1760      RegCloseKey(uninstallKey);
1761      
1762  LExit:
1763      er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
1764      return WcaFinalize(er);
1765  }
1766  
1767  void initSystemLogger()
1768  {
1769      static std::once_flag initLoggerFlag;
1770      std::call_once(initLoggerFlag, []()
1771                     {
1772              WCHAR temp_path[MAX_PATH];
1773              auto ret = GetTempPath(MAX_PATH, temp_path);
1774  
1775              if (ret)
1776              {
1777                  Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
1778              } });
1779  }
1780  
1781  // DllMain - Initialize and cleanup WiX custom action utils.
1782  extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
1783  {
1784      switch (ulReason)
1785      {
1786      case DLL_PROCESS_ATTACH:
1787          WcaGlobalInitialize(hInst);
1788          initSystemLogger();
1789          TraceLoggingRegister(g_hProvider);
1790          DLL_HANDLE = hInst;
1791          break;
1792  
1793      case DLL_PROCESS_DETACH:
1794          TraceLoggingUnregister(g_hProvider);
1795          WcaGlobalFinalize();
1796          break;
1797      }
1798  
1799      return TRUE;
1800  }