mirror of
https://github.com/reactos/reactos.git
synced 2025-01-05 13:59:25 +00:00
4900dd3d87
Parse InstallDate registry key in "YYYYMMDD" format. CORE-17422 Signed-off-by: Marcus Boillat <marcus.boillat@gmail.com> Reviewed-by: Mark Jansen <mark.jansen@reactos.org> Reviewed-by: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org> Reviewed-by: Stanislav Motylkov <x86corez@gmail.com>
371 lines
12 KiB
C++
371 lines
12 KiB
C++
/*
|
|
* PROJECT: ReactOS Applications Manager
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Classes for working with installed applications
|
|
* COPYRIGHT: Copyright 2009 Dmitry Chapyshev <dmitry@reactos.org>
|
|
* Copyright 2017 Alexander Shaposhnikov <sanchaez@reactos.org>
|
|
* Copyright 2020 He Yang <1160386205@qq.com>
|
|
* Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
|
|
*/
|
|
|
|
#include "rapps.h"
|
|
#include "installed.h"
|
|
#include "misc.h"
|
|
|
|
CInstalledApplicationInfo::CInstalledApplicationInfo(BOOL bIsUserKey, REGSAM RegWowKey, HKEY hKey, const CStringW& szKeyName)
|
|
: m_IsUserKey(bIsUserKey)
|
|
, m_WowKey(RegWowKey)
|
|
, m_hSubKey(hKey)
|
|
, m_szKeyName(szKeyName)
|
|
{
|
|
DWORD dwSize = 0;
|
|
bIsUpdate = (RegQueryValueExW(m_hSubKey, L"ParentKeyName", NULL, NULL, NULL, &dwSize) == ERROR_SUCCESS);
|
|
}
|
|
|
|
CInstalledApplicationInfo::~CInstalledApplicationInfo()
|
|
{
|
|
if (m_hSubKey)
|
|
{
|
|
CloseHandle(m_hSubKey);
|
|
m_hSubKey = NULL;
|
|
}
|
|
}
|
|
|
|
void CInstalledApplicationInfo::EnsureDetailsLoaded()
|
|
{
|
|
// Key not closed, so we have not loaded details yet
|
|
if (m_hSubKey)
|
|
{
|
|
GetApplicationRegString(L"Publisher", szPublisher);
|
|
GetApplicationRegString(L"RegOwner", szRegOwner);
|
|
GetApplicationRegString(L"ProductID", szProductID);
|
|
GetApplicationRegString(L"HelpLink", szHelpLink);
|
|
GetApplicationRegString(L"HelpTelephone", szHelpTelephone);
|
|
GetApplicationRegString(L"Readme", szReadme);
|
|
GetApplicationRegString(L"Contact", szContact);
|
|
GetApplicationRegString(L"URLUpdateInfo", szURLUpdateInfo);
|
|
GetApplicationRegString(L"URLInfoAbout", szURLInfoAbout);
|
|
|
|
DWORD dwInstallTimeStamp;
|
|
SYSTEMTIME InstallLocalTime;
|
|
if (GetApplicationRegString(L"InstallDate", szInstallDate))
|
|
{
|
|
ZeroMemory(&InstallLocalTime, sizeof(InstallLocalTime));
|
|
// Check if we have 8 characters to parse the datetime.
|
|
// Maybe other formats exist as well?
|
|
szInstallDate = szInstallDate.Trim();
|
|
if (szInstallDate.GetLength() == 8)
|
|
{
|
|
InstallLocalTime.wYear = wcstol(szInstallDate.Left(4).GetString(), NULL, 10);
|
|
InstallLocalTime.wMonth = wcstol(szInstallDate.Mid(4, 2).GetString(), NULL, 10);
|
|
InstallLocalTime.wDay = wcstol(szInstallDate.Mid(6, 2).GetString(), NULL, 10);
|
|
}
|
|
}
|
|
// It might be a DWORD (Unix timestamp). try again.
|
|
else if (GetApplicationRegDword(L"InstallDate", &dwInstallTimeStamp))
|
|
{
|
|
FILETIME InstallFileTime;
|
|
SYSTEMTIME InstallSystemTime;
|
|
|
|
UnixTimeToFileTime(dwInstallTimeStamp, &InstallFileTime);
|
|
FileTimeToSystemTime(&InstallFileTime, &InstallSystemTime);
|
|
|
|
// convert to localtime
|
|
SystemTimeToTzSpecificLocalTime(NULL, &InstallSystemTime, &InstallLocalTime);
|
|
}
|
|
|
|
// convert to readable date string
|
|
int cchTimeStrLen = GetDateFormatW(LOCALE_USER_DEFAULT, 0, &InstallLocalTime, NULL, 0, 0);
|
|
|
|
GetDateFormatW(
|
|
LOCALE_USER_DEFAULT, // use default locale for current user
|
|
0, &InstallLocalTime, NULL, szInstallDate.GetBuffer(cchTimeStrLen), cchTimeStrLen);
|
|
szInstallDate.ReleaseBuffer();
|
|
|
|
GetApplicationRegString(L"InstallLocation", szInstallLocation);
|
|
GetApplicationRegString(L"InstallSource", szInstallSource);
|
|
DWORD dwWindowsInstaller = 0;
|
|
if (GetApplicationRegDword(L"WindowsInstaller", &dwWindowsInstaller) && dwWindowsInstaller)
|
|
{
|
|
// MSI has the same info in Uninstall / modify, so manually build it
|
|
szUninstallString.Format(L"msiexec /x%s", m_szKeyName.GetString());
|
|
}
|
|
else
|
|
{
|
|
GetApplicationRegString(L"UninstallString", szUninstallString);
|
|
}
|
|
DWORD dwNoModify = 0;
|
|
if (!GetApplicationRegDword(L"NoModify", &dwNoModify))
|
|
{
|
|
CStringW Tmp;
|
|
if (GetApplicationRegString(L"NoModify", Tmp))
|
|
{
|
|
dwNoModify = Tmp.GetLength() > 0 ? (Tmp[0] == '1') : 0;
|
|
}
|
|
else
|
|
{
|
|
dwNoModify = 0;
|
|
}
|
|
}
|
|
if (!dwNoModify)
|
|
{
|
|
if (dwWindowsInstaller)
|
|
{
|
|
szModifyPath.Format(L"msiexec /i%s", m_szKeyName.GetString());
|
|
}
|
|
else
|
|
{
|
|
GetApplicationRegString(L"ModifyPath", szModifyPath);
|
|
}
|
|
}
|
|
|
|
CloseHandle(m_hSubKey);
|
|
m_hSubKey = NULL;
|
|
}
|
|
}
|
|
|
|
BOOL CInstalledApplicationInfo::GetApplicationRegString(LPCWSTR lpKeyName, ATL::CStringW& String)
|
|
{
|
|
DWORD dwAllocated = 0, dwSize, dwType;
|
|
|
|
// retrieve the size of value first.
|
|
if (RegQueryValueExW(m_hSubKey, lpKeyName, NULL, &dwType, NULL, &dwAllocated) != ERROR_SUCCESS)
|
|
{
|
|
String.Empty();
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwType != REG_SZ && dwType != REG_EXPAND_SZ)
|
|
{
|
|
String.Empty();
|
|
return FALSE;
|
|
}
|
|
|
|
// query the value
|
|
dwSize = dwAllocated;
|
|
LSTATUS Result =
|
|
RegQueryValueExW(m_hSubKey, lpKeyName, NULL, NULL, (LPBYTE)String.GetBuffer(dwAllocated / sizeof(WCHAR)), &dwSize);
|
|
|
|
dwSize = min(dwAllocated, dwSize);
|
|
// CString takes care of zero-terminating it
|
|
String.ReleaseBuffer(dwSize / sizeof(WCHAR));
|
|
|
|
if (Result != ERROR_SUCCESS)
|
|
{
|
|
String.Empty();
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwType == REG_EXPAND_SZ)
|
|
{
|
|
CStringW Tmp;
|
|
|
|
DWORD dwLen = ExpandEnvironmentStringsW(String, NULL, 0);
|
|
if (dwLen > 0)
|
|
{
|
|
BOOL bSuccess = ExpandEnvironmentStringsW(String, Tmp.GetBuffer(dwLen), dwLen) == dwLen;
|
|
Tmp.ReleaseBuffer(dwLen - 1);
|
|
if (bSuccess)
|
|
{
|
|
String = Tmp;
|
|
}
|
|
else
|
|
{
|
|
String.Empty();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CInstalledApplicationInfo::GetApplicationRegDword(LPCWSTR lpKeyName, DWORD *lpValue)
|
|
{
|
|
DWORD dwSize = sizeof(DWORD), dwType;
|
|
if (RegQueryValueExW(m_hSubKey,
|
|
lpKeyName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)lpValue,
|
|
&dwSize) != ERROR_SUCCESS || dwType != REG_DWORD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CInstalledApplicationInfo::RetrieveIcon(ATL::CStringW& IconLocation)
|
|
{
|
|
if (szDisplayIcon.IsEmpty())
|
|
{
|
|
return FALSE;
|
|
}
|
|
IconLocation = szDisplayIcon;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CInstalledApplicationInfo::UninstallApplication(BOOL bModify)
|
|
{
|
|
if (!bModify)
|
|
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szDisplayName);
|
|
|
|
return StartProcess(bModify ? szModifyPath : szUninstallString, TRUE);
|
|
}
|
|
|
|
typedef LSTATUS (WINAPI *RegDeleteKeyExWProc)(HKEY, LPCWSTR, REGSAM, DWORD);
|
|
|
|
LSTATUS CInstalledApplicationInfo::RemoveFromRegistry()
|
|
{
|
|
ATL::CStringW szFullName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + m_szKeyName;
|
|
HMODULE hMod = GetModuleHandleW(L"advapi32.dll");
|
|
RegDeleteKeyExWProc pRegDeleteKeyExW;
|
|
|
|
// TODO: if there are subkeys inside, simply RegDeleteKeyExW
|
|
// (or RegDeleteKeyW on Server 2003 SP0 and earlier) will fail
|
|
// we don't have RegDeleteTree for ReactOS now. (It's a WinVista API)
|
|
// write a function to delete all subkeys recursively to solve this
|
|
// or consider letting ReactOS having this API
|
|
|
|
/* Load RegDeleteKeyExW from advapi32.dll if available */
|
|
if (hMod)
|
|
{
|
|
pRegDeleteKeyExW = (RegDeleteKeyExWProc)GetProcAddress(hMod, "RegDeleteKeyExW");
|
|
|
|
if (pRegDeleteKeyExW)
|
|
{
|
|
/* Return it */
|
|
return pRegDeleteKeyExW(m_IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName, m_WowKey, 0);
|
|
}
|
|
}
|
|
|
|
/* Otherwise, return non-Ex function */
|
|
return RegDeleteKeyW(m_IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName);
|
|
}
|
|
|
|
BOOL CInstalledApps::Enum(INT EnumType, APPENUMPROC lpEnumProc, PVOID param)
|
|
{
|
|
FreeCachedEntries();
|
|
|
|
HKEY RootKeyEnum[3] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE };
|
|
REGSAM RegSamEnum[3] = { KEY_WOW64_32KEY, KEY_WOW64_32KEY, KEY_WOW64_64KEY };
|
|
|
|
int LoopTime;
|
|
|
|
// test if the OS is 64 bit.
|
|
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
|
|
LoopTime = 3;
|
|
}
|
|
else
|
|
{
|
|
// loop for 2 combination for KEY_WOW64_32KEY only
|
|
LoopTime = 2;
|
|
}
|
|
|
|
// loop for all combination
|
|
for (int i = 0; i < LoopTime; i++)
|
|
{
|
|
DWORD dwSize = MAX_PATH;
|
|
HKEY hKey, hSubKey;
|
|
LONG ItemIndex = 0;
|
|
ATL::CStringW szKeyName;
|
|
|
|
if (RegOpenKeyExW(RootKeyEnum[i],
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
|
NULL,
|
|
KEY_READ | RegSamEnum[i],
|
|
&hKey) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
dwSize = MAX_PATH;
|
|
if (RegEnumKeyExW(hKey, ItemIndex, szKeyName.GetBuffer(MAX_PATH), &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ItemIndex++;
|
|
|
|
szKeyName.ReleaseBuffer();
|
|
if (RegOpenKeyW(hKey, szKeyName, &hSubKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwValue = 0;
|
|
BOOL bIsSystemComponent = FALSE;
|
|
|
|
dwSize = sizeof(DWORD);
|
|
if (RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
|
|
{
|
|
bIsSystemComponent = (dwValue == 0x1);
|
|
}
|
|
// Ignore system components
|
|
if (bIsSystemComponent)
|
|
{
|
|
RegCloseKey(hSubKey);
|
|
continue;
|
|
}
|
|
|
|
BOOL bSuccess = FALSE;
|
|
CInstalledApplicationInfo *Info = new CInstalledApplicationInfo(RootKeyEnum[i] == HKEY_CURRENT_USER, RegSamEnum[i], hSubKey, szKeyName);
|
|
|
|
// items without display name are ignored
|
|
if (Info->GetApplicationRegString(L"DisplayName", Info->szDisplayName))
|
|
{
|
|
Info->GetApplicationRegString(L"DisplayIcon", Info->szDisplayIcon);
|
|
Info->GetApplicationRegString(L"DisplayVersion", Info->szDisplayVersion);
|
|
Info->GetApplicationRegString(L"Comments", Info->szComments);
|
|
|
|
bSuccess = TRUE;
|
|
}
|
|
|
|
if (bSuccess)
|
|
{
|
|
// add to InfoList.
|
|
m_InfoList.AddTail(Info);
|
|
|
|
// invoke callback
|
|
if (lpEnumProc)
|
|
{
|
|
if ((EnumType == ENUM_ALL_INSTALLED) || /* All components */
|
|
((EnumType == ENUM_INSTALLED_APPLICATIONS) && (!Info->bIsUpdate)) || /* Applications only */
|
|
((EnumType == ENUM_UPDATES) && (Info->bIsUpdate))) /* Updates only */
|
|
{
|
|
lpEnumProc(Info, param);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// destory object
|
|
delete Info;
|
|
}
|
|
}
|
|
}
|
|
|
|
szKeyName.ReleaseBuffer();
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID CInstalledApps::FreeCachedEntries()
|
|
{
|
|
POSITION InfoListPosition = m_InfoList.GetHeadPosition();
|
|
|
|
/* loop and deallocate all the cached app infos in the list */
|
|
while (InfoListPosition)
|
|
{
|
|
CInstalledApplicationInfo *Info = m_InfoList.GetNext(InfoListPosition);
|
|
delete Info;
|
|
}
|
|
|
|
m_InfoList.RemoveAll();
|
|
}
|