-
Notifications
You must be signed in to change notification settings - Fork 7.5k
fix: Improve install scope detection to prevent parallel installations #44112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -466,7 +466,7 @@ | |
| ENABLEDPOPUP | ||
| ENABLETAB | ||
| ENABLETEMPLATE | ||
| encodedlaunch | ||
Check warningCode scanning / check-spelling Ignored Expect Variant Warning
ekus is ignored by check-spelling because another more general variant is also in expect. (ignored-expect-variant)
|
||
| encryptor | ||
| ENDSESSION | ||
| ENSUREVISIBLE | ||
|
|
@@ -674,7 +674,9 @@ | |
| HKCOMB | ||
| HKCR | ||
| HKCU | ||
| hkcu | ||
| hkey | ||
| hklm | ||
| HKLM | ||
| HKM | ||
| hkmng | ||
|
|
@@ -1057,9 +1059,9 @@ | |
| Mso | ||
| msrc | ||
| msstore | ||
| mstsc | ||
| msvcp | ||
| MT | ||
| mstsc | ||
| MTND | ||
| MULTIPLEUSE | ||
| multizone | ||
|
|
@@ -1203,17 +1205,17 @@ | |
| oleaut | ||
| OLECHAR | ||
| ollama | ||
| ollama | ||
| onebranch | ||
| onnx | ||
| onnx | ||
| OOBEUI | ||
| openas | ||
| opencode | ||
| OPENFILENAME | ||
| openrdp | ||
| opensource | ||
| openxmlformats | ||
| ollama | ||
| onnx | ||
| OPTIMIZEFORINVOKE | ||
| ORPHANEDDIALOGTITLE | ||
| ORSCANS | ||
|
|
@@ -1426,7 +1428,6 @@ | |
| rbhid | ||
| rclsid | ||
| RCZOOMIT | ||
| remotedesktop | ||
| rdp | ||
| RDW | ||
| READMODE | ||
|
|
@@ -1455,6 +1456,7 @@ | |
| REMAPSUCCESSFUL | ||
| REMAPUNSUCCESSFUL | ||
| Remotable | ||
| remotedesktop | ||
| remoteip | ||
| Removelnk | ||
| renamable | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,65 +28,72 @@ namespace registry | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||
| // First, check HKLM for explicit per-machine install scope | ||||||||||||||||||||||||||||||||||||||
| HKEY hklmKey{}; | ||||||||||||||||||||||||||||||||||||||
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, INSTALL_SCOPE_REG_KEY, 0, KEY_READ, &hklmKey) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| // Open HKCU key | ||||||||||||||||||||||||||||||||||||||
| HKEY perUserKey{}; | ||||||||||||||||||||||||||||||||||||||
| if (RegOpenKeyExW(HKEY_CURRENT_USER, | ||||||||||||||||||||||||||||||||||||||
| INSTALL_SCOPE_REG_KEY, | ||||||||||||||||||||||||||||||||||||||
| 0, | ||||||||||||||||||||||||||||||||||||||
| KEY_READ, | ||||||||||||||||||||||||||||||||||||||
| &perUserKey) != ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
| DWORD dataSize{}; | ||||||||||||||||||||||||||||||||||||||
| if (RegGetValueW(hklmKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, nullptr, &dataSize) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| // both keys are missing | ||||||||||||||||||||||||||||||||||||||
| return InstallScope::PerMachine; | ||||||||||||||||||||||||||||||||||||||
| std::wstring data; | ||||||||||||||||||||||||||||||||||||||
| data.resize(dataSize / sizeof(wchar_t)); | ||||||||||||||||||||||||||||||||||||||
| if (RegGetValueW(hklmKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, &data[0], &dataSize) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| RegCloseKey(hklmKey); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| if (data.find(L"perMachine") != std::wstring::npos) | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| return InstallScope::PerMachine; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| RegCloseKey(hklmKey); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| RegCloseKey(hklmKey); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+32
to
+57
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Then check HKCU for per-user install | ||||||||||||||||||||||||||||||||||||||
| HKEY hkcuKey{}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| if (RegOpenKeyExW(HKEY_CURRENT_USER, INSTALL_SCOPE_REG_KEY, 0, KEY_READ, &hkcuKey) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| DWORD dataSize{}; | ||||||||||||||||||||||||||||||||||||||
| if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, nullptr, &dataSize) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||
| if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, &data[0], &dataSize) == ERROR_SUCCESS) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, &data[0], &dataSize) == ERROR_SUCCESS) | |
| if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, data.data(), &dataSize) == ERROR_SUCCESS) |
Fixed
Show fixed
Hide fixed
Fixed
Show fixed
Hide fixed
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the HKLM case - consider using the detail::on_exit RAII pattern for managing the registry key, which is the established pattern in this codebase:
HKEY hkcuKey{};
if (RegOpenKeyExW(HKEY_CURRENT_USER, INSTALL_SCOPE_REG_KEY, 0, KEY_READ, &hkcuKey) == ERROR_SUCCESS)
{
detail::on_exit closeKey{ [hkcuKey] { RegCloseKey(hkcuKey); } };
DWORD dataSize{};
if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, nullptr, &dataSize) == ERROR_SUCCESS)
{
std::wstring data;
data.resize(dataSize / sizeof(wchar_t));
if (RegGetValueW(hkcuKey, nullptr, L"InstallScope", RRF_RT_REG_SZ, nullptr, data.data(), &dataSize) == ERROR_SUCCESS)
{
if (data.find(L"perUser") != std::wstring::npos)
{
return InstallScope::PerUser;
}
}
}
}
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing include: std::transform is used on line 92, but <algorithm> is not included in this header file. This will cause a compilation error.
Add the required include at the top of the file:
#include <algorithm>
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete path check: The fallback logic only checks for \appdata\local\ but doesn't verify that it's actually within the user's profile. A malicious or unusual installation could have a path like C:\SomePath\appdata\local\PowerToys which would incorrectly match as PerUser.
Additionally, the default Program Files paths can vary (e.g., Program Files (x86), non-English Windows with localized folder names, or custom installation drives).
Consider checking against known environment variables or using stricter pattern matching:
wchar_t* localAppData = nullptr;
if (_wdupenv_s(&localAppData, nullptr, L"LOCALAPPDATA") == 0 && localAppData != nullptr)
{
std::wstring localAppDataPath(localAppData);
std::transform(localAppDataPath.begin(), localAppDataPath.end(), localAppDataPath.begin(), ::towlower);
free(localAppData);
if (path.find(localAppDataPath) == 0)
{
return InstallScope::PerUser;
}
}This ensures the path actually starts with the user's LocalAppData folder.
| if (path.find(L"\\appdata\\local\\") != std::wstring::npos) | |
| { | |
| return InstallScope::PerUser; | |
| wchar_t* localAppData = nullptr; | |
| if (_wdupenv_s(&localAppData, nullptr, L"LOCALAPPDATA") == 0 && localAppData != nullptr) | |
| { | |
| std::wstring localAppDataPath(localAppData); | |
| std::transform(localAppDataPath.begin(), localAppDataPath.end(), localAppDataPath.begin(), ::towlower); | |
| free(localAppData); | |
| // Ensure trailing backslash for proper prefix match | |
| if (!localAppDataPath.empty() && localAppDataPath.back() != L'\\') | |
| { | |
| localAppDataPath += L'\\'; | |
| } | |
| if (path.find(localAppDataPath) == 0) | |
| { | |
| return InstallScope::PerUser; | |
| } |
Uh oh!
There was an error while loading. Please reload this page.