diff --git a/base/applications/rapps/appdb.cpp b/base/applications/rapps/appdb.cpp index 35326830a3f..4e1ca6253c4 100644 --- a/base/applications/rapps/appdb.cpp +++ b/base/applications/rapps/appdb.cpp @@ -18,6 +18,48 @@ static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCA static REGSAM g_RegSamEnum[3] = {0, KEY_WOW64_32KEY, KEY_WOW64_64KEY}; #define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" +static inline HKEY +GetRootKeyInfo(UINT Index, REGSAM &RegSam) +{ + C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum)); + if (Index < _countof(g_RootKeyEnum)) + { + RegSam = g_RegSamEnum[Index]; + return g_RootKeyEnum[Index]; + } + return NULL; +} + +class CEnumInstalledRootKey +{ + // HKCU + HKLM (Native) + HKLM (WoW64) + // Note that HKEY_CURRENT_USER\Software does not have a redirect + // https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64 + + UINT m_Count; + UINT m_Index = 0; + +public: + CEnumInstalledRootKey() + { + // We don't want to display duplicate entries on systems without WoW64 keys support. + // When ROS starts supporting the WOW REGSAM flags, + // this code can be changed back to IsSystem64Bit() ? 3 : 2. + m_Count = 2 + !IsSameRegKey(HKEY_LOCAL_MACHINE, UNINSTALL_SUBKEY, KEY_WOW64_64KEY, + UNINSTALL_SUBKEY, KEY_WOW64_32KEY); + } + + HKEY GetNext(REGSAM &RegSam) + { + return m_Index < m_Count ? GetRootKeyInfo(m_Index++, RegSam) : NULL; + } + + UINT GetKeyIndex() const + { + return m_Index; + } +}; + static VOID ClearList(CAtlList &list) { @@ -150,30 +192,6 @@ CAppDB::UpdateAvailable() EnumerateFiles(); } -static inline HKEY -GetRootKeyInfo(UINT Index, REGSAM &RegSam) -{ - C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum)); - if (Index < _countof(g_RootKeyEnum)) - { - RegSam = g_RegSamEnum[Index]; - return g_RootKeyEnum[Index]; - } - return NULL; -} - -HKEY -CAppDB::EnumInstalledRootKey(UINT Index, REGSAM &RegSam) -{ - // Loop for through all combinations. - // Note that HKEY_CURRENT_USER\Software does not have a redirect - // https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64 - if (Index < (IsSystem64Bit() ? 3 : 2)) - return GetRootKeyInfo(Index, RegSam); - else - return NULL; -} - CInstalledApplicationInfo * CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex) { @@ -209,7 +227,7 @@ CAppDB::EnumerateRegistry(CAtlList *List, LPCWSTR SearchOnly) ATLASSERT(List || SearchOnly); REGSAM wowsam; HKEY hRootKey; - for (UINT rki = 0; (hRootKey = EnumInstalledRootKey(rki, wowsam)); ++rki) + for (CEnumInstalledRootKey RootEnum; (hRootKey = RootEnum.GetNext(wowsam)) != NULL;) { CRegKey hKey; if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | wowsam) != ERROR_SUCCESS) @@ -227,7 +245,7 @@ CAppDB::EnumerateRegistry(CAtlList *List, LPCWSTR SearchOnly) if (List || !StrCmpIW(SearchOnly, szKeyName)) { CInstalledApplicationInfo *Info; - Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, rki); + Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, RootEnum.GetKeyIndex()); if (Info) { if (List) diff --git a/base/applications/rapps/include/appdb.h b/base/applications/rapps/include/appdb.h index af927b55ab8..05a6d4f6181 100644 --- a/base/applications/rapps/include/appdb.h +++ b/base/applications/rapps/include/appdb.h @@ -47,8 +47,6 @@ class CAppDB CreateInstalledAppByRegistryKey(LPCWSTR Name); static CInstalledApplicationInfo * CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam); - static HKEY - EnumInstalledRootKey(UINT Index, REGSAM &RegSam); size_t GetAvailableCount() const { diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index 73f5165141a..70feca26511 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -97,6 +97,8 @@ UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime); BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle); +BOOL +IsSameRegKey(HKEY hRoot, LPCWSTR Path1, REGSAM Sam1, LPCWSTR Path2, REGSAM Sam2); HRESULT RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam = 0); LPCWSTR diff --git a/base/applications/rapps/misc.cpp b/base/applications/rapps/misc.cpp index cf6ad378322..407ab54afc7 100644 --- a/base/applications/rapps/misc.cpp +++ b/base/applications/rapps/misc.cpp @@ -10,6 +10,8 @@ #include "rapps.h" #include "misc.h" +EXTERN_C NTSTATUS WINAPI NtQueryObject(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); + static HANDLE hLog = NULL; UINT @@ -452,6 +454,32 @@ UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime) pFileTime->dwHighDateTime = ll >> 32; } +static BOOL +IsSameRegKey(HKEY hKey1, HKEY hKey2) +{ + // CompareObjectHandles is Win10+ so we check the path instead. + struct NameInfo : UNICODE_STRING + { + WCHAR Allocation[MAX_PATH]; + NameInfo() { MaximumLength = sizeof(Allocation); Buffer = Allocation; } + }; + NameInfo Name1, Name2; + ULONG Length; + return NT_SUCCESS(NtQueryObject(hKey1, ObjectNameInformation, &Name1, sizeof(Name1), &Length)) && + NT_SUCCESS(NtQueryObject(hKey2, ObjectNameInformation, &Name2, sizeof(Name2), &Length)) && + RtlCompareUnicodeString(&Name1, &Name2, TRUE) == 0; +} + +BOOL +IsSameRegKey(HKEY hRoot, LPCWSTR Path1, REGSAM Sam1, LPCWSTR Path2, REGSAM Sam2) +{ + REGSAM WowMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY; + CRegKey key1, key2; + return key1.Open(hRoot, Path1, MAXIMUM_ALLOWED | (Sam1 & WowMask)) == ERROR_SUCCESS && + key2.Open(hRoot, Path2, MAXIMUM_ALLOWED | (Sam2 & WowMask)) == ERROR_SUCCESS && + IsSameRegKey(key1, key2); +} + HRESULT RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam) {