[installer] Support per-user installation (#24087)

* Add per user installer

* Separate upgrade codes for per machine and per user installation
Move per machine check to bootstrapper
Move all defines to common.wxs
Fix CI

* Update installer/PowerToysSetup/generateFileList.ps1

Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>

* Update installer/PowerToysSetup/generateAllFileComponents.ps1

Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>

* Update installer/PowerToysSetup/generateFileList.ps1

Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>

* expect.txt

* Revert "Update installer/PowerToysSetup/generateFileList.ps1"

This reverts commit 34545dab9c.

* Update release CI to build both installers

* Revert bundle name change

It messes up app ID for per-user installation which ends up breaking winget update
of the per-user PT

* spellcheck

* Fix bad merge

* Add RegistryPreview

* Include backup_restore_settings.json

* Revert testing endpoint change

---------

Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>
This commit is contained in:
Stefan Markovic
2023-03-31 12:23:57 +02:00
committed by GitHub
parent 07579c910a
commit 870f8e3571
48 changed files with 3263 additions and 1128 deletions

View File

@@ -8,6 +8,9 @@
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/json.h>
#include <common/utils/registry.h>
using namespace registry::install_scope;
namespace // Strings in this namespace should not be localized
{
@@ -42,9 +45,16 @@ namespace updating
std::pair<Uri, std::wstring> extract_installer_asset_download_info(const json::JsonObject& release_object)
{
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
// Desc-sorted by its priority
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
const InstallScope current_install_scope = get_current_install_scope();
if (current_install_scope == InstallScope::PerUser)
{
required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN_USER;
}
for (const auto asset_extension : asset_extensions)
{
for (auto asset_elem : release_object.GetNamedArray(L"assets"))

View File

@@ -31,4 +31,5 @@ namespace updating
// non-localized
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN_USER = L"powertoysusersetup";
}

View File

@@ -15,6 +15,7 @@
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">

View File

@@ -12,14 +12,16 @@
namespace // Strings in this namespace should not be localized
{
const inline wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
const inline wchar_t POWER_TOYS_UPGRADE_CODE_USER[] = L"{D8B559DB-4C98-487A-A33F-50A8EEE42726}";
const inline wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
}
std::optional<std::wstring> GetMsiPackageInstalledPath()
std::optional<std::wstring> GetMsiPackageInstalledPath(bool perUser)
{
constexpr size_t guid_length = 39;
wchar_t product_ID[guid_length];
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, product_ID); !found)
std::wstring upgradeCode = (perUser ? POWER_TOYS_UPGRADE_CODE_USER : POWER_TOYS_UPGRADE_CODE);
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(upgradeCode.c_str(), 0, 0, product_ID); !found)
{
return std::nullopt;
}

View File

@@ -16,6 +16,83 @@
namespace registry
{
namespace install_scope
{
const wchar_t INSTALL_SCOPE_REG_KEY[] = L"Software\\Classes\\powertoys\\";
enum class InstallScope
{
PerMachine = 0,
PerUser,
};
inline const InstallScope get_current_install_scope()
{
// Open HKLM key
HKEY perMachineKey{};
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
INSTALL_SCOPE_REG_KEY,
0,
KEY_READ,
&perMachineKey) != ERROR_SUCCESS)
{
// Open HKCU key
HKEY perUserKey{};
if (RegOpenKeyExW(HKEY_CURRENT_USER,
INSTALL_SCOPE_REG_KEY,
0,
KEY_READ,
&perUserKey) != ERROR_SUCCESS)
{
// both keys are missing
return InstallScope::PerMachine;
}
else
{
DWORD dataSize{};
if (RegGetValueW(
perUserKey,
nullptr,
L"InstallScope",
RRF_RT_REG_SZ,
nullptr,
nullptr,
&dataSize) != ERROR_SUCCESS)
{
// HKCU key is missing
RegCloseKey(perUserKey);
return InstallScope::PerMachine;
}
std::wstring data;
data.resize(dataSize / sizeof(wchar_t));
if (RegGetValueW(
perUserKey,
nullptr,
L"InstallScope",
RRF_RT_REG_SZ,
nullptr,
&data[0],
&dataSize) != ERROR_SUCCESS)
{
// HKCU key is missing
RegCloseKey(perUserKey);
return InstallScope::PerMachine;
}
RegCloseKey(perUserKey);
if (data.contains(L"perUser"))
{
return InstallScope::PerUser;
}
}
}
return InstallScope::PerMachine;
}
}
template<class>
inline constexpr bool always_false_v = false;