2015-04-22 16:53:55 +00:00
|
|
|
/*
|
2017-09-09 20:38:06 +00:00
|
|
|
* PROJECT: ReactOS Applications Manager
|
2020-07-31 19:57:35 +00:00
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: Classes for working with installed applications
|
2021-10-03 18:35:13 +00:00
|
|
|
* 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>
|
2015-04-22 16:53:55 +00:00
|
|
|
*/
|
2017-08-02 12:50:53 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
#include "rapps.h"
|
2017-08-02 12:50:53 +00:00
|
|
|
#include "installed.h"
|
|
|
|
#include "misc.h"
|
2015-04-22 16:53:55 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
CInstalledApplicationInfo::CInstalledApplicationInfo(BOOL bIsUserKey, REGSAM RegWowKey, HKEY hKey, const CStringW& szKeyName)
|
|
|
|
: m_IsUserKey(bIsUserKey)
|
|
|
|
, m_WowKey(RegWowKey)
|
|
|
|
, m_hSubKey(hKey)
|
|
|
|
, m_szKeyName(szKeyName)
|
2017-07-10 21:02:24 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
DWORD dwSize = 0;
|
|
|
|
bIsUpdate = (RegQueryValueExW(m_hSubKey, L"ParentKeyName", NULL, NULL, NULL, &dwSize) == ERROR_SUCCESS);
|
|
|
|
}
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
CInstalledApplicationInfo::~CInstalledApplicationInfo()
|
|
|
|
{
|
|
|
|
if (m_hSubKey)
|
2020-07-21 14:13:39 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
CloseHandle(m_hSubKey);
|
|
|
|
m_hSubKey = NULL;
|
2020-07-21 14:13:39 +00:00
|
|
|
}
|
2021-10-03 18:35:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CInstalledApplicationInfo::EnsureDetailsLoaded()
|
|
|
|
{
|
|
|
|
// Key not closed, so we have not loaded details yet
|
|
|
|
if (m_hSubKey)
|
2020-07-21 14:13:39 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
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);
|
2022-05-24 09:20:52 +00:00
|
|
|
|
|
|
|
DWORD dwInstallTimeStamp;
|
|
|
|
SYSTEMTIME InstallLocalTime;
|
|
|
|
if (GetApplicationRegString(L"InstallDate", szInstallDate))
|
2021-10-03 18:35:13 +00:00
|
|
|
{
|
2022-05-24 09:20:52 +00:00
|
|
|
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)
|
2021-10-03 18:35:13 +00:00
|
|
|
{
|
2022-05-24 09:20:52 +00:00
|
|
|
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;
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2022-05-24 09:20:52 +00:00
|
|
|
UnixTimeToFileTime(dwInstallTimeStamp, &InstallFileTime);
|
|
|
|
FileTimeToSystemTime(&InstallFileTime, &InstallSystemTime);
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2022-05-24 09:20:52 +00:00
|
|
|
// convert to localtime
|
|
|
|
SystemTimeToTzSpecificLocalTime(NULL, &InstallSystemTime, &InstallLocalTime);
|
|
|
|
}
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2022-05-24 09:20:52 +00:00
|
|
|
// 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();
|
2017-07-10 21:02:24 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
GetApplicationRegString(L"InstallLocation", szInstallLocation);
|
|
|
|
GetApplicationRegString(L"InstallSource", szInstallSource);
|
2022-03-15 20:56:01 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2021-10-03 18:35:13 +00:00
|
|
|
|
|
|
|
CloseHandle(m_hSubKey);
|
|
|
|
m_hSubKey = NULL;
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-24 08:07:43 +00:00
|
|
|
BOOL CInstalledApplicationInfo::GetApplicationRegString(LPCWSTR lpKeyName, ATL::CStringW& String)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
DWORD dwAllocated = 0, dwSize, dwType;
|
2015-04-22 16:53:55 +00:00
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
// retrieve the size of value first.
|
2022-03-15 20:56:01 +00:00
|
|
|
if (RegQueryValueExW(m_hSubKey, lpKeyName, NULL, &dwType, NULL, &dwAllocated) != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
String.Empty();
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwType != REG_SZ && dwType != REG_EXPAND_SZ)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
String.Empty();
|
2015-04-22 16:53:55 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
// query the value
|
|
|
|
dwSize = dwAllocated;
|
|
|
|
LSTATUS Result =
|
|
|
|
RegQueryValueExW(m_hSubKey, lpKeyName, NULL, NULL, (LPBYTE)String.GetBuffer(dwAllocated / sizeof(WCHAR)), &dwSize);
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
dwSize = min(dwAllocated, dwSize);
|
|
|
|
// CString takes care of zero-terminating it
|
|
|
|
String.ReleaseBuffer(dwSize / sizeof(WCHAR));
|
2020-07-21 14:13:39 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
if (Result != ERROR_SUCCESS)
|
2020-07-21 14:13:39 +00:00
|
|
|
{
|
|
|
|
String.Empty();
|
|
|
|
return FALSE;
|
|
|
|
}
|
2021-10-03 18:35:13 +00:00
|
|
|
|
2022-03-15 20:56:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
return TRUE;
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
|
2020-07-24 08:07:43 +00:00
|
|
|
BOOL CInstalledApplicationInfo::GetApplicationRegDword(LPCWSTR lpKeyName, DWORD *lpValue)
|
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
DWORD dwSize = sizeof(DWORD), dwType;
|
|
|
|
if (RegQueryValueExW(m_hSubKey,
|
2020-07-24 08:07:43 +00:00
|
|
|
lpKeyName,
|
|
|
|
NULL,
|
|
|
|
&dwType,
|
|
|
|
(LPBYTE)lpValue,
|
2021-10-03 18:35:13 +00:00
|
|
|
&dwSize) != ERROR_SUCCESS || dwType != REG_DWORD)
|
2020-07-24 08:07:43 +00:00
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
[RAPPS] Display custom applications icons for installed applications CORE-17257 (#3144)
* [RAPPS] Display custom applications icons for installed applications
- Implement `RetrieveIcon` helper function in `CInstalledApplicationInfo` class, which retrueves the current app's icon from registry, same as it done for `CAvailableApplicationInfo`.
- Use it for loading the icon in `CAppsListView::AddInstalledApplication` function, via `ExtractIconW`. Load default Rapps icon only when the app has no its custom icon.
- Retrieve `DisplayIcon` value from registry in `CInstalledApps::Enum` function, same as other registry values (like app name, description, etc).Store it in `szDisplayIcon` string, which is used in `CInstalledApplicationInfo::RetrieveIcon` for retrieving the data of that value.
- Increase `LISTVIEW_ICON_SIZE` macro from 24 to 32, so 32x32 icon size is now used instead of 24x24. This makes displayed icons more accurate, since most of apps contain 32x32 icon, so they look a bit distorted with 24x24 size.
2020-09-20 17:19:59 +00:00
|
|
|
BOOL CInstalledApplicationInfo::RetrieveIcon(ATL::CStringW& IconLocation)
|
|
|
|
{
|
|
|
|
if (szDisplayIcon.IsEmpty())
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
IconLocation = szDisplayIcon;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
BOOL CInstalledApplicationInfo::UninstallApplication(BOOL bModify)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-10-25 20:19:06 +00:00
|
|
|
if (!bModify)
|
|
|
|
WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szDisplayName);
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
return StartProcess(bModify ? szModifyPath : szUninstallString, TRUE);
|
|
|
|
}
|
|
|
|
|
2020-09-16 14:54:20 +00:00
|
|
|
typedef LSTATUS (WINAPI *RegDeleteKeyExWProc)(HKEY, LPCWSTR, REGSAM, DWORD);
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
LSTATUS CInstalledApplicationInfo::RemoveFromRegistry()
|
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
ATL::CStringW szFullName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + m_szKeyName;
|
2020-09-16 14:54:20 +00:00
|
|
|
HMODULE hMod = GetModuleHandleW(L"advapi32.dll");
|
|
|
|
RegDeleteKeyExWProc pRegDeleteKeyExW;
|
2015-04-22 16:53:55 +00:00
|
|
|
|
2020-09-16 14:54:20 +00:00
|
|
|
// TODO: if there are subkeys inside, simply RegDeleteKeyExW
|
|
|
|
// (or RegDeleteKeyW on Server 2003 SP0 and earlier) will fail
|
2020-07-21 14:13:39 +00:00
|
|
|
// 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
|
2015-04-22 16:53:55 +00:00
|
|
|
|
2020-09-16 14:54:20 +00:00
|
|
|
/* Load RegDeleteKeyExW from advapi32.dll if available */
|
|
|
|
if (hMod)
|
|
|
|
{
|
|
|
|
pRegDeleteKeyExW = (RegDeleteKeyExWProc)GetProcAddress(hMod, "RegDeleteKeyExW");
|
|
|
|
|
|
|
|
if (pRegDeleteKeyExW)
|
|
|
|
{
|
|
|
|
/* Return it */
|
2021-10-03 18:35:13 +00:00
|
|
|
return pRegDeleteKeyExW(m_IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName, m_WowKey, 0);
|
2020-09-16 14:54:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, return non-Ex function */
|
2021-10-03 18:35:13 +00:00
|
|
|
return RegDeleteKeyW(m_IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName);
|
2020-07-21 14:13:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
// 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;
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
// loop for all combination
|
|
|
|
for (int i = 0; i < LoopTime; i++)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
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)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
dwSize = MAX_PATH;
|
|
|
|
if (RegEnumKeyExW(hKey, ItemIndex, szKeyName.GetBuffer(MAX_PATH), &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
break;
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
ItemIndex++;
|
|
|
|
|
|
|
|
szKeyName.ReleaseBuffer();
|
2021-10-03 18:35:13 +00:00
|
|
|
if (RegOpenKeyW(hKey, szKeyName, &hSubKey) == ERROR_SUCCESS)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
DWORD dwValue = 0;
|
|
|
|
BOOL bIsSystemComponent = FALSE;
|
2015-04-22 16:53:55 +00:00
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
dwSize = sizeof(DWORD);
|
|
|
|
if (RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
bIsSystemComponent = (dwValue == 0x1);
|
|
|
|
}
|
|
|
|
// Ignore system components
|
|
|
|
if (bIsSystemComponent)
|
|
|
|
{
|
|
|
|
RegCloseKey(hSubKey);
|
|
|
|
continue;
|
2020-07-21 14:13:39 +00:00
|
|
|
}
|
|
|
|
|
2021-10-03 18:35:13 +00:00
|
|
|
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))
|
2020-07-21 14:13:39 +00:00
|
|
|
{
|
2021-10-03 18:35:13 +00:00
|
|
|
Info->GetApplicationRegString(L"DisplayIcon", Info->szDisplayIcon);
|
|
|
|
Info->GetApplicationRegString(L"DisplayVersion", Info->szDisplayVersion);
|
|
|
|
Info->GetApplicationRegString(L"Comments", Info->szComments);
|
|
|
|
|
|
|
|
bSuccess = TRUE;
|
2020-07-21 14:13:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bSuccess)
|
|
|
|
{
|
|
|
|
// add to InfoList.
|
|
|
|
m_InfoList.AddTail(Info);
|
|
|
|
|
|
|
|
// invoke callback
|
|
|
|
if (lpEnumProc)
|
2015-04-22 16:53:55 +00:00
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-21 14:13:39 +00:00
|
|
|
// destory object
|
|
|
|
delete Info;
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 14:13:39 +00:00
|
|
|
szKeyName.ReleaseBuffer();
|
|
|
|
RegCloseKey(hKey);
|
2015-04-22 16:53:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2020-07-21 14:13:39 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|