You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
12 KiB
373 lines
12 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// File: instenum.cpp
|
|
//
|
|
// The current order of enumeration is Legacy --> Darwin --> SMS
|
|
//
|
|
// History:
|
|
// 1-18-97 by dli
|
|
//------------------------------------------------------------------------
|
|
#include "priv.h"
|
|
#include "instenum.h"
|
|
#include "instapp.h"
|
|
#include "sccls.h"
|
|
|
|
|
|
|
|
// constructor
|
|
CEnumInstalledApps::CEnumInstalledApps(void) : _cRef(1), _bEnumLegacy(TRUE), _dwCIA(-1) //_bEnumDarwin(FALSE)
|
|
{
|
|
DllAddRef();
|
|
|
|
TraceAddRef(CEnumInstalledApps, _cRef);
|
|
|
|
// Start off enumerating legacy apps, then switch to
|
|
// enumerating darwin apps.
|
|
ASSERT(_hkeyUninstall == NULL);
|
|
|
|
}
|
|
|
|
|
|
// destructor
|
|
CEnumInstalledApps::~CEnumInstalledApps()
|
|
{
|
|
if (_hkeyUninstall)
|
|
{
|
|
RegCloseKey(_hkeyUninstall);
|
|
_hkeyUninstall = NULL;
|
|
}
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
|
|
// IEnumInstalledApps::QueryInterface
|
|
HRESULT CEnumInstalledApps::QueryInterface(REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CEnumInstalledApps, IEnumInstalledApps), // IID_IEnumInstalledApps
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvOut);
|
|
}
|
|
|
|
// IUnknown::AddRef
|
|
ULONG CEnumInstalledApps::AddRef()
|
|
{
|
|
_cRef++;
|
|
TraceAddRef(CEnumInstalledApps, _cRef);
|
|
return _cRef;
|
|
}
|
|
|
|
// IUnknown::Release
|
|
ULONG CEnumInstalledApps::Release()
|
|
{
|
|
_cRef--;
|
|
TraceRelease(CEnumInstalledApps, _cRef);
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
#define REGSTR_VAL_UNINSTALLER_WINDOWSINSTALLER TEXT("WindowsInstaller")
|
|
#define REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT TEXT("SystemComponent")
|
|
|
|
|
|
HRESULT CEnumInstalledApps::_GetNextLegacyAppFromRegistry(IInstalledApp ** ppia)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
LONG lRet;
|
|
HKEY hkeySub = NULL;
|
|
TCHAR szKeyName[MAX_PATH];
|
|
DWORD dwType;
|
|
BOOL bTryAgain;
|
|
|
|
do
|
|
{
|
|
ULONG cchKeyName = ARRAYSIZE(szKeyName);
|
|
FILETIME ftLast;
|
|
|
|
bTryAgain = FALSE;
|
|
|
|
// Start enumerationg subkeys under _hkeyUninstall
|
|
if (RegEnumKeyEx(_hkeyUninstall, _iIndexEach, szKeyName, &cchKeyName, NULL,
|
|
NULL, NULL, &ftLast) == ERROR_SUCCESS)
|
|
{
|
|
_iIndexEach++;
|
|
|
|
// Open the key and get the subkey name
|
|
lRet = RegOpenKeyEx(_hkeyUninstall, szKeyName, 0, KEY_READ, &hkeySub);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szProduct[MAX_PATH];
|
|
|
|
// Don't enumerate system components
|
|
DWORD dwSysComponent = 0;
|
|
DWORD cbSysComponent = SIZEOF(dwSysComponent);
|
|
lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT, 0, &dwType,
|
|
(PBYTE)&dwSysComponent, &cbSysComponent);
|
|
if ((lRet != ERROR_SUCCESS) || (dwSysComponent != 1))
|
|
{
|
|
// Don't enumerate Darwin apps, who has WindowsInstaller set to 1
|
|
ULONG uDarwin;
|
|
ULONG cbDarwin = SIZEOF(uDarwin);
|
|
lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_WINDOWSINSTALLER, 0, &dwType,
|
|
(PBYTE)&uDarwin, &cbDarwin);
|
|
if ((lRet != ERROR_SUCCESS) || (uDarwin != 1))
|
|
{
|
|
|
|
// Get the DisplayName value
|
|
ULONG cbProductName = SIZEOF(szProduct);
|
|
lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, &dwType,
|
|
(PBYTE)szProduct, &cbProductName);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szUninstall[MAX_INFO_STRING];
|
|
|
|
// we proceed even if the below SHQueryValueEx fails, so we need
|
|
// to zero initialize
|
|
szUninstall[0] = 0;
|
|
|
|
// Get the uninstaller string
|
|
ULONG cbUninstall = SIZEOF(szUninstall);
|
|
lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, &dwType, (PBYTE)szUninstall, &cbUninstall);
|
|
|
|
// NOTE: We don't create CInstalledApp Object if there is no "Uninstall" key
|
|
// should we just delete this from registry?
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
// Create new CInstalledApp Object
|
|
CInstalledApp * pia = new CInstalledApp(hkeySub, szKeyName, szProduct, szUninstall, _dwCIA);
|
|
if (pia)
|
|
{
|
|
*ppia = SAFECAST(pia, IInstalledApp *);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
break; // We found an app, return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// In failure cases, go to the next one and try again
|
|
RegCloseKey(hkeySub);
|
|
bTryAgain = TRUE;
|
|
continue;
|
|
|
|
// (hkeySub is owned and closed by the CInstalledApp object)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey(_hkeyUninstall);
|
|
_hkeyUninstall = NULL;
|
|
}
|
|
} while (bTryAgain);
|
|
|
|
return hres;
|
|
}
|
|
|
|
typedef struct LEGACYAPPREGKEY {
|
|
HKEY hkRoot;
|
|
LPCTSTR pszSubkey;
|
|
} LEGACYAPPREGKEY;
|
|
|
|
const LEGACYAPPREGKEY c_rgLegacy[] = {
|
|
{ HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL }, // CIA_LM_NATIVE
|
|
{ HKEY_CURRENT_USER, REGSTR_PATH_UNINSTALL }, // CIA_CU_NATIVE
|
|
#ifdef _WIN64
|
|
{ HKEY_LOCAL_MACHINE, REGSTR_PATH_ALTUNINSTALL }, // CIA_LM_ALT
|
|
{ HKEY_CURRENT_USER, REGSTR_PATH_ALTUNINSTALL }, // CIA_CU_ALT
|
|
#endif
|
|
};
|
|
|
|
// Gets the next legacy app from the registry "uninstall" key
|
|
|
|
HRESULT CEnumInstalledApps::_GetNextLegacyApp(IInstalledApp ** ppia)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
restart:
|
|
// If we don't have an active enumeration key, then try to make a new one
|
|
while (_hkeyUninstall == NULL && ++_dwCIA < ARRAYSIZE(c_rgLegacy))
|
|
{
|
|
_iIndexEach = 0; // restart the RegEnumKey
|
|
RegOpenKeyEx(c_rgLegacy[_dwCIA].hkRoot,
|
|
c_rgLegacy[_dwCIA].pszSubkey,
|
|
0, KEY_READ, &_hkeyUninstall);
|
|
}
|
|
|
|
if (_hkeyUninstall)
|
|
{
|
|
// Enumerate the next one
|
|
hres = _GetNextLegacyAppFromRegistry(ppia);
|
|
|
|
if (hres == S_FALSE)
|
|
{
|
|
// No more from that key, try another one
|
|
// (_GetNextLegacyAppFromRegistry sets _hkeyUninstall = NULL when it returns S_FALSE)
|
|
goto restart;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CEnumInstalledApps::_GetNextDarwinApp(IInstalledApp ** ppia)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
TCHAR szProductID[GUIDSTR_MAX];
|
|
|
|
BOOL bContinue;
|
|
do
|
|
{
|
|
bContinue = FALSE;
|
|
|
|
UINT uRet = TW32(MsiEnumProducts(_iIndexEach, szProductID));
|
|
if (uRet == ERROR_SUCCESS)
|
|
{
|
|
BOOL bTake = TRUE; // Do we want to show this app, default to yes.
|
|
_iIndexEach++; // increment the counter
|
|
|
|
HKEY hkeySub = NULL;
|
|
DWORD dwType;
|
|
TCHAR szRegKey[MAX_PATH];
|
|
StringCchPrintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), REGSTR_PATH_UNINSTALL, szProductID);
|
|
|
|
// Open this key in the registry
|
|
uRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_READ, &hkeySub);
|
|
if (uRet == ERROR_SUCCESS)
|
|
{
|
|
// Don't enumerate system components
|
|
DWORD dwSysComponent = 0;
|
|
DWORD cbSysComponent = SIZEOF(dwSysComponent);
|
|
uRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT, 0, &dwType,
|
|
(PBYTE)&dwSysComponent, &cbSysComponent);
|
|
if ((uRet == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwSysComponent == 1))
|
|
bTake = FALSE;
|
|
|
|
RegCloseKey(hkeySub);
|
|
}
|
|
|
|
if (bTake)
|
|
{
|
|
INSTALLSTATE is = MsiQueryProductState(szProductID);
|
|
|
|
if ((is != INSTALLSTATE_DEFAULT) && (is != INSTALLSTATE_ADVERTISED))
|
|
bTake = FALSE;
|
|
|
|
// NOTE: INSTALLSTATE_ADVERTISED means assigned apps
|
|
if (bTake)
|
|
{
|
|
CInstalledApp * pia = new CInstalledApp(szProductID);
|
|
if (pia)
|
|
{
|
|
*ppia = SAFECAST(pia, IInstalledApp *);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
bContinue = TRUE;
|
|
}
|
|
else
|
|
{
|
|
switch(uRet)
|
|
{
|
|
//
|
|
// MsiEnumProducts can return ERROR_CALL_NOT_IMPLEMENTED
|
|
// on embedded SKU. Act as if enumeration is complete.
|
|
//
|
|
case ERROR_CALL_NOT_IMPLEMENTED:
|
|
case ERROR_NO_MORE_ITEMS:
|
|
//
|
|
// Enumeration is complete.
|
|
//
|
|
break;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
hres = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Some error other than "access denied" occured.
|
|
// Continue enumerating products.
|
|
//
|
|
_iIndexEach++;
|
|
bContinue = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} while (bContinue);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// IEnumInstalledApps::Next
|
|
// We allow only one app at a time.
|
|
STDMETHODIMP CEnumInstalledApps::Next(IInstalledApp ** ppia)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
if (_bEnumLegacy)
|
|
{
|
|
hres = _GetNextLegacyApp(ppia);
|
|
if (hres == S_FALSE)
|
|
{
|
|
// End of the enumeration for legacy apps
|
|
_bEnumLegacy = FALSE;
|
|
_iIndexEach = 0;
|
|
goto EnumDarwinNow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EnumDarwinNow:
|
|
hres = _GetNextDarwinApp(ppia);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
// IEnumInstalledApps::Reset
|
|
STDMETHODIMP CEnumInstalledApps::Reset(void)
|
|
{
|
|
// Start off enumerating legacy apps, then switch to
|
|
// enumerating darwin apps.
|
|
_bEnumLegacy = TRUE;
|
|
_dwCIA = -1;
|
|
_iIndexEach = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Create-instance function for class factory
|
|
|
|
*/
|
|
STDAPI CEnumInstalledApps_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
// aggregation checking is handled in class factory
|
|
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
CEnumInstalledApps * pObj = new CEnumInstalledApps();
|
|
if (pObj)
|
|
{
|
|
*ppunk = SAFECAST(pObj, IEnumInstalledApps *);
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|