reactos/base/applications/rapps/installed.cpp
Marcus Boillat 4900dd3d87
[RAPPS] Improve localized display of installation date (#4498)
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>
2022-05-24 12:20:52 +03:00

372 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();
}