//--------------------------------------------------------------------------- // // 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; }