[RAPPS] Don't display duplicate uninstall entries when WoW64 keys are unsupported (#8253)

This only happens on 64-bit because 32-bit was being conservative.
This commit is contained in:
Whindmar Saksit
2026-03-08 19:00:39 +01:00
committed by GitHub
parent 36ffee8ea3
commit f9b3aad55e
4 changed files with 74 additions and 28 deletions
+44 -26
View File
@@ -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<CAppInfo *> &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<CAppInfo *> *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<CAppInfo *> *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)
-2
View File
@@ -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
{
+2
View File
@@ -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
+28
View File
@@ -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)
{