diff --git a/base/applications/rapps/appdb.cpp b/base/applications/rapps/appdb.cpp index 4e1ca6253c4..b931ded745b 100644 --- a/base/applications/rapps/appdb.cpp +++ b/base/applications/rapps/appdb.cpp @@ -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(Cat), AppsPath); + if (pAppInfo->Valid()) + return pAppInfo; + delete pAppInfo; + return NULL; +} + void CAppDB::GetApps(CAtlList &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(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); diff --git a/base/applications/rapps/appinfo.cpp b/base/applications/rapps/appinfo.cpp index d43fa81acc2..5766d9a9178 100644 --- a/base/applications/rapps/appinfo.cpp +++ b/base/applications/rapps/appinfo.cpp @@ -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) { diff --git a/base/applications/rapps/geninst.cpp b/base/applications/rapps/geninst.cpp index f89d6d67b71..b5caf9b3aa7 100644 --- a/base/applications/rapps/geninst.cpp +++ b/base/applications/rapps/geninst.cpp @@ -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 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()); diff --git a/base/applications/rapps/include/appdb.h b/base/applications/rapps/include/appdb.h index 05a6d4f6181..020b93866bb 100644 --- a/base/applications/rapps/include/appdb.h +++ b/base/applications/rapps/include/appdb.h @@ -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 { diff --git a/base/applications/rapps/include/appinfo.h b/base/applications/rapps/include/appinfo.h index f312c19e273..019533e7279 100644 --- a/base/applications/rapps/include/appinfo.h +++ b/base/applications/rapps/include/appinfo.h @@ -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; diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index 70feca26511..cc9d2dd6d98 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -17,6 +17,13 @@ #define CurrentArchitecture L"ppc" #endif +template 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 diff --git a/base/applications/rapps/loaddlg.cpp b/base/applications/rapps/loaddlg.cpp index 6021b672a5c..e325178181d 100644 --- a/base/applications/rapps/loaddlg.cpp +++ b/base/applications/rapps/loaddlg.cpp @@ -149,7 +149,10 @@ struct DownloadInfo CConfigParser *cfg = static_cast(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 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); diff --git a/base/applications/rapps/misc.cpp b/base/applications/rapps/misc.cpp index 407ab54afc7..13b7026b10c 100644 --- a/base/applications/rapps/misc.cpp +++ b/base/applications/rapps/misc.cpp @@ -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; }