[RAPPS] Automatically install package dependencies (#8963)

This commit is contained in:
Whindmar Saksit
2026-05-14 13:23:40 +02:00
committed by GitHub
parent c6411ffa05
commit 6644a55343
8 changed files with 103 additions and 45 deletions

View File

@@ -17,6 +17,7 @@
static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
static REGSAM g_RegSamEnum[3] = {0, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
#define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
#define MANIFEST_DOTEXT L".txt"
static inline HKEY
GetRootKeyInfo(UINT Index, REGSAM &RegSam)
@@ -100,6 +101,24 @@ CAppDB::FindAvailableByPackageName(const CStringW &name)
return NULL;
}
CAvailableApplicationInfo *
CAppDB::CreateAvailableAppInstance(const CStringW &PkgName, PCWSTR DBPath)
{
CAvailableApplicationInfo *pAppInfo;
CPathW AppsPath = DBPath ? CPathW(DBPath) : (CPathW(GetDefaultPath()) += RAPPS_DATABASE_SUBDIR);
CPathW ManifestPath = CPathW(AppsPath) += PkgName + MANIFEST_DOTEXT;
CConfigParser *Parser = new CConfigParser(ManifestPath);
int Cat;
if (!Parser->GetInt(DB_CATEGORY, Cat))
Cat = ENUM_INVALID;
pAppInfo = new CAvailableApplicationInfo(Parser, PkgName, static_cast<AppsCategories>(Cat), AppsPath);
if (pAppInfo->Valid())
return pAppInfo;
delete pAppInfo;
return NULL;
}
void
CAppDB::GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const
{
@@ -127,7 +146,7 @@ CAppDB::EnumerateFiles()
CPathW AppsPath = m_BasePath;
AppsPath += RAPPS_DATABASE_SUBDIR;
CPathW WildcardPath = AppsPath;
WildcardPath += L"*.txt";
WildcardPath += L"*" MANIFEST_DOTEXT;
WIN32_FIND_DATAW FindFileData;
HANDLE hFind = FindFirstFileW(WildcardPath, &FindFileData);
@@ -142,24 +161,13 @@ CAppDB::EnumerateFiles()
PathRemoveExtensionW(szPkgName.GetBuffer(MAX_PATH));
szPkgName.ReleaseBuffer();
CAppInfo *Info = FindByPackageName(szPkgName);
CAppInfo *Info = FindAvailableByPackageName(szPkgName);
ATLASSERT(Info == NULL);
if (!Info)
{
CConfigParser *Parser = new CConfigParser(CPathW(AppsPath) += FindFileData.cFileName);
int Cat;
if (!Parser->GetInt(DB_CATEGORY, Cat))
Cat = ENUM_INVALID;
Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
if (Info->Valid())
{
Info = CreateAvailableAppInstance(szPkgName, AppsPath);
if (Info)
m_Available.AddTail(Info);
}
else
{
delete Info;
}
}
} while (FindNextFileW(hFind, &FindFileData));
@@ -330,7 +338,7 @@ CAppDB::RemoveCached()
DeleteWithWildcard(ScrnshotFolder, L"*.tmp");
// Delete data base files (*.txt)
DeleteWithWildcard(AppsPath, L"*.txt");
DeleteWithWildcard(AppsPath, L"*" MANIFEST_DOTEXT);
RemoveDirectoryW(IconPath);
RemoveDirectoryW(ScrnshotFolder);

View File

@@ -120,6 +120,14 @@ CompareVersion(const CStringW &left, const CStringW &right)
}
}
bool
CAvailableApplicationInfo::IsInstalled() const
{
CStringW szRegName;
m_Parser->GetString(DB_REGNAME, szRegName);
return ::GetInstalledVersion(NULL, szRegName) || ::GetInstalledVersion(NULL, szDisplayName);
}
VOID
CAvailableApplicationInfo::InsertVersionInfo(CAppRichEdit *RichEdit)
{

View File

@@ -850,13 +850,11 @@ HRESULT
ExtractArchiveForExecution(PCWSTR pszArchive, const CStringW &PackageName, CStringW &TempDir, CStringW &App)
{
WCHAR TempDirBuf[MAX_PATH], UniqueDir[MAX_PATH];
CAppDB db(CAppDB::GetDefaultPath());
db.UpdateAvailable();
CAvailableApplicationInfo *pAppInfo = db.FindAvailableByPackageName(PackageName);
CAvailableApplicationInfo *pAppInfo = CAppDB::CreateAvailableAppInstance(PackageName);
Deleter <CAvailableApplicationInfo*>del(pAppInfo);
if (!pAppInfo)
return HResultFromWin32(ERROR_NOT_FOUND);
CConfigParser *pCfg = pAppInfo->GetConfigParser();
if (!GetTempPathW(_countof(TempDirBuf), TempDirBuf))
return E_FAIL;
wsprintfW(UniqueDir, L"~%s-%u", RAPPS_NAME, GetCurrentProcessId());

View File

@@ -47,6 +47,8 @@ class CAppDB
CreateInstalledAppByRegistryKey(LPCWSTR Name);
static CInstalledApplicationInfo *
CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam);
static CAvailableApplicationInfo *
CreateAvailableAppInstance(const CStringW &PkgName, PCWSTR DBPath = NULL);
size_t GetAvailableCount() const
{

View File

@@ -99,6 +99,7 @@ enum InstallerType
#define DB_SAVEAS L"SaveAs"
#define DB_SILENTARGS L"SilentParameters"
#define DB_NTVER L"NTVersion" // "Max-" || "Min-Max" || "Min" || "Min+"
#define DB_DEPENDENCIES L"Dependencies"
#define DB_GENINSTSECTION L"Generate"
#define GENERATE_ARPSUBKEY L"RApps" // Our uninstall data is stored here
@@ -178,6 +179,8 @@ class CAvailableApplicationInfo : public CAppInfo
bool
IsCompatible() const;
bool
IsInstalled() const;
virtual BOOL
Valid() const override;

View File

@@ -17,6 +17,13 @@
#define CurrentArchitecture L"ppc"
#endif
template<class T> struct Deleter
{
Deleter(T ptr) : p(ptr) {}
~Deleter () { delete p; }
T p;
};
static inline HRESULT
HResultFromWin32(UINT Error)
{
@@ -46,6 +53,8 @@ UINT
ClassifyFile(PCWSTR Path);
BOOL
OpensWithExplorer(PCWSTR Path);
UINT
WaitForProcess(HANDLE hProcess);
BOOL
StartProcess(const CStringW &Path, BOOL Wait);
BOOL

View File

@@ -149,7 +149,10 @@ struct DownloadInfo
CConfigParser *cfg = static_cast<const CAvailableApplicationInfo&>(AppInfo).GetConfigParser();
if (cfg)
{
cfg->GetString(DB_SAVEAS, szFileName);
cfg->GetString(DB_DEPENDENCIES, szDependencies);
}
}
bool Equal(const DownloadInfo &other) const
@@ -167,6 +170,7 @@ struct DownloadInfo
CStringW szPackageName;
CStringW szFileName;
CStringW szSilentInstallArgs;
CStringW szDependencies;
ULONG SizeInBytes;
};
@@ -579,6 +583,30 @@ CDownloadManager::Add(const DownloadInfo &Info)
if (Info.Equal(m_List[i]))
return; // Already in the list
}
if (!Info.szDependencies.IsEmpty())
{
CStringW deps = Info.szDependencies;
for (int pos = 1; pos > 0; deps = deps.Mid(pos + 1))
{
pos = deps.Find(L'|');
CStringW pkg = (pos > 0 ? deps.Left(pos) : deps).Trim();
// In the future a package might need to specify dependency version or WoW64
// information ("vcredist*x64" etc), for now, just remove possible modifiers.
int suffix = pkg.FindOneOf(L"*<=>:/\\?");
if (suffix > 0)
pkg = pkg.Left(suffix);
CAvailableApplicationInfo *pApp = CAppDB::CreateAvailableAppInstance(pkg);
Deleter <CAvailableApplicationInfo*>del(pApp);
if (!pApp || pApp->IsInstalled())
continue;
// Add the dependency before the application in the list
Add(DownloadInfo(*pApp, DAF_SILENT));
}
}
m_List.Add(Info);
if (m_hDlg)
m_ListView.LoadList(m_List, start);

