Skip to content

Commit

Permalink
[RAPPS] Rework application handling
Browse files Browse the repository at this point in the history
Previously, there would be function duplication between installed and available applications.
Now this is handled with polymorphism, which allows to re-use a lot of code.
Additionally, toolbar buttons are properly disabled now.
The mutex used to guard rapps' single instance is renamed,
so that the 'new' and old rapps can be run at the same time for testing.

CORE-18459
  • Loading branch information
learn-more committed Feb 20, 2023
1 parent 0cd80c1 commit 33c2903
Show file tree
Hide file tree
Showing 30 changed files with 3,729 additions and 4,047 deletions.
8 changes: 4 additions & 4 deletions base/applications/rapps/CMakeLists.txt
Expand Up @@ -5,29 +5,29 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
include_directories(include)

list(APPEND SOURCE
applicationdb.cpp
applicationinfo.cpp
appview.cpp
asyncinet.cpp
available.cpp
cabinet.cpp
configparser.cpp
gui.cpp
installed.cpp
integrity.cpp
loaddlg.cpp
misc.cpp
settings.cpp
settingsdlg.cpp
unattended.cpp
winmain.cpp
include/applicationdb.h
include/applicationinfo.h
include/appview.h
include/asyncinet.h
include/available.h
include/configparser.h
include/crichedit.h
include/defines.h
include/dialogs.h
include/gui.h
include/installed.h
include/misc.h
include/rapps.h
include/resource.h
Expand Down
291 changes: 291 additions & 0 deletions base/applications/rapps/applicationdb.cpp
@@ -0,0 +1,291 @@
/*
* PROJECT: ReactOS Applications Manager
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Classes for working with available applications
* COPYRIGHT: Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
* Copyright 2020 He Yang (1160386205@qq.com)
* Copyright 2021-2023 Mark Jansen <mark.jansen@reactos.org>
*/

#include "rapps.h"
#include "applicationdb.h"
#include "configparser.h"
#include "settings.h"


static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
static REGSAM g_RegSamEnum[3] = {KEY_WOW64_32KEY, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
#define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"

static VOID
ClearList(CAtlList<CApplicationInfo *> &list)
{
POSITION InfoListPosition = list.GetHeadPosition();
while (InfoListPosition)
{
CApplicationInfo *Info = list.GetNext(InfoListPosition);
delete Info;
}
list.RemoveAll();
}

CApplicationDB::CApplicationDB(const CStringW &path) : m_BasePath(path)
{
m_BasePath.Canonicalize();
}

CApplicationInfo *
CApplicationDB::FindByPackageName(const CStringW &name)
{
POSITION CurrentListPosition = m_Available.GetHeadPosition();
while (CurrentListPosition)
{
CApplicationInfo *Info = m_Available.GetNext(CurrentListPosition);
if (Info->szIdentifier == name)
{
return Info;
}
}
return NULL;
}

void
CApplicationDB::GetApps(CAtlList<CApplicationInfo *> &List, AppsCategories Type) const
{
const BOOL UseInstalled = IsInstalledEnum(Type);
const CAtlList<CApplicationInfo *> &list = UseInstalled ? m_Installed : m_Available;
const BOOL IncludeAll = UseInstalled ? (Type == ENUM_ALL_INSTALLED) : (Type == ENUM_ALL_AVAILABLE);

POSITION CurrentListPosition = list.GetHeadPosition();
while (CurrentListPosition)
{
CApplicationInfo *Info = list.GetNext(CurrentListPosition);

if (IncludeAll || Type == Info->iCategory)
{
List.AddTail(Info);
}
}
}

BOOL
CApplicationDB::EnumerateFiles()
{
ClearList(m_Available);

CPathW AppsPath = m_BasePath;
AppsPath += L"rapps";
CPathW WildcardPath = AppsPath;
WildcardPath += L"*.txt";

WIN32_FIND_DATAW FindFileData;
HANDLE hFind = FindFirstFileW(WildcardPath, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
return FALSE;
}

do
{
CStringW szPkgName = FindFileData.cFileName;
PathRemoveExtensionW(szPkgName.GetBuffer(MAX_PATH));
szPkgName.ReleaseBuffer();

CApplicationInfo *Info = FindByPackageName(szPkgName);
ATLASSERT(Info == NULL);
if (!Info)
{
CConfigParser *Parser = new CConfigParser(FindFileData.cFileName);
int Cat;
if (!Parser->GetInt(L"Category", Cat))
Cat = ENUM_INVALID;

Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
if (Info->Valid())
{
m_Available.AddTail(Info);
}
else
{
delete Info;
}
}

} while (FindNextFileW(hFind, &FindFileData));

FindClose(hFind);
return TRUE;
}

VOID
CApplicationDB::UpdateAvailable()
{
if (!CreateDirectoryW(m_BasePath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
return;

if (EnumerateFiles())
return;

DownloadApplicationsDB(
SettingsInfo.bUseSource ? SettingsInfo.szSourceURL : APPLICATION_DATABASE_URL, !SettingsInfo.bUseSource);

CPathW AppsPath = m_BasePath;
AppsPath += L"rapps";
if (!ExtractFilesFromCab(APPLICATION_DATABASE_NAME, m_BasePath, AppsPath))
return;

CPathW CabFile = m_BasePath;
CabFile += APPLICATION_DATABASE_NAME;
DeleteFileW(CabFile);

EnumerateFiles();
}

VOID
CApplicationDB::UpdateInstalled()
{
// Remove all old entries
ClearList(m_Installed);

int LoopKeys = 2;

if (IsSystem64Bit())
{
// loop for all 3 combination.
// note that HKEY_CURRENT_USER\Software don't have a redirect
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64
LoopKeys = 3;
}

for (int keyIndex = 0; keyIndex < LoopKeys; keyIndex++)
{
LONG ItemIndex = 0;
WCHAR szKeyName[MAX_PATH];

CRegKey hKey;
if (hKey.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | g_RegSamEnum[keyIndex]) != ERROR_SUCCESS)
{
continue;
}

while (1)
{
DWORD dwSize = _countof(szKeyName);
if (hKey.EnumKey(ItemIndex, szKeyName, &dwSize) != ERROR_SUCCESS)
{
break;
}

ItemIndex++;

CRegKey hSubKey;
if (hSubKey.Open(hKey, szKeyName, KEY_READ) == ERROR_SUCCESS)
{
DWORD dwValue = 0;

dwSize = sizeof(DWORD);
if (RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&dwValue, &dwSize) ==
ERROR_SUCCESS &&
dwValue == 1)
{
// Ignore system components
continue;
}

BOOL bIsUpdate =
(RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &dwSize) == ERROR_SUCCESS);

CInstalledApplicationInfo *Info = new CInstalledApplicationInfo(
hSubKey.Detach(), szKeyName, bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS, keyIndex);

if (Info->Valid())
{
m_Installed.AddTail(Info);
}
else
{
delete Info;
}
}
}
}
}

static void
DeleteWithWildcard(const CPathW &Dir, const CStringW &Filter)
{
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW FindFileData;

CPathW DirWithFilter = Dir;
DirWithFilter += Filter;

hFind = FindFirstFileW(DirWithFilter, &FindFileData);

if (hFind == INVALID_HANDLE_VALUE)
return;

do
{
CPathW szTmp = Dir;
szTmp += FindFileData.cFileName;

if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
DeleteFileW(szTmp);
}
} while (FindNextFileW(hFind, &FindFileData) != 0);
FindClose(hFind);
}

