Source code of Windows XP (NT5)
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.

368 lines
11 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // File: instenum.cpp
  6. //
  7. // The current order of enumeration is Legacy --> Darwin --> SMS
  8. //
  9. // History:
  10. // 1-18-97 by dli
  11. //------------------------------------------------------------------------
  12. #include "priv.h"
  13. #include "instenum.h"
  14. #include "instapp.h"
  15. #include "sccls.h"
  16. // constructor
  17. CEnumInstalledApps::CEnumInstalledApps(void) : _cRef(1), _bEnumLegacy(TRUE), _dwCIA(-1) //_bEnumDarwin(FALSE)
  18. {
  19. DllAddRef();
  20. TraceAddRef(CEnumInstalledApps, _cRef);
  21. // Start off enumerating legacy apps, then switch to
  22. // enumerating darwin apps.
  23. ASSERT(_hkeyUninstall == NULL);
  24. }
  25. // destructor
  26. CEnumInstalledApps::~CEnumInstalledApps()
  27. {
  28. if (_hkeyUninstall)
  29. {
  30. RegCloseKey(_hkeyUninstall);
  31. _hkeyUninstall = NULL;
  32. }
  33. DllRelease();
  34. }
  35. // IEnumInstalledApps::QueryInterface
  36. HRESULT CEnumInstalledApps::QueryInterface(REFIID riid, LPVOID * ppvOut)
  37. {
  38. static const QITAB qit[] = {
  39. QITABENT(CEnumInstalledApps, IEnumInstalledApps), // IID_IEnumInstalledApps
  40. { 0 },
  41. };
  42. return QISearch(this, qit, riid, ppvOut);
  43. }
  44. // IUnknown::AddRef
  45. ULONG CEnumInstalledApps::AddRef()
  46. {
  47. _cRef++;
  48. TraceAddRef(CEnumInstalledApps, _cRef);
  49. return _cRef;
  50. }
  51. // IUnknown::Release
  52. ULONG CEnumInstalledApps::Release()
  53. {
  54. _cRef--;
  55. TraceRelease(CEnumInstalledApps, _cRef);
  56. if (_cRef > 0)
  57. return _cRef;
  58. delete this;
  59. return 0;
  60. }
  61. #define REGSTR_VAL_UNINSTALLER_WINDOWSINSTALLER TEXT("WindowsInstaller")
  62. #define REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT TEXT("SystemComponent")
  63. HRESULT CEnumInstalledApps::_GetNextLegacyAppFromRegistry(IInstalledApp ** ppia)
  64. {
  65. HRESULT hres = S_FALSE;
  66. LONG lRet;
  67. HKEY hkeySub = NULL;
  68. TCHAR szKeyName[MAX_PATH];
  69. DWORD dwType;
  70. BOOL bTryAgain;
  71. do
  72. {
  73. ULONG cchKeyName = ARRAYSIZE(szKeyName);
  74. FILETIME ftLast;
  75. bTryAgain = FALSE;
  76. // Start enumerationg subkeys under _hkeyUninstall
  77. if (RegEnumKeyEx(_hkeyUninstall, _iIndexEach, szKeyName, &cchKeyName, NULL,
  78. NULL, NULL, &ftLast) == ERROR_SUCCESS)
  79. {
  80. _iIndexEach++;
  81. // Open the key and get the subkey name
  82. lRet = RegOpenKeyEx(_hkeyUninstall, szKeyName, 0, KEY_READ, &hkeySub);
  83. if (lRet == ERROR_SUCCESS)
  84. {
  85. TCHAR szProduct[MAX_PATH];
  86. // Don't enumerate system components
  87. DWORD dwSysComponent = 0;
  88. DWORD cbSysComponent = SIZEOF(dwSysComponent);
  89. lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT, 0, &dwType,
  90. (PBYTE)&dwSysComponent, &cbSysComponent);
  91. if ((lRet != ERROR_SUCCESS) || (dwSysComponent != 1))
  92. {
  93. // Don't enumerate Darwin apps, who has WindowsInstaller set to 1
  94. ULONG uDarwin;
  95. ULONG cbDarwin = SIZEOF(uDarwin);
  96. lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_WINDOWSINSTALLER, 0, &dwType,
  97. (PBYTE)&uDarwin, &cbDarwin);
  98. if ((lRet != ERROR_SUCCESS) || (uDarwin != 1))
  99. {
  100. // Get the DisplayName value
  101. ULONG cbProductName = SIZEOF(szProduct);
  102. lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, &dwType,
  103. (PBYTE)szProduct, &cbProductName);
  104. if (lRet == ERROR_SUCCESS)
  105. {
  106. TCHAR szUninstall[MAX_INFO_STRING];
  107. // we proceed even if the below SHQueryValueEx fails, so we need
  108. // to zero initialize
  109. szUninstall[0] = 0;
  110. // Get the uninstaller string
  111. ULONG cbUninstall = SIZEOF(szUninstall);
  112. lRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, &dwType, (PBYTE)szUninstall, &cbUninstall);
  113. // NOTE: We don't create CInstalledApp Object if there is no "Uninstall" key
  114. // should we just delete this from registry?
  115. if (lRet == ERROR_SUCCESS)
  116. {
  117. // Create new CInstalledApp Object
  118. CInstalledApp * pia = new CInstalledApp(hkeySub, szKeyName, szProduct, szUninstall, _dwCIA);
  119. if (pia)
  120. {
  121. *ppia = SAFECAST(pia, IInstalledApp *);
  122. hres = S_OK;
  123. }
  124. else
  125. hres = E_OUTOFMEMORY;
  126. break; // We found an app, return
  127. }
  128. }
  129. }
  130. }
  131. // In failure cases, go to the next one and try again
  132. RegCloseKey(hkeySub);
  133. bTryAgain = TRUE;
  134. continue;
  135. // (hkeySub is owned and closed by the CInstalledApp object)
  136. }
  137. }
  138. else
  139. {
  140. RegCloseKey(_hkeyUninstall);
  141. _hkeyUninstall = NULL;
  142. }
  143. } while (bTryAgain);
  144. return hres;
  145. }
  146. typedef struct LEGACYAPPREGKEY {
  147. HKEY hkRoot;
  148. LPCTSTR pszSubkey;
  149. } LEGACYAPPREGKEY;
  150. const LEGACYAPPREGKEY c_rgLegacy[] = {
  151. { HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL }, // CIA_LM_NATIVE
  152. { HKEY_CURRENT_USER, REGSTR_PATH_UNINSTALL }, // CIA_CU_NATIVE
  153. #ifdef _WIN64
  154. { HKEY_LOCAL_MACHINE, REGSTR_PATH_ALTUNINSTALL }, // CIA_LM_ALT
  155. { HKEY_CURRENT_USER, REGSTR_PATH_ALTUNINSTALL }, // CIA_CU_ALT
  156. #endif
  157. };
  158. // Gets the next legacy app from the registry "uninstall" key
  159. HRESULT CEnumInstalledApps::_GetNextLegacyApp(IInstalledApp ** ppia)
  160. {
  161. HRESULT hres = S_FALSE;
  162. restart:
  163. // If we don't have an active enumeration key, then try to make a new one
  164. while (_hkeyUninstall == NULL && ++_dwCIA < ARRAYSIZE(c_rgLegacy))
  165. {
  166. _iIndexEach = 0; // restart the RegEnumKey
  167. RegOpenKeyEx(c_rgLegacy[_dwCIA].hkRoot,
  168. c_rgLegacy[_dwCIA].pszSubkey,
  169. 0, KEY_READ, &_hkeyUninstall);
  170. }
  171. if (_hkeyUninstall)
  172. {
  173. // Enumerate the next one
  174. hres = _GetNextLegacyAppFromRegistry(ppia);
  175. if (hres == S_FALSE)
  176. {
  177. // No more from that key, try another one
  178. // (_GetNextLegacyAppFromRegistry sets _hkeyUninstall = NULL when it returns S_FALSE)
  179. goto restart;
  180. }
  181. }
  182. return hres;
  183. }
  184. HRESULT CEnumInstalledApps::_GetNextDarwinApp(IInstalledApp ** ppia)
  185. {
  186. HRESULT hres = S_FALSE;
  187. TCHAR szProductID[GUIDSTR_MAX];
  188. BOOL bContinue;
  189. do
  190. {
  191. bContinue = FALSE;
  192. UINT uRet = TW32(MsiEnumProducts(_iIndexEach, szProductID));
  193. if (uRet == ERROR_SUCCESS)
  194. {
  195. BOOL bTake = TRUE; // Do we want to show this app, default to yes.
  196. _iIndexEach++; // increment the counter
  197. HKEY hkeySub = NULL;
  198. DWORD dwType;
  199. TCHAR szRegKey[MAX_PATH];
  200. wnsprintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), REGSTR_PATH_UNINSTALL, szProductID);
  201. // Open this key in the registry
  202. uRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_READ, &hkeySub);
  203. if (uRet == ERROR_SUCCESS)
  204. {
  205. // Don't enumerate system components
  206. DWORD dwSysComponent = 0;
  207. DWORD cbSysComponent = SIZEOF(dwSysComponent);
  208. uRet = SHQueryValueEx(hkeySub, REGSTR_VAL_UNINSTALLER_SYSTEMCOMPONENT, 0, &dwType,
  209. (PBYTE)&dwSysComponent, &cbSysComponent);
  210. if ((uRet == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwSysComponent == 1))
  211. bTake = FALSE;
  212. RegCloseKey(hkeySub);
  213. }
  214. if (bTake)
  215. {
  216. INSTALLSTATE is = MsiQueryProductState(szProductID);
  217. if ((is != INSTALLSTATE_DEFAULT) && (is != INSTALLSTATE_ADVERTISED))
  218. bTake = FALSE;
  219. // NOTE: INSTALLSTATE_ADVERTISED means assigned apps
  220. if (bTake)
  221. {
  222. CInstalledApp * pia = new CInstalledApp(szProductID);
  223. if (pia)
  224. {
  225. *ppia = SAFECAST(pia, IInstalledApp *);
  226. hres = S_OK;
  227. }
  228. else
  229. hres = E_OUTOFMEMORY;
  230. break;
  231. }
  232. }
  233. bContinue = TRUE;
  234. }
  235. else
  236. {
  237. switch(uRet)
  238. {
  239. case ERROR_NO_MORE_ITEMS:
  240. //
  241. // Enumeration is complete.
  242. //
  243. break;
  244. case ERROR_ACCESS_DENIED:
  245. hres = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
  246. break;
  247. default:
  248. //
  249. // Some error other than "access denied" occured.
  250. // Continue enumerating products.
  251. //
  252. _iIndexEach++;
  253. bContinue = TRUE;
  254. break;
  255. }
  256. }
  257. } while (bContinue);
  258. return hres;
  259. }
  260. // IEnumInstalledApps::Next
  261. // We allow only one app at a time.
  262. STDMETHODIMP CEnumInstalledApps::Next(IInstalledApp ** ppia)
  263. {
  264. HRESULT hres = S_FALSE;
  265. if (_bEnumLegacy)
  266. {
  267. hres = _GetNextLegacyApp(ppia);
  268. if (hres == S_FALSE)
  269. {
  270. // End of the enumeration for legacy apps
  271. _bEnumLegacy = FALSE;
  272. _iIndexEach = 0;
  273. goto EnumDarwinNow;
  274. }
  275. }
  276. else
  277. {
  278. EnumDarwinNow:
  279. hres = _GetNextDarwinApp(ppia);
  280. }
  281. return hres;
  282. }
  283. // IEnumInstalledApps::Reset
  284. STDMETHODIMP CEnumInstalledApps::Reset(void)
  285. {
  286. // Start off enumerating legacy apps, then switch to
  287. // enumerating darwin apps.
  288. _bEnumLegacy = TRUE;
  289. _dwCIA = -1;
  290. _iIndexEach = 0;
  291. return S_OK;
  292. }
  293. /*----------------------------------------------------------
  294. Purpose: Create-instance function for class factory
  295. */
  296. STDAPI CEnumInstalledApps_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  297. {
  298. // aggregation checking is handled in class factory
  299. HRESULT hres = E_OUTOFMEMORY;
  300. CEnumInstalledApps * pObj = new CEnumInstalledApps();
  301. if (pObj)
  302. {
  303. *ppunk = SAFECAST(pObj, IEnumInstalledApps *);
  304. hres = S_OK;
  305. }
  306. return hres;
  307. }