View File

@@ -174,13 +174,36 @@ OpensWithExplorer(PCWSTR Path)
return SUCCEEDED(hr) && !StrCmpIW(PathFindFileNameW(szCmd), L"explorer.exe"); // .cab
}
UINT
WaitForProcess(HANDLE hProcess)
{
DWORD code = STILL_ACTIVE;
for (;;)
{
DWORD wait = MsgWaitForMultipleObjects(1, &hProcess, FALSE, INFINITE, QS_ALLEVENTS);
if (wait == WAIT_OBJECT_0 + 1)
{
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
else
{
// TODO: if (wait == WAIT_OBJECT_0) GetExitCodeProcess for MSI reboot codes
break;
}
}
return code;
}
BOOL
StartProcess(const CStringW &Path, BOOL Wait)
{
PROCESS_INFORMATION pi;
STARTUPINFOW si;
DWORD dwRet;
MSG msg;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
@@ -201,37 +224,16 @@ StartProcess(const CStringW &Path, BOOL Wait)
if (Wait)
{
EnableWindow(hMainWnd, FALSE);
}
while (Wait)
{
dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
if (dwRet == WAIT_OBJECT_0 + 1)
{
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
else
{
if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED)
break;
}
}
WaitForProcess(pi.hProcess);
CloseHandle(pi.hProcess);
if (Wait)
{
EnableWindow(hMainWnd, TRUE);
SetForegroundWindow(hMainWnd);
// We got the real activation message during MsgWaitForMultipleObjects while
// we were disabled, we need to set the focus again now.
SetFocus(hMainWnd);
}
CloseHandle(pi.hProcess);
return TRUE;
}