diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp index 6f3185a6c0..815731c161 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp @@ -1384,120 +1384,112 @@ UINT __stdcall SetBundleInstallLocationCA(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; - std::wstring installationFolder; - // Declare variables at the beginning to avoid goto issues - std::wstring bundleName; - HKEY rootKey = HKEY_LOCAL_MACHINE; - LPWSTR installScopeBuffer = nullptr; - std::wstring uninstallPath; - HKEY uninstallKey = nullptr; - bool bundleFound = false; + // Declare all variables at the beginning to avoid goto issues + std::wstring customActionData; + std::wstring installationFolder; + std::wstring bundleUpgradeCode; + std::wstring installScope; + bool isPerUser = false; + size_t pos1 = std::wstring::npos; + size_t pos2 = std::wstring::npos; + std::vector keysToTry; hr = WcaInitialize(hInstall, "SetBundleInstallLocationCA"); ExitOnFailure(hr, "Failed to initialize"); - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Starting custom action"); + // Parse CustomActionData: "installFolder;upgradeCode;installScope" + hr = getInstallFolder(hInstall, customActionData); + ExitOnFailure(hr, "Failed to get CustomActionData."); - hr = getInstallFolder(hInstall, installationFolder); - ExitOnFailure(hr, "Failed to get installFolder."); - - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Installation folder: %ls", installationFolder.c_str()); - - // Get bundle name with platform - bundleName = L"PowerToys (Preview) "; -#ifdef _M_ARM64 - bundleName += L"ARM64"; -#else - bundleName += L"x64"; -#endif - - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Looking for Bundle: %ls", bundleName.c_str()); - - // Get InstallScope property to determine if per-user or per-machine - hr = WcaGetProperty(L"InstallScope", &installScopeBuffer); - if (SUCCEEDED(hr) && installScopeBuffer && wcscmp(installScopeBuffer, L"perUser") == 0) + pos1 = customActionData.find(L';'); + if (pos1 == std::wstring::npos) { - rootKey = HKEY_CURRENT_USER; - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Using HKEY_CURRENT_USER"); - } - else - { - rootKey = HKEY_LOCAL_MACHINE; - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Using HKEY_LOCAL_MACHINE"); + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid CustomActionData format - missing first semicolon"); } - // Search for Bundle registry entry - uninstallPath = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; - - LONG result = RegOpenKeyExW(rootKey, uninstallPath.c_str(), 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &uninstallKey); - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: RegOpenKeyExW result: %ld", result); - - if (result == ERROR_SUCCESS) + pos2 = customActionData.find(L';', pos1 + 1); + if (pos2 == std::wstring::npos) { - DWORD index = 0; - wchar_t subKeyName[256]; - DWORD subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t); - - while (RegEnumKeyExW(uninstallKey, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid CustomActionData format - missing second semicolon"); + } + + installationFolder = customActionData.substr(0, pos1); + bundleUpgradeCode = customActionData.substr(pos1 + 1, pos2 - pos1 - 1); + installScope = customActionData.substr(pos2 + 1); + + isPerUser = (installScope == L"perUser"); + + // Use the appropriate registry based on install scope + HKEY targetKey = isPerUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + const wchar_t* keyName = isPerUser ? L"HKCU" : L"HKLM"; + + WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Searching for Bundle in %ls registry", keyName); + + HKEY uninstallKey; + LONG openResult = RegOpenKeyExW(targetKey, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &uninstallKey); + if (openResult != ERROR_SUCCESS) + { + WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Failed to open uninstall key, error: %ld", openResult); + goto LExit; + } + + DWORD index = 0; + wchar_t subKeyName[256]; + DWORD subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t); + + while (RegEnumKeyExW(uninstallKey, index, subKeyName, &subKeyNameSize, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) + { + HKEY productKey; + if (RegOpenKeyExW(uninstallKey, subKeyName, 0, KEY_READ | KEY_WRITE, &productKey) == ERROR_SUCCESS) { - HKEY productKey; - if (RegOpenKeyExW(uninstallKey, subKeyName, 0, KEY_READ | KEY_WRITE, &productKey) == ERROR_SUCCESS) - { - wchar_t displayName[512]; - DWORD displayNameSize = sizeof(displayName); - DWORD valueType; - - if (RegQueryValueExW(productKey, L"DisplayName", nullptr, &valueType, - reinterpret_cast(displayName), &displayNameSize) == ERROR_SUCCESS) - { - WcaLog(LOGMSG_VERBOSE, "SetBundleInstallLocationCA: Found entry: %ls", displayName); - - // Check if this matches our Bundle name (case-insensitive and flexible) - if (wcsstr(displayName, L"PowerToys") && wcsstr(displayName, L"Preview")) - { - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Found PowerToys Bundle: %ls", displayName); - - // Found the Bundle entry, add InstallLocation - LONG setResult = RegSetValueExW(productKey, L"InstallLocation", 0, REG_SZ, - reinterpret_cast(installationFolder.c_str()), - static_cast((installationFolder.length() + 1) * sizeof(wchar_t))); - - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: RegSetValueExW result: %ld", setResult); - - if (setResult == ERROR_SUCCESS) - { - bundleFound = true; - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Successfully set InstallLocation to: %ls", installationFolder.c_str()); - } - else - { - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Failed to set InstallLocation, error: %ld", setResult); - } - - RegCloseKey(productKey); - break; - } - } - RegCloseKey(productKey); - } + wchar_t upgradeCode[256]; + DWORD upgradeCodeSize = sizeof(upgradeCode); + DWORD valueType; - index++; - subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t); + if (RegQueryValueExW(productKey, L"BundleUpgradeCode", nullptr, &valueType, + reinterpret_cast(upgradeCode), &upgradeCodeSize) == ERROR_SUCCESS) + { + // Remove brackets from registry upgradeCode for comparison (bundleUpgradeCode doesn't have brackets) + std::wstring regUpgradeCode = upgradeCode; + if (!regUpgradeCode.empty() && regUpgradeCode.front() == L'{' && regUpgradeCode.back() == L'}') + { + regUpgradeCode = regUpgradeCode.substr(1, regUpgradeCode.length() - 2); + } + + if (_wcsicmp(regUpgradeCode.c_str(), bundleUpgradeCode.c_str()) == 0) + { + // Found matching Bundle, set InstallLocation + LONG setResult = RegSetValueExW(productKey, L"InstallLocation", 0, REG_SZ, + reinterpret_cast(installationFolder.c_str()), + static_cast((installationFolder.length() + 1) * sizeof(wchar_t))); + + if (setResult == ERROR_SUCCESS) + { + WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: InstallLocation set successfully"); + } + else + { + WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Failed to set InstallLocation, error: %ld", setResult); + } + + RegCloseKey(productKey); + RegCloseKey(uninstallKey); + goto LExit; + } + } + RegCloseKey(productKey); } - RegCloseKey(uninstallKey); + + index++; + subKeyNameSize = sizeof(subKeyName) / sizeof(wchar_t); } - if (!bundleFound) - { - WcaLog(LOGMSG_STANDARD, "SetBundleInstallLocationCA: Bundle entry not found"); - } + RegCloseKey(uninstallKey); LExit: - if (installScopeBuffer) - { - ReleaseStr(installScopeBuffer); - } er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } diff --git a/installer/PowerToysSetupVNext/PowerToys.wxs b/installer/PowerToysSetupVNext/PowerToys.wxs index 19906089bf..64f6f35c5e 100644 --- a/installer/PowerToysSetupVNext/PowerToys.wxs +++ b/installer/PowerToysSetupVNext/PowerToys.wxs @@ -28,6 +28,9 @@ + + + @@ -58,6 +61,7 @@ + diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs index 7ea86ee53f..30831548dd 100644 --- a/installer/PowerToysSetupVNext/Product.wxs +++ b/installer/PowerToysSetupVNext/Product.wxs @@ -164,7 +164,7 @@ - +