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.

1743 lines
48 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Matrix Array
  4. //
  5. //------------------------------------------------------------------------
  6. #include "priv.h"
  7. // Do not build this file if on Win9X or NT4
  8. #ifndef DOWNLEVEL_PLATFORM
  9. #include <shstr.h>
  10. #include "mtxarray.h"
  11. #include "adcctl.h" // for EnumArea string constants
  12. #include "util.h" // for ReleaseShellCategory
  13. #include "dump.h"
  14. #include "appwizid.h"
  15. #ifdef WINNT
  16. #include <tsappcmp.h> // for TermsrvAppInstallMode
  17. #endif
  18. //--------------------------------------------------------------------
  19. //
  20. //
  21. // CAppData class
  22. //
  23. //
  24. //--------------------------------------------------------------------
  25. #define MAX_DATE_SIZE 50
  26. #define IFS_APPINFODATA 1
  27. #define IFS_SHELLCAT 2
  28. #define IFS_CAPABILITY 3
  29. #define IFS_SUPPORTINFO 4
  30. #define IFS_INDEXLABEL 5
  31. #define IFS_INDEXVALUE 6
  32. #define IFS_PROPERTIES 7
  33. #define IFS_SIZE 8
  34. #define IFS_TIMESUSED 9
  35. #define IFS_LASTUSED 10
  36. #define IFS_OCSETUP 11
  37. #define IFS_SCHEDULE 12
  38. #define IFS_ICON 13
  39. #define IFS_ISINSTALLED 14
  40. const APPFIELDS g_rginstfields[] = {
  41. { /* 0 */ L"displayname", SORT_NAME, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszDisplayName) },
  42. { /* 1 */ L"size", SORT_SIZE, IFS_SIZE, VT_BSTR, 0 },
  43. { /* 2 */ L"timesused", SORT_TIMESUSED, IFS_TIMESUSED, VT_BSTR, 0 },
  44. { /* 3 */ L"lastused", SORT_LASTUSED, IFS_LASTUSED, VT_BSTR, 0 },
  45. { /* 4 */ L"capability", SORT_NA, IFS_CAPABILITY , VT_UI4, 0 },
  46. { /* 5 */ L"supportinfo", SORT_NA, IFS_SUPPORTINFO, VT_BSTR, 0 },
  47. { /* 6 */ L"indexlabel", SORT_NA, IFS_INDEXLABEL, VT_BSTR, 0 },
  48. { /* 7 */ L"indexvalue", SORT_NA, IFS_INDEXVALUE, VT_BSTR, 0 },
  49. { /* 8 */ L"htmlproperties", SORT_NA, IFS_PROPERTIES, VT_BSTR, 0 },
  50. { /* 9 */ L"icon", SORT_NA, IFS_ICON, VT_BSTR, 0 },
  51. };
  52. const APPFIELDS g_rgpubfields[] = {
  53. { /* 0 */ L"displayname", SORT_NAME, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszDisplayName) },
  54. { /* 1 */ L"capability", SORT_NA, IFS_CAPABILITY , VT_UI4, 0 },
  55. { /* 2 */ L"supporturl", SORT_NA, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszSupportUrl) },
  56. { /* 3 */ L"htmlproperties", SORT_NA, IFS_PROPERTIES, VT_BSTR, 0 },
  57. { /* 4 */ L"addlaterschedule", SORT_NA, IFS_SCHEDULE, VT_BSTR, 0 },
  58. { /* 5 */ L"isinstalled", SORT_NA, IFS_ISINSTALLED, VT_BSTR, 0 },
  59. };
  60. const APPFIELDS g_rgsetupfields[] = {
  61. { /* 0 */ L"displayname", SORT_NAME, IFS_OCSETUP, VT_BSTR, FIELD_OFFSET(COCSetupApp, _szDisplayName) },
  62. };
  63. const APPFIELDS g_rgcatfields[] = {
  64. { /* 0 */ L"DisplayName", SORT_NAME, IFS_SHELLCAT, VT_BSTR, FIELD_OFFSET(SHELLAPPCATEGORY, pszCategory) },
  65. { /* 1 */ L"ID", SORT_NA, IFS_SHELLCAT, VT_UI4, FIELD_OFFSET(SHELLAPPCATEGORY, idCategory) },
  66. };
  67. // Overloaded constructor
  68. CAppData::CAppData(IInstalledApp* pia, APPINFODATA* paid, PSLOWAPPINFO psai) : _cRef(1)
  69. {
  70. _pia = pia;
  71. CopyMemory(&_ai, paid, sizeof(_ai));
  72. //NOTE: psai can be NULL
  73. if (psai)
  74. {
  75. CopyMemory(&_aiSlow, psai, sizeof(_aiSlow));
  76. // Let's massage some values.
  77. _MassageSlowAppInfo();
  78. }
  79. _dwEnum = ENUM_INSTALLED;
  80. InitializeCriticalSection(&_cs);
  81. _fCsInitialized = TRUE;
  82. }
  83. // Overloaded constructor
  84. CAppData::CAppData(IPublishedApp* ppa, APPINFODATA* paid, PUBAPPINFO * ppai) : _cRef(1)
  85. {
  86. _ppa = ppa;
  87. CopyMemory(&_ai, paid, sizeof(_ai));
  88. CopyMemory(&_pai, ppai, sizeof(_pai));
  89. _dwEnum = ENUM_PUBLISHED;
  90. }
  91. // Overloaded constructor
  92. CAppData::CAppData(COCSetupApp * pocsa, APPINFODATA* paid) : _cRef(1)
  93. {
  94. _pocsa = pocsa;
  95. CopyMemory(&_ai, paid, sizeof(_ai));
  96. _dwEnum = ENUM_OCSETUP;
  97. }
  98. // Overloaded constructor
  99. CAppData::CAppData(SHELLAPPCATEGORY * psac) : _cRef(1)
  100. {
  101. _psac = psac;
  102. _dwEnum = ENUM_CATEGORIES;
  103. }
  104. // destructor
  105. CAppData::~CAppData()
  106. {
  107. if (ENUM_CATEGORIES == _dwEnum)
  108. ReleaseShellCategory(_psac);
  109. else if (ENUM_OCSETUP == _dwEnum)
  110. {
  111. delete _pocsa;
  112. ClearAppInfoData(&_ai);
  113. }
  114. else
  115. {
  116. // Release _pia or _ppa. Take your pick. Either way releases
  117. // the object because both inherit from IUnknown.
  118. _pia->Release();
  119. ClearAppInfoData(&_ai);
  120. ClearSlowAppInfo(&_aiSlow);
  121. ClearPubAppInfo(&_pai);
  122. }
  123. if (_fCsInitialized)
  124. DeleteCriticalSection(&_cs);
  125. }
  126. /*--------------------------------------------------------------------
  127. Purpose: IUnknown::QueryInterface
  128. */
  129. STDMETHODIMP CAppData::QueryInterface(REFIID riid, LPVOID * ppvObj)
  130. {
  131. static const QITAB qit[] = {
  132. QITABENT(CAppData, IAppData),
  133. { 0 },
  134. };
  135. return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  136. }
  137. STDMETHODIMP_(ULONG) CAppData::AddRef()
  138. {
  139. InterlockedIncrement(&_cRef);
  140. TraceAddRef(CAppData, _cRef);
  141. return _cRef;
  142. }
  143. STDMETHODIMP_(ULONG) CAppData::Release()
  144. {
  145. ASSERT(_cRef > 0);
  146. InterlockedDecrement(&_cRef);
  147. TraceRelease(CAppData, _cRef);
  148. if (_cRef > 0)
  149. return _cRef;
  150. delete this;
  151. return 0;
  152. }
  153. /*-------------------------------------------------------------------------
  154. Purpose: Massage some values so they are sorted correctly
  155. */
  156. void CAppData::_MassageSlowAppInfo(void)
  157. {
  158. // We don't tell the difference b/t unknown app sizes and zero
  159. // app sizes, so to make sorting easier, change the unknown app
  160. // size to zero.
  161. if (-1 == (__int64)_aiSlow.ullSize)
  162. _aiSlow.ullSize = 0;
  163. // Unmarked last-used fields will get marked as 'not used', so
  164. // they are sorted correctly.
  165. if (0 == _aiSlow.ftLastUsed.dwHighDateTime &&
  166. 0 == _aiSlow.ftLastUsed.dwLowDateTime)
  167. {
  168. _aiSlow.ftLastUsed.dwHighDateTime = NOTUSED_HIGHDATETIME;
  169. _aiSlow.ftLastUsed.dwLowDateTime = NOTUSED_LOWDATETIME;
  170. }
  171. }
  172. /*-------------------------------------------------------------------------
  173. Purpose: IAppData::ReadSlowData
  174. Read the slow app data. Call GetSlowDataPtr() to get it.
  175. */
  176. STDMETHODIMP CAppData::ReadSlowData(void)
  177. {
  178. HRESULT hres = E_FAIL;
  179. if (ENUM_INSTALLED == _dwEnum && _pia)
  180. {
  181. SLOWAPPINFO sai = {0};
  182. hres = _pia->GetSlowAppInfo(&sai);
  183. if (S_OK == hres)
  184. {
  185. // Switch our current SLOWAPPINFO with the new one
  186. // This is necessary because our current one is from the GetCachedSlowAppInfo
  187. // It may not have the most up-to-date info.
  188. if (IsSlowAppInfoChanged(&_aiSlow, &sai))
  189. {
  190. EnterCriticalSection(&_cs);
  191. {
  192. ClearSlowAppInfo(&_aiSlow);
  193. _aiSlow = sai;
  194. hres = S_OK;
  195. }
  196. LeaveCriticalSection(&_cs);
  197. // Let's massage some values.
  198. _MassageSlowAppInfo();
  199. }
  200. else
  201. hres = S_FALSE;
  202. }
  203. }
  204. return hres;
  205. }
  206. /*-------------------------------------------------------------------------
  207. Purpose: IAppData::GetDataPtr
  208. Returns the pointer to the internal data structure. The
  209. caller must hold the appdata object to guarantee the pointer
  210. remains valid.
  211. */
  212. STDMETHODIMP_(APPINFODATA *) CAppData::GetDataPtr(void)
  213. {
  214. return &_ai;
  215. }
  216. /*-------------------------------------------------------------------------
  217. Purpose: IAppData::GetSlowDataPtr
  218. Returns the pointer to the internal data structure for slow
  219. data. The caller must hold the appdata object to guarantee the
  220. pointer remains valid.
  221. */
  222. STDMETHODIMP_(SLOWAPPINFO *) CAppData::GetSlowDataPtr(void)
  223. {
  224. return &_aiSlow;
  225. }
  226. /*-------------------------------------------------------------------------
  227. Purpose: Return the capability flags
  228. */
  229. DWORD CAppData::_GetCapability(void)
  230. {
  231. DWORD dwCapability = 0;
  232. if (ENUM_INSTALLED == _dwEnum || ENUM_PUBLISHED == _dwEnum)
  233. {
  234. // We can use _pia or _ppa for this method because both
  235. // inherit from IShellApp.
  236. _pia->GetPossibleActions(&dwCapability);
  237. }
  238. return dwCapability;
  239. }
  240. /*-------------------------------------------------------------------------
  241. Purpose: Returns the current sort index
  242. */
  243. DWORD CAppData::_GetSortIndex(void)
  244. {
  245. DWORD dwRet = SORT_NAME; // default
  246. if (_pmtxParent)
  247. _pmtxParent->GetSortIndex(&dwRet);
  248. return dwRet;
  249. }
  250. /*-------------------------------------------------------------------------
  251. Purpose: Return the amount of disk space the app occupies.
  252. Returns S_OK if the field is valid.
  253. */
  254. HRESULT CAppData::_GetDiskSize(LPTSTR pszBuf, int cchBuf)
  255. {
  256. HRESULT hres = S_FALSE;
  257. ULONGLONG ullSize = GetSlowDataPtr()->ullSize;
  258. if (pszBuf && 0 < cchBuf)
  259. *pszBuf = 0;
  260. // Is this size printable?
  261. if (-1 != (__int64)ullSize && 0 != (__int64)ullSize)
  262. {
  263. // Yes
  264. if (pszBuf)
  265. ShortSizeFormat64(ullSize, pszBuf);
  266. hres = S_OK;
  267. }
  268. return hres;
  269. }
  270. #define FREQDECAY_OFTEN 10
  271. /*-------------------------------------------------------------------------
  272. Purpose: IAppData::GetFrequencyOfUse
  273. Return friendly names that map from the frequency-of-use
  274. metric from the UEM. Returns S_OK if the field is valid.
  275. */
  276. STDMETHODIMP CAppData::GetFrequencyOfUse(LPTSTR pszBuf, int cchBuf)
  277. {
  278. HRESULT hres;
  279. int iTemp = GetSlowDataPtr()->iTimesUsed;
  280. if (0 > iTemp)
  281. {
  282. if (pszBuf && 0 < cchBuf)
  283. *pszBuf = 0;
  284. hres = S_FALSE;
  285. }
  286. else
  287. {
  288. UINT ids = IDS_OFTEN;
  289. if (2 >= iTemp)
  290. ids = IDS_RARELY;
  291. else if (FREQDECAY_OFTEN >= iTemp)
  292. ids = IDS_SOMETIMES;
  293. if (pszBuf)
  294. LoadString(g_hinst, ids, pszBuf, cchBuf);
  295. hres = S_OK;
  296. }
  297. return hres;
  298. }
  299. /*-------------------------------------------------------------------------
  300. Purpose: Return the friendly date when the app was last used.
  301. Returns S_OK if the field is valid.
  302. */
  303. HRESULT CAppData::_GetLastUsed(LPTSTR pszBuf, int cchBuf)
  304. {
  305. HRESULT hres;
  306. FILETIME ft = GetSlowDataPtr()->ftLastUsed;
  307. if (NOTUSED_HIGHDATETIME == ft.dwHighDateTime &&
  308. NOTUSED_LOWDATETIME == ft.dwLowDateTime)
  309. {
  310. if (pszBuf && 0 < cchBuf)
  311. *pszBuf = 0;
  312. hres = S_FALSE;
  313. }
  314. else
  315. {
  316. DWORD dwFlags = FDTF_SHORTDATE;
  317. ASSERT(0 != ft.dwHighDateTime || 0 != ft.dwLowDateTime);
  318. if (pszBuf)
  319. SHFormatDateTime(&ft, &dwFlags, pszBuf, cchBuf);
  320. hres = S_OK;
  321. }
  322. return hres;
  323. }
  324. /*-------------------------------------------------------------------------
  325. Purpose: Build up a structured string that the html script can read and
  326. parse. The Boilerplate JavaScript class assumes the fields
  327. follow this format:
  328. <fieldname1 "value1"><fieldname2 value2>...
  329. It can accept quoted or non-quoted values. If the value has a '>' in
  330. it, then it should be quoted, otherwise the Boilerplate code will
  331. get confused.
  332. The order of the fieldnames is not important, and some or all may
  333. be missing.
  334. */
  335. LPTSTR CAppData::_BuildSupportInfo(void)
  336. {
  337. const static struct
  338. {
  339. LPWSTR pszFieldname;
  340. DWORD ibOffset; // offset into structure designated by dwStruct
  341. } s_rgsupfld[] = {
  342. { L"comments", FIELD_OFFSET(APPINFODATA, pszComments) },
  343. { L"contact", FIELD_OFFSET(APPINFODATA, pszContact) },
  344. { L"displayname", FIELD_OFFSET(APPINFODATA, pszDisplayName) },
  345. { L"helpurl", FIELD_OFFSET(APPINFODATA, pszHelpLink) },
  346. { L"helpphone", FIELD_OFFSET(APPINFODATA, pszSupportTelephone) },
  347. { L"productID", FIELD_OFFSET(APPINFODATA, pszProductID) },
  348. { L"publisher", FIELD_OFFSET(APPINFODATA, pszPublisher) },
  349. { L"readmeUrl", FIELD_OFFSET(APPINFODATA, pszReadmeUrl) },
  350. { L"regcompany", FIELD_OFFSET(APPINFODATA, pszRegisteredCompany) },
  351. { L"regowner", FIELD_OFFSET(APPINFODATA, pszRegisteredOwner) },
  352. { L"supporturl", FIELD_OFFSET(APPINFODATA, pszSupportUrl) },
  353. { L"updateinfoUrl", FIELD_OFFSET(APPINFODATA, pszUpdateInfoUrl) },
  354. { L"version", FIELD_OFFSET(APPINFODATA, pszVersion) },
  355. };
  356. ShStr shstr;
  357. int i;
  358. // Now populate the structured string
  359. for (i = 0; i < ARRAYSIZE(s_rgsupfld); i++)
  360. {
  361. LPWSTR pszT = *(LPWSTR *)((LPBYTE)&_ai + s_rgsupfld[i].ibOffset);
  362. if (pszT && *pszT)
  363. {
  364. TCHAR sz[64];
  365. wsprintf(sz, TEXT("<%s \""), s_rgsupfld[i].pszFieldname);
  366. shstr.Append(sz);
  367. shstr.Append(pszT);
  368. shstr.Append(TEXT("\">"));
  369. }
  370. }
  371. return shstr.CloneStr();
  372. }
  373. #define c_szPropRowBegin TEXT("<TR><TD class=PropLabel>")
  374. #define c_szPropRowMid TEXT("</TD><TD class=PropValue>")
  375. #define c_szPropRowEnd TEXT("</TD></TR>")
  376. // CAppData::_BuildPropertiesHTML
  377. // Build up a string of HTML that represents the list of useful
  378. // properties for this app. We skip any blank fields.
  379. LPTSTR CAppData::_BuildPropertiesHTML(void)
  380. {
  381. LPTSTR pszRet = NULL;
  382. DWORD dwSort = _GetSortIndex();
  383. // The HTML consists of a table, each row is a property label and value.
  384. // Since the 'indexlabel' and 'indexvalue' properties vary depending
  385. // on the sort criteria, we exclude them to avoid duplication. For
  386. // example, if the user is sorting by size, we don't show the size
  387. // in the properties HTML because the size is being shown via the
  388. // 'indexvalue' property.
  389. // Let's first make a pass of the eligible properties to see which
  390. // ones to include.
  391. #define PM_SIZE 0x0001
  392. #define PM_TIMESUSED 0x0002
  393. #define PM_LASTUSED 0x0004
  394. #define PM_INSTALLEDON 0x0008
  395. TCHAR szSize[64];
  396. TCHAR szFreq[64];
  397. TCHAR szLastUsed[64];
  398. DWORD dwPropMask = 0; // Can be combination of PM_*
  399. dwPropMask |= (S_OK == _GetDiskSize(szSize, SIZECHARS(szSize))) ? PM_SIZE : 0;
  400. dwPropMask |= (S_OK == GetFrequencyOfUse(szFreq, SIZECHARS(szFreq))) ? PM_TIMESUSED : 0;
  401. dwPropMask |= (S_OK == _GetLastUsed(szLastUsed, SIZECHARS(szLastUsed))) ? PM_LASTUSED : 0;
  402. // The sorting criteria should be removed
  403. if (SORT_NAME == dwSort || SORT_SIZE == dwSort)
  404. dwPropMask &= ~PM_SIZE;
  405. else if (SORT_TIMESUSED == dwSort)
  406. dwPropMask &= ~PM_TIMESUSED;
  407. else if (SORT_LASTUSED == dwSort)
  408. dwPropMask &= ~PM_LASTUSED;
  409. // Are there any properties to show at all?
  410. if (dwPropMask)
  411. {
  412. // Yes; build the html for the table.
  413. ShStr shstr;
  414. TCHAR szLabel[64];
  415. shstr = TEXT("<TABLE id=idTblExtendedProps class=Focus>");
  416. struct {
  417. DWORD dwMask;
  418. UINT ids;
  419. LPTSTR psz;
  420. } s_rgProp[] = {
  421. { PM_SIZE, IDS_LABEL_SIZE, szSize },
  422. { PM_TIMESUSED, IDS_LABEL_TIMESUSED, szFreq },
  423. { PM_LASTUSED, IDS_LABEL_LASTUSED, szLastUsed },
  424. };
  425. int i;
  426. for (i = 0; i < ARRAYSIZE(s_rgProp); i++)
  427. {
  428. if (dwPropMask & s_rgProp[i].dwMask)
  429. {
  430. LoadString(g_hinst, s_rgProp[i].ids, szLabel, SIZECHARS(szLabel));
  431. shstr.Append(c_szPropRowBegin);
  432. shstr.Append(szLabel);
  433. shstr.Append(c_szPropRowMid);
  434. // Size and frequency-of-use get anchor elements around them...
  435. if (s_rgProp[i].dwMask & PM_TIMESUSED)
  436. shstr.Append(TEXT("<SPAN id=idAFrequency class='FakeAnchor' tabIndex=0 onKeyDown='_OnKeyDownFakeAnchor()' onClick='_OpenDefinition();'>&nbsp;<U>"));
  437. else if (s_rgProp[i].dwMask & PM_SIZE)
  438. shstr.Append(TEXT("<SPAN id=idASize class='FakeAnchor' tabIndex=0 onKeyDown='_OnKeyDownFakeAnchor()' onClick='_OpenDefinition();'>&nbsp;<U>"));
  439. shstr.Append(s_rgProp[i].psz);
  440. if (s_rgProp[i].dwMask & PM_TIMESUSED)
  441. shstr.Append(TEXT("</U></SPAN>"));
  442. else if (s_rgProp[i].dwMask & PM_SIZE)
  443. shstr.Append(TEXT("</U></SPAN>"));
  444. shstr.Append(c_szPropRowEnd);
  445. }
  446. }
  447. shstr.Append(TEXT("</TABLE>"));
  448. pszRet = shstr.CloneStr(); // clone for the caller
  449. }
  450. return pszRet;
  451. }
  452. #define c_szIconHTMLFormat TEXT("sysimage://%s/small")
  453. /*-------------------------------------------------------------------------
  454. Purpose: Return the HTML that points to the icon of this application
  455. use instshld.ico as a default
  456. */
  457. void CAppData::_GetIconHTML(LPTSTR pszIconHTML, UINT cch)
  458. {
  459. if (cch > (MAX_PATH + ARRAYSIZE(c_szIconHTMLFormat)))
  460. {
  461. TCHAR szImage[MAX_PATH+10];
  462. if (_ai.pszImage && _ai.pszImage[0])
  463. {
  464. StrCpyN(szImage, _ai.pszImage, ARRAYSIZE(szImage));
  465. //PathParseIconLocation(szImage);
  466. }
  467. else
  468. {
  469. BOOL bUseDefault = FALSE;
  470. EnterCriticalSection(&_cs);
  471. if (_aiSlow.pszImage && _aiSlow.pszImage[0])
  472. lstrcpy(szImage, _aiSlow.pszImage);
  473. else
  474. bUseDefault = TRUE;
  475. LeaveCriticalSection(&_cs);
  476. if (bUseDefault)
  477. goto DEFAULT;
  478. }
  479. wnsprintf(pszIconHTML, cch, c_szIconHTMLFormat, szImage);
  480. }
  481. else
  482. DEFAULT:
  483. // In the default case, return instshld.ico
  484. lstrcpy(pszIconHTML, TEXT("res://appwiz.cpl/instshld.ico"));
  485. }
  486. /*-------------------------------------------------------------------------
  487. Purpose: Sets the variant with the correct data given the field
  488. */
  489. HRESULT CAppData::_VariantFromData(const APPFIELDS * pfield, LPVOID pvData, VARIANT * pvar)
  490. {
  491. HRESULT hres = S_OK;
  492. VariantInit(pvar);
  493. if (pvData)
  494. {
  495. if (VT_BSTR == pfield->vt)
  496. {
  497. LPWSTR psz = *(LPWSTR*)pvData;
  498. if (NULL == psz)
  499. psz = L"";
  500. ASSERT(IS_VALID_STRING_PTR(psz, -1));
  501. pvar->bstrVal = SysAllocString(psz);
  502. if (pvar->bstrVal)
  503. pvar->vt = VT_BSTR;
  504. else
  505. hres = E_OUTOFMEMORY;
  506. }
  507. else if (VT_UI4 == pfield->vt)
  508. {
  509. pvar->lVal = *(LONG*)pvData;
  510. pvar->vt = pfield->vt;
  511. }
  512. }
  513. return hres;
  514. }
  515. /*-------------------------------------------------------------------------
  516. Purpose: Get the value of the installed-app field.
  517. */
  518. HRESULT CAppData::_GetInstField(DB_LORDINAL iField, VARIANT * pvar)
  519. {
  520. HRESULT hres = E_FAIL;
  521. ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT));
  522. ASSERT(ENUM_INSTALLED == _dwEnum);
  523. // Columns map to different fields.
  524. if (IsInRange(iField, 0, ARRAYSIZE(g_rginstfields)))
  525. {
  526. const APPFIELDS * pfield = &g_rginstfields[iField];
  527. LPVOID pvData = NULL; // assume no value
  528. TCHAR szTemp[MAX_PATH*2];
  529. LPCTSTR pszTmp;
  530. DWORD dwTemp;
  531. // Map the field ordinal to the actual value in the appropriate structure
  532. switch (pfield->dwStruct)
  533. {
  534. case IFS_APPINFODATA:
  535. pvData = (LPVOID)((LPBYTE)&_ai + pfield->ibOffset);
  536. break;
  537. case IFS_CAPABILITY:
  538. dwTemp = _GetCapability();
  539. pvData = (LPVOID) &dwTemp;
  540. break;
  541. case IFS_SUPPORTINFO:
  542. pszTmp = CAppData::_BuildSupportInfo();
  543. if (pszTmp)
  544. {
  545. TraceMsg(TF_VERBOSEDSO, "HTML (supportinfo): \"%s\"", pszTmp);
  546. pvData = (LPVOID) &pszTmp;
  547. }
  548. break;
  549. case IFS_SIZE:
  550. _GetDiskSize(szTemp, SIZECHARS(szTemp));
  551. pszTmp = szTemp;
  552. pvData = (LPVOID) &pszTmp;
  553. break;
  554. case IFS_TIMESUSED:
  555. GetFrequencyOfUse(szTemp, SIZECHARS(szTemp));
  556. pszTmp = szTemp;
  557. pvData = (LPVOID) &pszTmp;
  558. break;
  559. case IFS_LASTUSED:
  560. _GetLastUsed(szTemp, SIZECHARS(szTemp));
  561. pszTmp = szTemp;
  562. pvData = (LPVOID) &pszTmp;
  563. break;
  564. case IFS_INDEXLABEL:
  565. // Unknown values shouldn't show the labels, so assume the
  566. // value will be unknown
  567. dwTemp = 0;
  568. // The index label is according to the current sort criteria
  569. switch (_GetSortIndex())
  570. {
  571. case SORT_NAME: // when sorting by name, we show the size
  572. case SORT_SIZE:
  573. if (S_OK == _GetDiskSize(NULL, 0))
  574. dwTemp = IDS_LABEL_SIZE;
  575. break;
  576. case SORT_TIMESUSED:
  577. if (S_OK == GetFrequencyOfUse(NULL, 0))
  578. dwTemp = IDS_LABEL_TIMESUSED;
  579. break;
  580. case SORT_LASTUSED:
  581. if (S_OK == _GetLastUsed(NULL, 0))
  582. dwTemp = IDS_LABEL_LASTUSED;
  583. break;
  584. }
  585. if (0 != dwTemp)
  586. {
  587. LoadString(g_hinst, dwTemp, szTemp, SIZECHARS(szTemp));
  588. pszTmp = szTemp;
  589. pvData = (LPVOID) &pszTmp;
  590. TraceMsg(TF_VERBOSEDSO, "HTML (indexlabel): \"%s\"", pszTmp);
  591. }
  592. break;
  593. case IFS_INDEXVALUE:
  594. // The index value is according to the current sort criteria
  595. switch (_GetSortIndex())
  596. {
  597. case SORT_NAME: // when sorting by name, we show the size
  598. case SORT_SIZE:
  599. _GetDiskSize(szTemp, SIZECHARS(szTemp));
  600. break;
  601. case SORT_TIMESUSED:
  602. GetFrequencyOfUse(szTemp, SIZECHARS(szTemp));
  603. break;
  604. case SORT_LASTUSED:
  605. _GetLastUsed(szTemp, SIZECHARS(szTemp));
  606. break;
  607. }
  608. pszTmp = szTemp;
  609. pvData = (LPVOID) &pszTmp;
  610. TraceMsg(TF_VERBOSEDSO, "HTML (indexvalue) for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp);
  611. break;
  612. case IFS_PROPERTIES:
  613. pszTmp = _BuildPropertiesHTML();
  614. if (pszTmp)
  615. {
  616. pvData = (LPVOID) &pszTmp;
  617. TraceMsg(TF_VERBOSEDSO, "HTML (properties) for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp);
  618. }
  619. break;
  620. case IFS_ICON:
  621. _GetIconHTML(szTemp, ARRAYSIZE(szTemp));
  622. pszTmp = szTemp;
  623. pvData = (LPVOID) &pszTmp;
  624. TraceMsg(TF_VERBOSEDSO, "Icon HTML for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp);
  625. break;
  626. default:
  627. ASSERTMSG(0, "invalid field type");
  628. break;
  629. }
  630. hres = _VariantFromData(pfield, pvData, pvar);
  631. // Clean up
  632. switch (pfield->dwStruct)
  633. {
  634. case IFS_SUPPORTINFO:
  635. case IFS_PROPERTIES:
  636. if (pvData)
  637. {
  638. pszTmp = *(LPWSTR*)pvData;
  639. LocalFree((HLOCAL)pszTmp);
  640. }
  641. break;
  642. }
  643. }
  644. return hres;
  645. }
  646. #define c_szAddLaterHTML TEXT("<TABLE id=idTblExtendedProps class=Focus><TR><TD class=AddPropLabel>%s</TD><TD class=AddPropValue><A id=idASchedule class=Focus href=''>%s</A></TD></TR></TABLE>")
  647. /*-------------------------------------------------------------------------
  648. Purpose: Get the value of the published app field.
  649. */
  650. HRESULT CAppData::_GetPubField(DB_LORDINAL iField, VARIANT * pvar)
  651. {
  652. HRESULT hres = E_FAIL;
  653. ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT));
  654. ASSERT(ENUM_PUBLISHED == _dwEnum);
  655. // Columns map to different fields.
  656. if (IsInRange(iField, 0, ARRAYSIZE(g_rgpubfields)))
  657. {
  658. DWORD dwTemp = 0;
  659. const APPFIELDS * pfield = &g_rgpubfields[iField];
  660. LPVOID pvData = NULL;
  661. WCHAR szTemp[MAX_PATH*2];
  662. LPWSTR pszTmp = NULL;
  663. // Map the field ordinal to the actual value in the appropriate structure
  664. switch (pfield->dwStruct)
  665. {
  666. case IFS_APPINFODATA:
  667. // For the display name, we want to display it in the following format if there are
  668. // duplicate entries: Ex "Word : Policy1" and "Word : Policy2"
  669. if ((iField == 0) && _bNameDupe && (_pai.dwMask & PAI_SOURCE) && _pai.pszSource && _pai.pszSource[0])
  670. {
  671. wnsprintf(szTemp, ARRAYSIZE(szTemp), L"%ls: %ls", _ai.pszDisplayName, _pai.pszSource);
  672. pszTmp = szTemp;
  673. pvData = (LPVOID) &pszTmp;
  674. }
  675. else
  676. pvData = (LPVOID)((LPBYTE)&_ai + pfield->ibOffset);
  677. break;
  678. case IFS_CAPABILITY:
  679. dwTemp = _GetCapability();
  680. pvData = (LPVOID) &dwTemp;
  681. break;
  682. case IFS_SCHEDULE:
  683. if ((_GetCapability() & APPACTION_ADDLATER) && (_pai.dwMask & PAI_SCHEDULEDTIME))
  684. {
  685. TCHAR szTime[MAX_PATH];
  686. if (FormatSystemTimeString(&_pai.stScheduled, szTime, ARRAYSIZE(szTime)))
  687. {
  688. TCHAR szAddLater[100];
  689. LoadString(g_hinst, IDS_ADDLATER, szAddLater, ARRAYSIZE(szAddLater));
  690. wsprintf(szTemp, c_szAddLaterHTML, szAddLater, szTime);
  691. pszTmp = szTemp;
  692. pvData = (LPVOID) &pszTmp;
  693. }
  694. }
  695. break;
  696. case IFS_ISINSTALLED:
  697. if (S_OK == _ppa->IsInstalled())
  698. {
  699. LoadString(g_hinst, IDS_INSTALLED, szTemp, SIZECHARS(szTemp));
  700. pszTmp = szTemp;
  701. pvData = (LPVOID) &pszTmp;
  702. }
  703. break;
  704. default:
  705. ASSERTMSG(0, "invalid field type");
  706. hres = E_FAIL;
  707. break;
  708. }
  709. hres = _VariantFromData(pfield, pvData, pvar);
  710. }
  711. return hres;
  712. }
  713. /*-------------------------------------------------------------------------
  714. Purpose: Get the value of the ocsetup app field.
  715. */
  716. HRESULT CAppData::_GetSetupField(DB_LORDINAL iField, VARIANT * pvar)
  717. {
  718. HRESULT hres = E_FAIL;
  719. ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT));
  720. ASSERT(ENUM_OCSETUP == _dwEnum);
  721. // Columns map to different fields.
  722. if (IsInRange(iField, 0, ARRAYSIZE(g_rgsetupfields)))
  723. {
  724. const APPFIELDS * pfield = &g_rgsetupfields[iField];
  725. LPVOID pvData = NULL;
  726. ASSERT(IFS_OCSETUP == pfield->dwStruct);
  727. pvData = (LPVOID)((LPBYTE)&_pocsa + pfield->ibOffset);
  728. hres = _VariantFromData(pfield, pvData, pvar);
  729. }
  730. return hres;
  731. }
  732. /*-------------------------------------------------------------------------
  733. Purpose: Get the value of the category field.
  734. */
  735. HRESULT CAppData::_GetCatField(DB_LORDINAL iField, VARIANT * pvar)
  736. {
  737. HRESULT hres = E_FAIL;
  738. ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT));
  739. ASSERT(ENUM_CATEGORIES == _dwEnum);
  740. // Columns map to different fields.
  741. if (IsInRange(iField, 0, ARRAYSIZE(g_rgcatfields)))
  742. {
  743. const APPFIELDS * pfield = &g_rgcatfields[iField];
  744. LPVOID pvData = NULL;
  745. ASSERT(IFS_SHELLCAT == pfield->dwStruct);
  746. pvData = (LPVOID)((LPBYTE)_psac + pfield->ibOffset);
  747. hres = _VariantFromData(pfield, pvData, pvar);
  748. }
  749. return hres;
  750. }
  751. /*-------------------------------------------------------------------------
  752. Purpose: IAppData::GetVariant
  753. Return the particular field as a variant. The caller is responsible
  754. for clearing/freeing the variant. The iField is assumed to be
  755. 0-based.
  756. */
  757. STDMETHODIMP CAppData::GetVariant(DB_LORDINAL iField, VARIANT * pvar)
  758. {
  759. HRESULT hres;
  760. switch (_dwEnum)
  761. {
  762. case ENUM_INSTALLED:
  763. hres = _GetInstField(iField, pvar);
  764. break;
  765. case ENUM_PUBLISHED:
  766. hres = _GetPubField(iField, pvar);
  767. break;
  768. case ENUM_OCSETUP:
  769. hres = _GetSetupField(iField, pvar);
  770. break;
  771. case ENUM_CATEGORIES:
  772. hres = _GetCatField(iField, pvar);
  773. break;
  774. }
  775. return hres;
  776. }
  777. /*-------------------------------------------------------------------------
  778. Purpose: IAppData::SetMtxParent
  779. */
  780. STDMETHODIMP CAppData::SetMtxParent(IMtxArray * pmtxParent)
  781. {
  782. // We don't AddRef because this appdata object lives within the
  783. // lifetime of the matrix object.
  784. _pmtxParent = pmtxParent;
  785. return S_OK;
  786. }
  787. /*-------------------------------------------------------------------------
  788. Purpose: IAppData::DoCommand
  789. Executes the given command depending on the capabilities
  790. of the item.
  791. */
  792. STDMETHODIMP CAppData::DoCommand(HWND hwndParent, APPCMD appcmd)
  793. {
  794. HRESULT hres = S_FALSE;
  795. DWORD dwActions = 0;
  796. switch (_dwEnum)
  797. {
  798. case ENUM_INSTALLED:
  799. // Find out of the requested command is allowed
  800. // FEATURE (scotth): need to add policies check here
  801. _pia->GetPossibleActions(&dwActions);
  802. switch (appcmd)
  803. {
  804. case APPCMD_UNINSTALL:
  805. if (g_dwPrototype & PF_FAKEUNINSTALL)
  806. {
  807. // Say the app was deleted (this is optimized out in retail)
  808. hres = S_OK;
  809. }
  810. else
  811. {
  812. if (dwActions & APPACTION_UNINSTALL)
  813. {
  814. hres = _pia->Uninstall(hwndParent);
  815. if (SUCCEEDED(hres))
  816. {
  817. // Return S_FALSE if the app is still installed (the user
  818. // might have cancelled the uninstall)
  819. hres = (S_OK == _pia->IsInstalled()) ? S_FALSE : S_OK;
  820. }
  821. }
  822. }
  823. break;
  824. case APPCMD_MODIFY:
  825. if (dwActions & APPACTION_MODIFY)
  826. hres = _pia->Modify(hwndParent);
  827. break;
  828. case APPCMD_REPAIR:
  829. if (dwActions & APPACTION_REPAIR)
  830. hres = _pia->Repair(TRUE);
  831. break;
  832. case APPCMD_UPGRADE:
  833. if (dwActions & APPACTION_UPGRADE)
  834. hres = _pia->Upgrade();
  835. break;
  836. default:
  837. TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd));
  838. break;
  839. }
  840. break;
  841. case ENUM_PUBLISHED:
  842. // Find out of the requested command is allowed
  843. // FEATURE (scotth): need to add policies check here
  844. _ppa->GetPossibleActions(&dwActions);
  845. switch (appcmd)
  846. {
  847. case APPCMD_INSTALL:
  848. if (dwActions & APPACTION_INSTALL)
  849. {
  850. hres = S_OK;
  851. // Does the app have an expired publishing time?
  852. if (_pai.dwMask & PAI_EXPIRETIME)
  853. {
  854. // Yes, it does. Let's compare the expired time with our current time
  855. SYSTEMTIME stCur = {0};
  856. GetLocalTime(&stCur);
  857. // Is "now" later than the expired time?
  858. if (CompareSystemTime(&stCur, &_pai.stExpire) > 0)
  859. {
  860. // Yes, warn the user and return failure
  861. ShellMessageBox(g_hinst, hwndParent, MAKEINTRESOURCE(IDS_EXPIRED),
  862. MAKEINTRESOURCE(IDS_NAME), MB_OK | MB_ICONEXCLAMATION);
  863. hres = E_FAIL;
  864. }
  865. }
  866. // if hres is not set by the above code, preceed with installation
  867. if (hres == S_OK)
  868. {
  869. HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  870. #ifdef WINNT
  871. // On NT, let Terminal Services know that we are about to install an application.
  872. // NOTE: This function should be called no matter the Terminal Services
  873. // is running or not.
  874. BOOL bPrevMode = TermsrvAppInstallMode();
  875. SetTermsrvAppInstallMode(TRUE);
  876. #endif
  877. hres = _ppa->Install(NULL);
  878. #ifdef WINNT
  879. SetTermsrvAppInstallMode(bPrevMode);
  880. #endif
  881. SetCursor(hcur);
  882. }
  883. }
  884. break;
  885. case APPCMD_ADDLATER:
  886. if (dwActions & APPACTION_ADDLATER)
  887. {
  888. ADDLATERDATA ald = {0};
  889. if (_pai.dwMask & PAI_SCHEDULEDTIME)
  890. {
  891. ald.stSchedule = _pai.stScheduled;
  892. ald.dwMasks |= ALD_SCHEDULE;
  893. }
  894. if (_pai.dwMask & PAI_ASSIGNEDTIME)
  895. {
  896. ald.stAssigned = _pai.stAssigned;
  897. ald.dwMasks |= ALD_ASSIGNED;
  898. }
  899. if (_pai.dwMask & PAI_EXPIRETIME)
  900. {
  901. ald.stExpire = _pai.stExpire;
  902. ald.dwMasks |= ALD_EXPIRE;
  903. }
  904. if (GetNewInstallTime(hwndParent, &ald))
  905. {
  906. if (ald.dwMasks & ALD_SCHEDULE)
  907. hres = _ppa->Install(&ald.stSchedule);
  908. else
  909. hres = _ppa->Unschedule();
  910. }
  911. }
  912. default:
  913. TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd));
  914. break;
  915. }
  916. break;
  917. case ENUM_OCSETUP:
  918. switch (appcmd)
  919. {
  920. case APPCMD_INSTALL:
  921. // call some command that "installs" the selected item.
  922. _pocsa->Run();
  923. hres = S_OK;
  924. break;
  925. default:
  926. TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd));
  927. break;
  928. }
  929. break;
  930. case ENUM_CATEGORIES:
  931. TraceMsg(TF_WARNING, "(Ctl) tried to exec appcmd %s on a category. Not supported.", Dbg_GetAppCmd(appcmd));
  932. break;
  933. }
  934. return hres;
  935. }
  936. //---------------------------------------------------------------------------
  937. // Matrix Array class
  938. //---------------------------------------------------------------------------
  939. // constructor
  940. CMtxArray2::CMtxArray2()
  941. {
  942. ASSERT(NULL == _hdpa);
  943. ASSERT(0 == _cRefLock);
  944. TraceMsg(TF_OBJLIFE, "(MtxArray) creating");
  945. InitializeCriticalSection(&_cs);
  946. }
  947. // destructor
  948. CMtxArray2::~CMtxArray2()
  949. {
  950. DBROWCOUNT cItems;
  951. ASSERT(0 == _cRefLock);
  952. TraceMsg(TF_OBJLIFE, "(MtxArray) destroying");
  953. GetItemCount(&cItems);
  954. DeleteItems(0, cItems);
  955. if (_hdpa)
  956. DPA_Destroy(_hdpa);
  957. DeleteCriticalSection(&_cs);
  958. }
  959. /*--------------------------------------------------------------------
  960. Purpose: IUnknown::QueryInterface
  961. */
  962. STDMETHODIMP CMtxArray2::QueryInterface(REFIID riid, LPVOID * ppvObj)
  963. {
  964. static const QITAB qit[] = {
  965. QITABENT(CMtxArray2, IMtxArray),
  966. { 0 },
  967. };
  968. HRESULT hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  969. if (FAILED(hres))
  970. hres = CWorkerThread::QueryInterface(riid, ppvObj);
  971. return hres;
  972. }
  973. void CMtxArray2::_Lock(void)
  974. {
  975. EnterCriticalSection(&_cs);
  976. DEBUG_CODE( _cRefLock++; )
  977. }
  978. void CMtxArray2::_Unlock(void)
  979. {
  980. DEBUG_CODE( _cRefLock--; )
  981. LeaveCriticalSection(&_cs);
  982. }
  983. /*-------------------------------------------------------------------------
  984. Purpose: Create array
  985. */
  986. HRESULT CMtxArray2::_CreateArray(void)
  987. {
  988. _Lock();
  989. if (NULL == _hdpa)
  990. _hdpa = DPA_Create(8);
  991. _Unlock();
  992. return _hdpa ? S_OK : E_OUTOFMEMORY;
  993. }
  994. HRESULT CMtxArray2::_DeleteItem(DBROWCOUNT idpa)
  995. {
  996. // The caller must enter the lock first
  997. ASSERT(0 < _cRefLock);
  998. ASSERT(_hdpa);
  999. IAppData * pappdata = (IAppData *)DPA_GetPtr(_hdpa, (LONG)idpa);
  1000. if (pappdata)
  1001. pappdata->Release();
  1002. DPA_DeletePtr(_hdpa, (LONG)idpa);
  1003. return S_OK;
  1004. }
  1005. /*-------------------------------------------------------------------------
  1006. Purpose: IMtxArray::Initialize
  1007. */
  1008. STDMETHODIMP CMtxArray2::Initialize(DWORD dwEnum)
  1009. {
  1010. _dwEnum = dwEnum;
  1011. return S_OK;
  1012. }
  1013. /*-------------------------------------------------------------------------
  1014. Purpose: IMtxArray::AddItem
  1015. Add an item to the array
  1016. Returns the row in which the record was added (0-based).
  1017. */
  1018. STDMETHODIMP CMtxArray2::AddItem(IAppData * pappdata, DBROWCOUNT * piRow)
  1019. {
  1020. HRESULT hres = E_FAIL;
  1021. ASSERT(pappdata);
  1022. ASSERT(NULL == piRow || IS_VALID_WRITE_PTR(piRow, DBROWCOUNT));
  1023. hres = _CreateArray();
  1024. if (SUCCEEDED(hres))
  1025. {
  1026. _Lock();
  1027. {
  1028. pappdata->AddRef(); // AddRef since we're storing it away
  1029. LONG iRow = DPA_AppendPtr(_hdpa, pappdata);
  1030. if (DPA_ERR != iRow)
  1031. {
  1032. DEBUG_CODE( TraceMsg(TF_DSO, "(CMtxArray) added record %d", iRow); )
  1033. pappdata->SetMtxParent(SAFECAST(this, IMtxArray *));
  1034. hres = S_OK;
  1035. }
  1036. else
  1037. {
  1038. pappdata->Release();
  1039. hres = E_OUTOFMEMORY;
  1040. iRow = -1;
  1041. }
  1042. if (piRow)
  1043. *piRow = iRow;
  1044. }
  1045. _Unlock();
  1046. }
  1047. return hres;
  1048. }
  1049. /*-------------------------------------------------------------------------
  1050. Purpose: IMtxArray::DeleteItems
  1051. Delete a group of records (count of cRows) starting at iRow.
  1052. Assumes iRow is 0-based.
  1053. */
  1054. STDMETHODIMP CMtxArray2::DeleteItems(DBROWCOUNT idpa, DBROWCOUNT cdpa)
  1055. {
  1056. DBROWCOUNT i;
  1057. if (_hdpa)
  1058. {
  1059. _Lock();
  1060. {
  1061. ASSERT(IS_VALID_HANDLE(_hdpa, DPA));
  1062. ASSERT(IsInRange(idpa, 0, DPA_GetPtrCount(_hdpa)));
  1063. for (i = idpa + cdpa - 1; i >= idpa; i--)
  1064. {
  1065. _DeleteItem(i);
  1066. }
  1067. }
  1068. _Unlock();
  1069. }
  1070. return S_OK;
  1071. }
  1072. /*-------------------------------------------------------------------------
  1073. Purpose: IMtxArray::GetAppData
  1074. Return a pointer to the appdata element of the given row. The
  1075. caller must Release the returned appdata.
  1076. */
  1077. STDMETHODIMP CMtxArray2::GetAppData(DBROWCOUNT iRow, IAppData ** ppappdata)
  1078. {
  1079. HRESULT hres = E_FAIL;
  1080. IAppData * pappdata = NULL;
  1081. if (_hdpa)
  1082. {
  1083. _Lock();
  1084. pappdata = (IAppData *)DPA_GetPtr(_hdpa, iRow);
  1085. if (pappdata)
  1086. {
  1087. pappdata->AddRef();
  1088. hres = S_OK;
  1089. }
  1090. _Unlock();
  1091. }
  1092. *ppappdata = pappdata;
  1093. return hres;
  1094. }
  1095. /*-------------------------------------------------------------------------
  1096. Purpose: IMtxArray::GetItemCount
  1097. Return the count of items in the array.
  1098. */
  1099. STDMETHODIMP CMtxArray2::GetItemCount(DBROWCOUNT * pcItems)
  1100. {
  1101. if (_hdpa)
  1102. *pcItems = DPA_GetPtrCount(_hdpa);
  1103. else
  1104. *pcItems = 0;
  1105. return S_OK;
  1106. }
  1107. /*-------------------------------------------------------------------------
  1108. Purpose: IMtxArray::GetFieldCount
  1109. Return the count of fields based upon what we are enumerating.
  1110. */
  1111. STDMETHODIMP CMtxArray2::GetFieldCount(DB_LORDINAL * pcFields)
  1112. {
  1113. DB_LORDINAL lRet = 0;
  1114. switch (_dwEnum)
  1115. {
  1116. case ENUM_INSTALLED:
  1117. lRet = ARRAYSIZE(g_rginstfields);
  1118. break;
  1119. case ENUM_PUBLISHED:
  1120. lRet = ARRAYSIZE(g_rgpubfields);
  1121. break;
  1122. case ENUM_OCSETUP:
  1123. lRet = ARRAYSIZE(g_rgsetupfields);
  1124. break;
  1125. case ENUM_CATEGORIES:
  1126. lRet = ARRAYSIZE(g_rgcatfields);
  1127. break;
  1128. }
  1129. *pcFields = lRet;
  1130. return S_OK;
  1131. }
  1132. /*-------------------------------------------------------------------------
  1133. Purpose: IMtxArray::GetFieldName
  1134. Return the field name.
  1135. */
  1136. STDMETHODIMP CMtxArray2::GetFieldName(DB_LORDINAL iField, VARIANT * pvar)
  1137. {
  1138. HRESULT hres = E_INVALIDARG;
  1139. DB_LORDINAL cfields = 0;
  1140. const APPFIELDS *rgfield;
  1141. VariantInit(pvar);
  1142. switch (_dwEnum)
  1143. {
  1144. case ENUM_INSTALLED:
  1145. cfields = ARRAYSIZE(g_rginstfields);
  1146. rgfield = g_rginstfields;
  1147. break;
  1148. case ENUM_PUBLISHED:
  1149. cfields = ARRAYSIZE(g_rgpubfields);
  1150. rgfield = g_rgpubfields;
  1151. break;
  1152. case ENUM_OCSETUP:
  1153. cfields = ARRAYSIZE(g_rgsetupfields);
  1154. rgfield = g_rgsetupfields;
  1155. break;
  1156. case ENUM_CATEGORIES:
  1157. cfields = ARRAYSIZE(g_rgcatfields);
  1158. rgfield = g_rgcatfields;
  1159. break;
  1160. }
  1161. if (IsInRange(iField, 0, cfields))
  1162. {
  1163. pvar->bstrVal = SysAllocString(rgfield[iField].pszLabel);
  1164. if (pvar->bstrVal)
  1165. {
  1166. pvar->vt = VT_BSTR;
  1167. hres = S_OK;
  1168. }
  1169. else
  1170. hres = E_OUTOFMEMORY;
  1171. }
  1172. return hres;
  1173. }
  1174. /*-------------------------------------------------------------------------
  1175. Purpose: IMtxArray:GetSortIndex
  1176. */
  1177. STDMETHODIMP CMtxArray2::GetSortIndex(DWORD * pdwSort)
  1178. {
  1179. ASSERT(pdwSort);
  1180. *pdwSort = _dwSort;
  1181. return S_OK;
  1182. }
  1183. /*-------------------------------------------------------------------------
  1184. Purpose: IMtxArray::SetSortCriteria
  1185. Determines which field to sort by depending on the given criteria.
  1186. */
  1187. STDMETHODIMP CMtxArray2::SetSortCriteria(LPCWSTR pszSortField)
  1188. {
  1189. int i;
  1190. int cfields;
  1191. const APPFIELDS * pfield;
  1192. ASSERT(IS_VALID_STRING_PTRW(pszSortField, -1));
  1193. switch (_dwEnum)
  1194. {
  1195. case ENUM_INSTALLED:
  1196. cfields = ARRAYSIZE(g_rginstfields);
  1197. pfield = g_rginstfields;
  1198. break;
  1199. case ENUM_PUBLISHED:
  1200. cfields = ARRAYSIZE(g_rgpubfields);
  1201. pfield = g_rgpubfields;
  1202. break;
  1203. case ENUM_OCSETUP:
  1204. cfields = ARRAYSIZE(g_rgsetupfields);
  1205. pfield = g_rgsetupfields;
  1206. break;
  1207. case ENUM_CATEGORIES:
  1208. cfields = ARRAYSIZE(g_rgcatfields);
  1209. pfield = g_rgcatfields;
  1210. break;
  1211. }
  1212. // Find the given field's sort index
  1213. for (i = 0; i < cfields; i++, pfield++)
  1214. {
  1215. if (0 == StrCmpW(pszSortField, pfield->pszLabel))
  1216. {
  1217. // Can this field be sorted?
  1218. if (SORT_NA != pfield->dwSort)
  1219. {
  1220. // Yes
  1221. _dwSort = pfield->dwSort;
  1222. }
  1223. break;
  1224. }
  1225. }
  1226. return S_OK;
  1227. }
  1228. /*-------------------------------------------------------------------------
  1229. Purpose: Static callback function to handle sorting. lParam is the
  1230. pointer to the IMtxArray.
  1231. */
  1232. int CMtxArray2::s_SortItemsCallbackWrapper(LPVOID pv1, LPVOID pv2, LPARAM lParam)
  1233. {
  1234. IMtxArray * pmtxarray = (IMtxArray *)lParam;
  1235. ASSERT(pmtxarray);
  1236. return pmtxarray->CompareItems((IAppData *)pv1, (IAppData *)pv2);
  1237. }
  1238. /*-------------------------------------------------------------------------
  1239. Purpose: IMtxArray::CompareItems
  1240. Compares two appdata objects according to the current sort index.
  1241. */
  1242. STDMETHODIMP_(int) CMtxArray2::CompareItems(IAppData * pappdata1, IAppData * pappdata2)
  1243. {
  1244. switch (_dwSort)
  1245. {
  1246. case SORT_SIZE:
  1247. {
  1248. ULONGLONG ull1 = pappdata1->GetSlowDataPtr()->ullSize;
  1249. ULONGLONG ull2 = pappdata2->GetSlowDataPtr()->ullSize;
  1250. // Big apps come before smaller apps
  1251. if (ull1 > ull2)
  1252. return -1;
  1253. else if (ull1 < ull2)
  1254. return 1;
  1255. goto sort_by_name;
  1256. }
  1257. break;
  1258. case SORT_TIMESUSED:
  1259. {
  1260. // Rarely used apps come before frequently used apps. Blank
  1261. // (unknown) apps go last. Unknown apps are -1, so those sort
  1262. // to the bottom if we simply compare unsigned values.
  1263. UINT u1 = (UINT)pappdata1->GetSlowDataPtr()->iTimesUsed;
  1264. UINT u2 = (UINT)pappdata2->GetSlowDataPtr()->iTimesUsed;
  1265. if (u1 < u2)
  1266. return -1;
  1267. else if (u1 > u2)
  1268. return 1;
  1269. goto sort_by_name;
  1270. }
  1271. break;
  1272. case SORT_LASTUSED:
  1273. {
  1274. FILETIME ft1 = pappdata1->GetSlowDataPtr()->ftLastUsed;
  1275. FILETIME ft2 = pappdata2->GetSlowDataPtr()->ftLastUsed;
  1276. LONG diff = CompareFileTime(&ft1, &ft2);
  1277. if (0 != diff)
  1278. return diff;
  1279. goto sort_by_name;
  1280. }
  1281. break;
  1282. sort_by_name:
  1283. case SORT_NAME:
  1284. {
  1285. LPWSTR pszName1 = pappdata1->GetDataPtr()->pszDisplayName;
  1286. LPWSTR pszName2 = pappdata2->GetDataPtr()->pszDisplayName;
  1287. if (pszName1 && pszName2)
  1288. return StrCmpW(pszName1, pszName2);
  1289. else if (pszName1)
  1290. return 1;
  1291. else if (pszName2)
  1292. return -1;
  1293. }
  1294. break;
  1295. }
  1296. return 0;
  1297. }
  1298. /*-------------------------------------------------------------------------
  1299. Purpose: IMtxArray::SortItems
  1300. Sorts the array according to the current sort index.
  1301. */
  1302. STDMETHODIMP CMtxArray2::SortItems(void)
  1303. {
  1304. if (_hdpa)
  1305. {
  1306. _Lock();
  1307. {
  1308. ASSERT(IS_VALID_HANDLE(_hdpa, DPA));
  1309. DPA_Sort(_hdpa, s_SortItemsCallbackWrapper, (LPARAM)this);
  1310. }
  1311. _Unlock();
  1312. }
  1313. return S_OK;
  1314. }
  1315. /*-------------------------------------------------------------------------
  1316. Purpose: IMtxArray::MarkDupEntries
  1317. Mark the (name) duplicate entries in the DPA array of app data
  1318. */
  1319. STDMETHODIMP CMtxArray2::MarkDupEntries(void)
  1320. {
  1321. if (_hdpa)
  1322. {
  1323. _Lock();
  1324. {
  1325. int idpa;
  1326. for (idpa = 0; idpa < DPA_GetPtrCount(_hdpa) - 1; idpa++)
  1327. {
  1328. IAppData * pad = (IAppData *)DPA_GetPtr(_hdpa, idpa);
  1329. IAppData * padNext = (IAppData *)DPA_GetPtr(_hdpa, idpa+1);
  1330. LPWSTR pszName = pad->GetDataPtr()->pszDisplayName;
  1331. LPWSTR pszNameNext = padNext->GetDataPtr()->pszDisplayName;
  1332. if (pszName && pszNameNext && !StrCmpW(pszName, pszNameNext))
  1333. {
  1334. pad->SetNameDupe(TRUE);
  1335. padNext->SetNameDupe(TRUE);
  1336. }
  1337. }
  1338. }
  1339. _Unlock();
  1340. }
  1341. return S_OK;
  1342. }
  1343. /*-------------------------------------------------------------------------
  1344. Purpose: IMtxArray::KillWT
  1345. Kills the worker thread.
  1346. */
  1347. STDMETHODIMP CMtxArray2::KillWT()
  1348. {
  1349. return CWorkerThread::KillWT();
  1350. }
  1351. /*----------------------------------------------------------
  1352. Purpose: Create-instance function for CMtxArray
  1353. */
  1354. HRESULT CMtxArray_CreateInstance(REFIID riid, LPVOID * ppvObj)
  1355. {
  1356. HRESULT hres = E_OUTOFMEMORY;
  1357. *ppvObj = NULL;
  1358. CMtxArray2 * pObj = new CMtxArray2();
  1359. if (pObj)
  1360. {
  1361. hres = pObj->QueryInterface(riid, ppvObj);
  1362. pObj->Release();
  1363. }
  1364. return hres;
  1365. }
  1366. /*-------------------------------------------------------------------------
  1367. Purpose: IMtxArray::_ThreadStartProc()
  1368. */
  1369. DWORD CMtxArray2::_ThreadStartProc()
  1370. {
  1371. DBROWCOUNT i;
  1372. TraceMsg(TF_TASKS, "[%x] Starting slow app info thread", _dwThreadId);
  1373. DBROWCOUNT cItems = 0;
  1374. GetItemCount(&cItems);
  1375. // Loop through all enumerated items, getting the 'slow' information
  1376. for (i = 0 ; i < cItems ; i++)
  1377. {
  1378. IAppData * pappdata;
  1379. // If we've been asked to bail, do so
  1380. if (IsKilled())
  1381. break;
  1382. GetAppData(i, &pappdata);
  1383. if (pappdata)
  1384. {
  1385. HRESULT hres = pappdata->ReadSlowData();
  1386. // Call to get the slow info for the item. Fire an event on success
  1387. if (hres == S_OK)
  1388. {
  1389. PostWorkerMessage(WORKERWIN_FIRE_ROW_READY, i, 0);
  1390. TraceMsg(TF_TASKS, "[%x] Slow info for %d (%ls): hit", _dwThreadId, i,
  1391. pappdata->GetDataPtr()->pszDisplayName);
  1392. }
  1393. else
  1394. {
  1395. TraceMsg(TF_TASKS, "[%x] Slow info for %d (%ls): miss", _dwThreadId, i,
  1396. pappdata->GetDataPtr()->pszDisplayName);
  1397. }
  1398. pappdata->Release();
  1399. }
  1400. }
  1401. PostWorkerMessage(WORKERWIN_FIRE_FINISHED, 0, 0);
  1402. return CWorkerThread::_ThreadStartProc();
  1403. }
  1404. #endif //DOWNLEVEL_PLATFORM