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.

579 lines
16 KiB

  1. // Appmgr.cpp : Implementation of CShellAppManager
  2. #include "priv.h"
  3. #include "appmgr.h"
  4. #include "instenum.h"
  5. #include "util.h"
  6. #ifndef DOWNLEVEL_PLATFORM
  7. #include "pubenum.h"
  8. #endif //DOWNLEVEL_PLATFORM
  9. #include "sccls.h"
  10. #ifndef DOWNLEVEL_PLATFORM
  11. #pragma message("DOWNLEVEL_PLATFORM is NOT defined")
  12. #endif //DOWNLEVEL_PLATFORM
  13. const TCHAR c_szTSMsiHackKey[] = TEXT("Software\\Policies\\Microsoft\\Windows\\Installer\\Terminal Server");
  14. const TCHAR c_szTSMsiHackValue[] = TEXT("EnableAdminRemote");
  15. /////////////////////////////////////////////////////////////////////////////
  16. // CShellAppManager
  17. // constructor
  18. CShellAppManager::CShellAppManager() : _cRef(1)
  19. {
  20. DllAddRef();
  21. TraceAddRef(CShellAppManager, _cRef);
  22. ASSERT(_hdpaPub == NULL);
  23. #ifndef DOWNLEVEL_PLATFORM
  24. InitializeCriticalSection(&_cs);
  25. HDCA hdca = DCA_Create();
  26. if (hdca)
  27. {
  28. // Enumerate all of the Application Publishers
  29. DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, REGSTR_PATH_APPPUBLISHER);
  30. if (DCA_GetItemCount(hdca) > 0)
  31. {
  32. _Lock();
  33. // Create our internal list of IAppPublisher *
  34. _hdpaPub = DPA_Create(4);
  35. if(_hdpaPub)
  36. {
  37. int idca;
  38. for (idca = 0; idca < DCA_GetItemCount(hdca); idca++)
  39. {
  40. IAppPublisher * pap;
  41. if (FAILED(DCA_CreateInstance(hdca, idca, IID_IAppPublisher, (LPVOID *) &pap)))
  42. continue;
  43. ASSERT(IS_VALID_CODE_PTR(pap, IAppPublisher));
  44. if (DPA_AppendPtr(_hdpaPub, pap) == DPA_ERR)
  45. {
  46. pap->Release();
  47. break;
  48. }
  49. }
  50. // if we have no pointers in this array, don't bother create one
  51. if (DPA_GetPtrCount(_hdpaPub) == 0)
  52. {
  53. DPA_Destroy(_hdpaPub);
  54. _hdpaPub = NULL;
  55. }
  56. }
  57. _Unlock();
  58. }
  59. DCA_Destroy(hdca);
  60. }
  61. if (IsTerminalServicesRunning())
  62. {
  63. // Hack for MSI work on Terminal Server
  64. HKEY hkeyMsiHack = NULL;
  65. DWORD lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyMsiHack);
  66. if (lRet == ERROR_FILE_NOT_FOUND)
  67. {
  68. lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE,
  69. NULL, &hkeyMsiHack, NULL);
  70. }
  71. if (lRet == ERROR_SUCCESS)
  72. {
  73. DWORD dwType = 0;
  74. DWORD dwTSMsiHack = 0;
  75. DWORD cbSize = SIZEOF(dwTSMsiHack);
  76. if ((ERROR_SUCCESS != RegQueryValueEx(hkeyMsiHack, c_szTSMsiHackValue, 0, &dwType, (LPBYTE)&dwTSMsiHack, &cbSize))
  77. || (dwType != REG_DWORD) || (dwTSMsiHack != 1))
  78. {
  79. dwTSMsiHack = 1;
  80. if (RegSetValueEx(hkeyMsiHack, c_szTSMsiHackValue, 0, REG_DWORD, (LPBYTE)&dwTSMsiHack, SIZEOF(dwTSMsiHack)) == ERROR_SUCCESS)
  81. _bCreatedTSMsiHack = TRUE;
  82. }
  83. RegCloseKey(hkeyMsiHack);
  84. }
  85. }
  86. #endif //DOWNLEVEL_PLATFORM
  87. }
  88. // destructor
  89. CShellAppManager::~CShellAppManager()
  90. {
  91. if (_bCreatedTSMsiHack)
  92. {
  93. ASSERT(IsTerminalServicesRunning());
  94. HKEY hkeyMsiHack;
  95. DWORD lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, KEY_SET_VALUE, &hkeyMsiHack);
  96. if (ERROR_SUCCESS == lRet)
  97. {
  98. RegDeleteValue(hkeyMsiHack, c_szTSMsiHackValue);
  99. RegCloseKey(hkeyMsiHack);
  100. }
  101. }
  102. _Lock();
  103. // The order below is important
  104. if (_hdsaCategoryList)
  105. _DestroyInternalCategoryList();
  106. if (_hdpaPub)
  107. _DestroyAppPublisherList();
  108. _Unlock();
  109. DeleteCriticalSection(&_cs);
  110. DllRelease();
  111. }
  112. // Recursively destroys a GUIDLIST
  113. void CShellAppManager::_DestroyGuidList(GUIDLIST * pGuidList)
  114. {
  115. ASSERT(IS_VALID_WRITE_PTR(pGuidList, GUIDLIST));
  116. if (pGuidList->pNextGuid)
  117. _DestroyGuidList(pGuidList->pNextGuid);
  118. LocalFree(pGuidList);
  119. }
  120. void CShellAppManager::_DestroyCategoryItem(CATEGORYITEM * pci)
  121. {
  122. ASSERT(IS_VALID_WRITE_PTR(pci, CATEGORYITEM));
  123. if (pci->pszDescription)
  124. LocalFree(pci->pszDescription);
  125. if (pci->pGuidList)
  126. _DestroyGuidList(pci->pGuidList);
  127. }
  128. // Destroys our Category List Table
  129. void CShellAppManager::_DestroyInternalCategoryList()
  130. {
  131. // The caller must enter the lock first
  132. ASSERT(0 < _cRefLock);
  133. ASSERT(IS_VALID_HANDLE(_hdsaCategoryList, DSA));
  134. int idsa;
  135. for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
  136. {
  137. CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
  138. if (pci)
  139. _DestroyCategoryItem(pci);
  140. }
  141. DSA_Destroy(_hdsaCategoryList);
  142. }
  143. // Destroys our list of IAppPublisher *
  144. void CShellAppManager::_DestroyAppPublisherList()
  145. {
  146. // The caller must enter the lock first
  147. ASSERT(0 < _cRefLock);
  148. ASSERT(IS_VALID_HANDLE(_hdpaPub, DPA));
  149. int idpa;
  150. for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
  151. {
  152. IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
  153. if (EVAL(pap))
  154. pap->Release();
  155. }
  156. DPA_Destroy(_hdpaPub);
  157. _hdpaPub = NULL;
  158. }
  159. // IShellAppManager::QueryInterface
  160. HRESULT CShellAppManager::QueryInterface(REFIID riid, LPVOID * ppvOut)
  161. {
  162. static const QITAB qit[] = {
  163. QITABENT(CShellAppManager, IShellAppManager), // IID_IShellAppManager
  164. { 0 },
  165. };
  166. return QISearch(this, qit, riid, ppvOut);
  167. }
  168. // IShellAppManager::AddRef
  169. ULONG CShellAppManager::AddRef()
  170. {
  171. InterlockedIncrement(&_cRef);
  172. TraceAddRef(CShellAppManager, _cRef);
  173. return _cRef;
  174. }
  175. // IShellAppManager::Release
  176. ULONG CShellAppManager::Release()
  177. {
  178. InterlockedDecrement(&_cRef);
  179. TraceRelease(CShellAppManager, _cRef);
  180. if (_cRef > 0)
  181. return _cRef;
  182. delete this;
  183. return 0;
  184. }
  185. void CShellAppManager::_Lock(void)
  186. {
  187. EnterCriticalSection(&_cs);
  188. DEBUG_CODE( _cRefLock++; )
  189. }
  190. void CShellAppManager::_Unlock(void)
  191. {
  192. DEBUG_CODE( _cRefLock--; )
  193. LeaveCriticalSection(&_cs);
  194. }
  195. STDMETHODIMP CShellAppManager::GetNumberofInstalledApps(DWORD * pdwResult)
  196. {
  197. return E_NOTIMPL;
  198. }
  199. STDMETHODIMP CShellAppManager::EnumInstalledApps(IEnumInstalledApps ** ppeia)
  200. {
  201. HRESULT hres = E_FAIL;
  202. CEnumInstalledApps * peia = new CEnumInstalledApps();
  203. if (peia)
  204. {
  205. *ppeia = SAFECAST(peia, IEnumInstalledApps *);
  206. hres = S_OK;
  207. }
  208. else
  209. hres = E_OUTOFMEMORY;
  210. return hres;
  211. }
  212. #ifndef DOWNLEVEL_PLATFORM
  213. HRESULT CShellAppManager::_AddCategoryToList(APPCATEGORYINFO * pai, IAppPublisher * pap)
  214. {
  215. // The caller must enter the lock first
  216. ASSERT(0 < _cRefLock);
  217. if (pai == NULL || _hdsaCategoryList == NULL)
  218. return E_FAIL;
  219. ASSERT(IS_VALID_CODE_PTR(pap, IAppPublisher));
  220. // Allocate a GUIDLIST item first
  221. GUIDLIST * pgl = (GUIDLIST *)LocalAlloc(LPTR, SIZEOF(GUIDLIST));
  222. if (!pgl)
  223. return E_OUTOFMEMORY;
  224. pgl->CatGuid = pai->AppCategoryId;
  225. pgl->papSupport = pap;
  226. // Search in the CategoryList
  227. int idsa;
  228. for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
  229. {
  230. CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
  231. if (pci)
  232. {
  233. // If we find an empty spot, fill it
  234. if (pci->pszDescription == NULL)
  235. {
  236. CATEGORYITEM ci = {0};
  237. ci.pszDescription = StrDupW(pai->pszDescription);
  238. ci.pGuidList = pgl;
  239. pgl->pNextGuid = NULL;
  240. if (DSA_InsertItem(_hdsaCategoryList, idsa, &ci) == -1)
  241. {
  242. LocalFree(ci.pszDescription);
  243. break;
  244. }
  245. }
  246. // If we find an entry with the same description text, add our GUID to the GuidList
  247. else if(!lstrcmpi(pai->pszDescription, pci->pszDescription))
  248. {
  249. pgl->pNextGuid = pci->pGuidList;
  250. pci->pGuidList = pgl;
  251. break;
  252. }
  253. }
  254. else
  255. ASSERT(0);
  256. }
  257. // We ran out of items in the list, and didn't run into an identical category string
  258. if (idsa == DSA_GetItemCount(_hdsaCategoryList))
  259. {
  260. CATEGORYITEM ci = {0};
  261. ci.pszDescription = StrDupW(pai->pszDescription);
  262. ci.pGuidList = pgl;
  263. pgl->pNextGuid = NULL;
  264. if (DSA_AppendItem(_hdsaCategoryList, &ci) == -1)
  265. LocalFree(ci.pszDescription);
  266. }
  267. return S_OK;
  268. }
  269. #endif //DOWNLEVEL_PLATFORM
  270. #ifndef DOWNLEVEL_PLATFORM
  271. HRESULT CShellAppManager::_BuildInternalCategoryList()
  272. {
  273. HRESULT hres = E_OUTOFMEMORY;
  274. // The caller must enter the lock first
  275. ASSERT(0 < _cRefLock);
  276. ASSERT(IsValidHDPA(_hdpaPub));
  277. // We have just one valid version of _hdsaCategoryList, so we should never call this function
  278. // again once _hdsaCategoryList is created.
  279. ASSERT(_hdsaCategoryList == NULL);
  280. // Is the structure automatically filled with zero?
  281. _hdsaCategoryList = DSA_Create(SIZEOF(CATEGORYITEM), CATEGORYLIST_GROW);
  282. if (_hdsaCategoryList)
  283. {
  284. int idpa;
  285. for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
  286. {
  287. IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
  288. ASSERT(pap);
  289. if (pap)
  290. {
  291. UINT i;
  292. APPCATEGORYINFOLIST AppCatList;
  293. if (SUCCEEDED(pap->GetCategories(&AppCatList)))
  294. {
  295. if ((AppCatList.cCategory > 0) && AppCatList.pCategoryInfo)
  296. {
  297. for (i = 0; i < AppCatList.cCategory; i++)
  298. _AddCategoryToList(&AppCatList.pCategoryInfo[i], pap);
  299. _DestroyCategoryList(&AppCatList);
  300. }
  301. }
  302. }
  303. hres = S_OK;
  304. }
  305. }
  306. return hres;
  307. }
  308. #endif //DOWNLEVEL_PLATFORM
  309. #ifndef DOWNLEVEL_PLATFORM
  310. // Compile a multi string of categories and return it to the caller
  311. HRESULT CShellAppManager::_CompileCategoryList(PSHELLAPPCATEGORYLIST psacl)
  312. {
  313. HRESULT hres = E_FAIL;
  314. ASSERT(IS_VALID_READ_PTR(psacl, SHELLAPPCATEGORYLIST));
  315. // Don't do anything if we have an empty list
  316. if (_hdsaCategoryList && (DSA_GetItemCount(_hdsaCategoryList) > 0))
  317. {
  318. psacl->pCategory = (PSHELLAPPCATEGORY) SHAlloc(DSA_GetItemCount(_hdsaCategoryList) * SIZEOF(SHELLAPPCATEGORY));
  319. if (psacl->pCategory)
  320. {
  321. int idsa;
  322. for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
  323. {
  324. CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
  325. if (pci && pci->pszDescription)
  326. {
  327. if (SUCCEEDED(SHStrDup(pci->pszDescription, &psacl->pCategory[idsa].pszCategory)))
  328. {
  329. ASSERT(IS_VALID_STRING_PTR(psacl->pCategory[idsa].pszCategory, -1));
  330. psacl->cCategories++;
  331. }
  332. else
  333. break;
  334. }
  335. }
  336. hres = S_OK;
  337. }
  338. else
  339. hres = E_OUTOFMEMORY;
  340. }
  341. return hres;
  342. }
  343. #endif //DOWNLEVEL_PLATFORM
  344. // IShellAppManager::GetPublishedAppCategories
  345. STDMETHODIMP CShellAppManager::GetPublishedAppCategories(PSHELLAPPCATEGORYLIST psacl)
  346. {
  347. #ifndef DOWNLEVEL_PLATFORM
  348. HRESULT hres = E_INVALIDARG;
  349. if (psacl)
  350. {
  351. ASSERT(IS_VALID_READ_PTR(psacl, SHELLAPPCATEGORYLIST));
  352. ZeroMemory(psacl, SIZEOF(SHELLAPPCATEGORYLIST));
  353. // NOTE: keep the check inside the lock! So that only one thread
  354. // is allowed in.
  355. _Lock();
  356. // Do we have any app publishers in our list at all
  357. if (_hdpaPub)
  358. {
  359. if (_hdsaCategoryList == NULL)
  360. _BuildInternalCategoryList();
  361. hres = _CompileCategoryList(psacl);
  362. }
  363. _Unlock();
  364. }
  365. #else
  366. HRESULT hres = E_NOTIMPL;
  367. #endif //DOWNLEVEL_PLATFORM
  368. return hres;
  369. }
  370. #ifndef DOWNLEVEL_PLATFORM
  371. GUIDLIST * CShellAppManager::_FindGuidListForCategory(LPCWSTR pszDescription)
  372. {
  373. // The caller must enter the lock first
  374. ASSERT(0 < _cRefLock);
  375. if (_hdsaCategoryList)
  376. {
  377. int idsa;
  378. for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
  379. {
  380. CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
  381. if (pci && pci->pszDescription && !lstrcmpi(pszDescription, pci->pszDescription))
  382. return pci->pGuidList;
  383. }
  384. }
  385. return NULL;
  386. }
  387. #endif //DOWNLEVEL_PLATFORM
  388. extern void _DestroyHdpaEnum(HDPA hdpaEnum);
  389. // IShellAppManager::EnumPublishedApps
  390. STDMETHODIMP CShellAppManager::EnumPublishedApps(LPCWSTR pszCategory, IEnumPublishedApps ** ppepa)
  391. {
  392. #ifndef DOWNLEVEL_PLATFORM
  393. HRESULT hres = E_OUTOFMEMORY;
  394. ASSERT(pszCategory == NULL || IS_VALID_STRING_PTRW(pszCategory, -1));
  395. // hdpaEnum is the list of IEnumPublishedApp * we pass to the constructor of CShellEnumPublishedApps
  396. HDPA hdpaEnum = DPA_Create(4);
  397. if (hdpaEnum)
  398. {
  399. // If no category is required, we enumerate all
  400. if (pszCategory == NULL)
  401. {
  402. _Lock();
  403. if (_hdpaPub)
  404. {
  405. int idpa;
  406. for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
  407. {
  408. IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
  409. IEnumPublishedApps * pepa;
  410. if (pap && SUCCEEDED(pap->EnumApps(NULL, &pepa)))
  411. {
  412. ASSERT(IS_VALID_CODE_PTR(pepa, IEnumPublishedApps));
  413. if (DPA_AppendPtr(hdpaEnum, pepa) == DPA_ERR)
  414. {
  415. pepa->Release();
  416. break;
  417. }
  418. }
  419. }
  420. }
  421. _Unlock();
  422. }
  423. else
  424. {
  425. _Lock();
  426. // If there is no Category list, let's build one
  427. if (_hdsaCategoryList == NULL)
  428. _BuildInternalCategoryList();
  429. // Otherwise we find the GuidList and enumerate all the guys in the list
  430. GUIDLIST * pgl = _FindGuidListForCategory(pszCategory);
  431. while (pgl && pgl->papSupport)
  432. {
  433. ASSERT(IS_VALID_READ_PTR(pgl, GUIDLIST) && IS_VALID_CODE_PTR(pgl->papSupport, IAppPulisher));
  434. IEnumPublishedApps * pepa;
  435. if (SUCCEEDED(pgl->papSupport->EnumApps(&pgl->CatGuid, &pepa)))
  436. {
  437. ASSERT(IS_VALID_CODE_PTR(pepa, IEnumPublishedApps));
  438. if (DPA_AppendPtr(hdpaEnum, pepa) == DPA_ERR)
  439. {
  440. pepa->Release();
  441. break;
  442. }
  443. }
  444. pgl = pgl->pNextGuid;
  445. }
  446. _Unlock();
  447. }
  448. }
  449. // Even if we have no enumerators we return success and pass back an empty enumerator
  450. CShellEnumPublishedApps * psepa = new CShellEnumPublishedApps(hdpaEnum);
  451. if (psepa)
  452. {
  453. *ppepa = SAFECAST(psepa, IEnumPublishedApps *);
  454. hres = S_OK;
  455. }
  456. else
  457. {
  458. hres = E_OUTOFMEMORY;
  459. if (hdpaEnum)
  460. _DestroyHdpaEnum(hdpaEnum);
  461. }
  462. #else
  463. HRESULT hres = E_NOTIMPL;
  464. #endif //DOWNLEVEL_PLATFORM
  465. return hres;
  466. }
  467. EXTERN_C STDAPI InstallAppFromFloppyOrCDROM(HWND hwnd);
  468. STDMETHODIMP CShellAppManager::InstallFromFloppyOrCDROM(HWND hwndParent)
  469. {
  470. return InstallAppFromFloppyOrCDROM(hwndParent);
  471. }
  472. /*----------------------------------------------------------
  473. Purpose: Create-instance function for class factory
  474. */
  475. STDAPI CShellAppManager_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  476. {
  477. // aggregation checking is handled in class factory
  478. HRESULT hres = E_OUTOFMEMORY;
  479. CShellAppManager* pObj = new CShellAppManager();
  480. if (pObj)
  481. {
  482. *ppunk = SAFECAST(pObj, IShellAppManager *);
  483. hres = S_OK;
  484. }
  485. return hres;
  486. }