VOID
CApplicationDB::RemoveCached()
{
// Delete icons
CPathW AppsPath = m_BasePath;
AppsPath += L"rapps";
CPathW IconPath = AppsPath;
IconPath += L"icons";
DeleteWithWildcard(IconPath, L"*.ico");

// Delete leftover screenshots
CPathW ScrnshotFolder = AppsPath;
ScrnshotFolder += L"screenshots";
DeleteWithWildcard(ScrnshotFolder, L"*.tmp");

// Delete data base files (*.txt)
DeleteWithWildcard(AppsPath, L"*.txt");

RemoveDirectoryW(IconPath);
RemoveDirectoryW(ScrnshotFolder);
RemoveDirectoryW(AppsPath);
RemoveDirectoryW(m_BasePath);
}

BOOL
CApplicationDB::RemoveInstalledAppFromRegistry(const CApplicationInfo *Info)
{
// Validate that this is actually an installed app / update
ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES);
if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES)
return FALSE;

// Grab the index in the registry keys
const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info);
ATLASSERT(InstalledInfo->iKeyIndex >= 0 && InstalledInfo->iKeyIndex < (int)_countof(g_RootKeyEnum));
if (InstalledInfo->iKeyIndex < 0 && InstalledInfo->iKeyIndex >= (int)_countof(g_RootKeyEnum))
return FALSE;

int keyIndex = InstalledInfo->iKeyIndex;

// Grab the registry key name
CStringW Name = InstalledInfo->szIdentifier;

// Recursively delete this key
CRegKey Uninstall;
if (Uninstall.Open(g_RootKeyEnum[keyIndex], UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | g_RegSamEnum[keyIndex]) !=
ERROR_SUCCESS)
return FALSE;

return Uninstall.RecurseDeleteKey(Name) == ERROR_SUCCESS;
}

2 comments on commit 33c2903

@learn-more
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was PR #5070 but I pushed in the wrong order.

@JoachimHenze
Copy link
Contributor

@JoachimHenze JoachimHenze commented on 33c2903 Feb 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was reported later, that this big commit also improved the operation of the rapps tool on XPSP3 and therefore fixed also https://jira.reactos.org/browse/CORE-18704

It was also claimed later that this commit introduced a regression, which is then aimed to be fixed again by:
#6864

Please sign in to comment.