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.

1419 lines
40 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Add/Remove Programs Data Source Object
  4. //
  5. //------------------------------------------------------------------------
  6. #include "priv.h"
  7. // Do not build this file if on Win9X or NT4
  8. #ifndef DOWNLEVEL_PLATFORM
  9. #include "datasrc.h"
  10. #include "dump.h"
  11. #include "util.h"
  12. //---------------------------------------------------------------------------
  13. //
  14. //---------------------------------------------------------------------------
  15. // constructor
  16. CDataSrc::CDataSrc()
  17. {
  18. TraceMsg(TF_OBJLIFE, "(Mtx) creating");
  19. TraceAddRef(CDataSrc, _cRef);
  20. ASSERT(NULL == _parpevt);
  21. ASSERT(NULL == _pmtxarray);
  22. ASSERT(NULL == _psam);
  23. ASSERT(FALSE == _fAppsEnumed);
  24. ASSERT(FALSE == _fInEnumOp);
  25. _loadstate = LS_NOTSTARTED;
  26. }
  27. // destructor
  28. CDataSrc::~CDataSrc()
  29. {
  30. TraceMsg(TF_OBJLIFE, "(Mtx) destroying");
  31. ATOMICRELEASE(_pmtxarray);
  32. ATOMICRELEASE(_parpevt);
  33. ATOMICRELEASE(_psam);
  34. }
  35. /*--------------------------------------------------------------------
  36. Purpose: IUnknown::QueryInterface
  37. */
  38. STDMETHODIMP CDataSrc::QueryInterface(REFIID riid, LPVOID * ppvObj)
  39. {
  40. static const QITAB qit[] = {
  41. QITABENT(CDataSrc, IARPSimpleProvider),
  42. QITABENT(CDataSrc, OLEDBSimpleProvider),
  43. QITABENT(CDataSrc, ISequentialStream),
  44. QITABENT(CDataSrc, IWorkerEvent),
  45. { 0 },
  46. };
  47. HRESULT hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  48. if (FAILED(hres))
  49. hres = CWorkerThread::QueryInterface(riid, ppvObj);
  50. return hres;
  51. }
  52. /*--------------------------------------------------------------------
  53. Purpose: IARPWorker::KillWT
  54. Kills the worker thread that enumerates apps
  55. */
  56. STDMETHODIMP CDataSrc::KillWT()
  57. {
  58. // Primary thread wants to kill us, this means we are about to be released
  59. // also kill the mtxarray thread here, because that kill has to be on the main thread, too.
  60. // And we can't depend on CDataSrc descrutor to do it (because that final release could be called on the
  61. // back groud thread)
  62. _KillMtxWorkerThread();
  63. return CWorkerThread::KillWT();
  64. }
  65. /*-------------------------------------------------------------------------
  66. Purpose: IWorkerEvent::FireOnDataReady
  67. Called by worker thread when some data is ready.
  68. */
  69. STDMETHODIMP
  70. CDataSrc::FireOnDataReady(
  71. DBROWCOUNT iRow
  72. )
  73. {
  74. // OSP listener expects row to be 1-based
  75. _parpevt->RowChanged(iRow + 1);
  76. return S_OK;
  77. }
  78. /*-------------------------------------------------------------------------
  79. Purpose: IWorkerEvent::FireOnFinished
  80. Called by worker thread when it is complete.
  81. */
  82. STDMETHODIMP
  83. CDataSrc::FireOnFinished(void)
  84. {
  85. _loadstate = LS_DONE;
  86. return S_OK;
  87. }
  88. /*-------------------------------------------------------------------------
  89. Purpose: IWorkerEvent::FireOnDatasetChanged
  90. Called by worker thread when it is complete.
  91. */
  92. STDMETHODIMP
  93. CDataSrc::FireOnDatasetChanged(void)
  94. {
  95. if (_parpevt)
  96. _parpevt->DataSetChanged();
  97. return S_OK;
  98. }
  99. // CDataSrc::_CalcRows
  100. // Calculate the number of rows in the OSP
  101. DBROWCOUNT CDataSrc::_CalcRows(void)
  102. {
  103. DBROWCOUNT lRet = 0;
  104. if (_pmtxarray)
  105. _pmtxarray->GetItemCount(&lRet);
  106. return lRet;
  107. }
  108. // CDataSrc::_CalcCols
  109. // Calculate the number of columns in the OSP
  110. DB_LORDINAL CDataSrc::_CalcCols(void)
  111. {
  112. DB_LORDINAL lRet = 0;
  113. if (_pmtxarray)
  114. _pmtxarray->GetFieldCount(&lRet);
  115. return lRet;
  116. }
  117. inline BOOL CDataSrc::_IsValidDataRow(DBROWCOUNT iRow)
  118. {
  119. // Rows are 1-based. The 0th row refers to label information.
  120. // -1 means wildcard.
  121. // The 0th row is NOT a valid data row.
  122. return (iRow > 0 && iRow <= _cRows);
  123. }
  124. inline BOOL CDataSrc::_IsValidRow(DBROWCOUNT iRow)
  125. {
  126. // Rows are 1-based. The 0th row refers to label information.
  127. // -1 means wildcard.
  128. return (iRow >= 0 && iRow <= _cRows);
  129. }
  130. inline BOOL CDataSrc::_IsValidCol(DB_LORDINAL iCol)
  131. {
  132. // Columns are 1-based. The 0th column refers to header information.
  133. // -1 means wildcard.
  134. return (iCol >= 1 && iCol <= _cCols);
  135. }
  136. inline BOOL CDataSrc::_IsValidCell(DBROWCOUNT iRow, DB_LORDINAL iCol)
  137. {
  138. return _IsValidRow(iRow) && _IsValidCol(iCol);
  139. }
  140. /*-------------------------------------------------------------------------
  141. Purpose: Returns the appdata object of the given row (1-based). Returns
  142. NULL if there is none.
  143. */
  144. IAppData * CDataSrc::_GetAppData(DBROWCOUNT iRow)
  145. {
  146. IAppData * pappdata = NULL;
  147. if (_pmtxarray)
  148. {
  149. ASSERT(0 < iRow && iRow <= _cRows);
  150. _pmtxarray->GetAppData(iRow-1, &pappdata);
  151. }
  152. return pappdata;
  153. }
  154. // Structure used to transfer matrix object thru ISequentialStream()
  155. typedef struct tagARPDSODATA
  156. {
  157. LOAD_STATE loadstate;
  158. DB_LORDINAL cCols; // count of columns
  159. DBROWCOUNT cRows; // count of rows
  160. DWORD dwEnum; // items to enumerate (ENUM_*)
  161. IMtxArray * pmtxarray; // data is stored here
  162. BSTR bstrSort; // sort string
  163. } ARPDSODATA;
  164. /*-------------------------------------------------------------------------
  165. Purpose: ISequentialStream::Read
  166. Return the matrix object of this datasource object.
  167. IARPSimpleProvider::TransferData uses this method.
  168. */
  169. STDMETHODIMP CDataSrc::Read(void * pvData, ULONG cbData, ULONG * pcbRead)
  170. {
  171. HRESULT hres = E_INVALIDARG;
  172. ASSERT(IS_VALID_WRITE_BUFFER(pvData, BYTE, cbData));
  173. ASSERT(NULL == pcbRead || IS_VALID_WRITE_PTR(pcbRead, ULONG));
  174. if (pvData)
  175. {
  176. ARPDSODATA * pdsodata = (ARPDSODATA *)pvData;
  177. if (pcbRead)
  178. *pcbRead = 0;
  179. if (sizeof(*pdsodata) <= cbData)
  180. {
  181. pdsodata->loadstate = _loadstate;
  182. pdsodata->cCols = _cCols;
  183. pdsodata->cRows = _cRows;
  184. pdsodata->dwEnum = _dwEnum;
  185. pdsodata->pmtxarray = _pmtxarray;
  186. if (_pmtxarray)
  187. _pmtxarray->AddRef();
  188. pdsodata->bstrSort = _cbstrSort.Copy();
  189. if (pcbRead)
  190. *pcbRead = sizeof(*pdsodata);
  191. }
  192. hres = S_OK;
  193. }
  194. return hres;
  195. }
  196. /*-------------------------------------------------------------------------
  197. Purpose: ISequentialStream::Write
  198. Set the matrix object of this datasource object.
  199. IARPSimpleProvider::TransferData uses this method.
  200. */
  201. STDMETHODIMP CDataSrc::Write(void const * pvData, ULONG cbData, ULONG * pcbWritten)
  202. {
  203. HRESULT hres = E_INVALIDARG;
  204. ASSERT(IS_VALID_READ_BUFFER(pvData, BYTE, cbData));
  205. ASSERT(NULL == pcbWritten || IS_VALID_WRITE_PTR(pcbWritten, ULONG));
  206. if (pvData)
  207. {
  208. ARPDSODATA * pdsodata = (ARPDSODATA *)pvData;
  209. if (pcbWritten)
  210. *pcbWritten = 0;
  211. if (sizeof(*pdsodata) <= cbData)
  212. {
  213. _loadstate = pdsodata->loadstate;
  214. _cCols = pdsodata->cCols;
  215. _cRows = pdsodata->cRows;
  216. _dwEnum = pdsodata->dwEnum;
  217. // We won't addref this, since the supplier should have done that.
  218. _pmtxarray = pdsodata->pmtxarray;
  219. _cbstrSort.Empty();
  220. _cbstrSort.Attach(pdsodata->bstrSort);
  221. if (pcbWritten)
  222. *pcbWritten = sizeof(*pdsodata);
  223. }
  224. hres = S_OK;
  225. }
  226. return hres;
  227. }
  228. /*-------------------------------------------------------------------------
  229. Purpose: IARPSimpleProvider::Initialize
  230. Must be called before enumerating items.
  231. */
  232. STDMETHODIMP CDataSrc::Initialize(IShellAppManager * psam, IARPEvent * parpevt, DWORD dwEnum)
  233. {
  234. ASSERT(psam);
  235. ASSERT(IS_VALID_CODE_PTR(parpevt, CEventBroker));
  236. ATOMICRELEASE(_psam);
  237. ATOMICRELEASE(_parpevt);
  238. _psam = psam;
  239. _psam->AddRef();
  240. _parpevt = parpevt;
  241. _parpevt->AddRef();
  242. _dwEnum = dwEnum;
  243. return S_OK;
  244. }
  245. HRESULT CDataSrc::_EnumAppItems(DWORD dwEnum, LPCWSTR pszCategory)
  246. {
  247. HRESULT hres = E_INVALIDARG;
  248. IInstalledApp* pAppIns;
  249. CAppData* pcad;
  250. ASSERT(NULL == pszCategory || IS_VALID_STRING_PTRW(pszCategory, -1));
  251. switch (dwEnum)
  252. {
  253. case ENUM_INSTALLED:
  254. IEnumInstalledApps* pEnumIns;
  255. // Now that we have the object, start enumerating the items
  256. hres = THR(_psam->EnumInstalledApps(&pEnumIns));
  257. if (SUCCEEDED(hres))
  258. {
  259. // Loop through all the apps on the machine, building our table
  260. while (S_OK == pEnumIns->Next(&pAppIns))
  261. {
  262. // If we've been asked to bail, do so
  263. if (IsKilled())
  264. {
  265. pAppIns->Release();
  266. break;
  267. }
  268. APPINFODATA ai = {0};
  269. // Get the 'fast' app info from the app manager object
  270. ai.cbSize = sizeof(ai);
  271. ai.dwMask = AIM_DISPLAYNAME | AIM_VERSION | AIM_PUBLISHER | AIM_PRODUCTID | AIM_REGISTEREDOWNER
  272. | AIM_REGISTEREDCOMPANY | AIM_SUPPORTURL | AIM_SUPPORTTELEPHONE | AIM_HELPLINK
  273. | AIM_INSTALLLOCATION | AIM_INSTALLDATE | AIM_COMMENTS | AIM_IMAGE
  274. | AIM_READMEURL | AIM_CONTACT | AIM_UPDATEINFOURL;
  275. if (SUCCEEDED(pAppIns->GetAppInfo(&ai)) &&
  276. lstrlen(ai.pszDisplayName) > 0)
  277. {
  278. SLOWAPPINFO sai = {0};
  279. pAppIns->GetCachedSlowAppInfo(&sai);
  280. // Now save all this information away
  281. pcad = new CAppData(pAppIns, &ai, &sai);
  282. if (pcad)
  283. {
  284. _pmtxarray->AddItem(pcad, NULL);
  285. pcad->Release();
  286. }
  287. else
  288. {
  289. // Something failed
  290. pAppIns->Release();
  291. ClearAppInfoData(&ai);
  292. }
  293. }
  294. // NOTE: we do NOT release the pointer (pAppIns) here,
  295. // its lifetime is passed to the CAppData object
  296. }
  297. pEnumIns->Release();
  298. hres = S_OK;
  299. }
  300. break;
  301. case ENUM_PUBLISHED:
  302. IEnumPublishedApps * pepa; // Salt 'n...
  303. // Convert an empty string to a null string if we need to
  304. if (pszCategory && 0 == *pszCategory)
  305. pszCategory = NULL;
  306. // Enumerate published apps
  307. hres = THR(_psam->EnumPublishedApps(pszCategory, &pepa));
  308. if (SUCCEEDED(hres))
  309. {
  310. IPublishedApp * ppa;
  311. while (S_OK == pepa->Next(&ppa))
  312. {
  313. // If we've been asked to bail, do so
  314. if (IsKilled())
  315. {
  316. ppa->Release();
  317. break;
  318. }
  319. APPINFODATA ai = {0};
  320. // Get the 'fast' app info from the app manager object
  321. ai.cbSize = sizeof(ai);
  322. ai.dwMask = AIM_DISPLAYNAME | AIM_VERSION | AIM_PUBLISHER | AIM_PRODUCTID | AIM_REGISTEREDOWNER
  323. | AIM_REGISTEREDCOMPANY | AIM_SUPPORTURL | AIM_SUPPORTTELEPHONE | AIM_HELPLINK
  324. | AIM_INSTALLLOCATION | AIM_INSTALLDATE | AIM_COMMENTS | AIM_IMAGE;
  325. if (SUCCEEDED(ppa->GetAppInfo(&ai)) &&
  326. lstrlen(ai.pszDisplayName) > 0)
  327. {
  328. PUBAPPINFO pai = {0};
  329. pai.cbSize = sizeof(pai);
  330. pai.dwMask = PAI_SOURCE | PAI_ASSIGNEDTIME | PAI_PUBLISHEDTIME | PAI_EXPIRETIME | PAI_SCHEDULEDTIME;
  331. ppa->GetPublishedAppInfo(&pai);
  332. // Now save all this information away
  333. pcad = new CAppData(ppa, &ai, &pai);
  334. if (pcad)
  335. {
  336. _pmtxarray->AddItem(pcad, NULL);
  337. pcad->Release();
  338. }
  339. else
  340. {
  341. // Something failed
  342. ppa->Release();
  343. ClearAppInfoData(&ai);
  344. ClearPubAppInfo(&pai);
  345. }
  346. }
  347. // NOTE: we do NOT release the pointer (ppa) here,
  348. // its lifetime is passed to the CAppData object
  349. }
  350. pepa->Release();
  351. hres = S_OK;
  352. }
  353. break;
  354. case ENUM_OCSETUP:
  355. // Create an object that enums the OCSetup items
  356. COCSetupEnum * pocse;
  357. pocse = new COCSetupEnum;
  358. if ( pocse && pocse->EnumOCSetupItems() )
  359. {
  360. COCSetupApp * pocsa;
  361. while ( pocse->Next(&pocsa) )
  362. {
  363. // If we've been asked to bail, do so
  364. if (IsKilled())
  365. {
  366. delete pocsa;
  367. break;
  368. }
  369. // REVIEW: Is it worth it to use an APPINFODATA structure? COcSetupApp
  370. // doesn't need this structure but I think it buys us sorting once inside
  371. // the CAppData array as well as a free implementation of the get_DisplayName
  372. // property which can be accessed via script. The data sorting might be
  373. // important but it might also be worth it to special case that ability.
  374. APPINFODATA ai = {0};
  375. ai.cbSize = sizeof(ai);
  376. ai.dwMask = AIM_DISPLAYNAME;
  377. if ( pocsa->GetAppInfo(&ai) && (lstrlen(ai.pszDisplayName) > 0) )
  378. {
  379. // Now save all this information away
  380. pcad = new CAppData(pocsa, &ai);
  381. if (pcad)
  382. {
  383. _pmtxarray->AddItem(pcad, NULL);
  384. pcad->Release();
  385. }
  386. else
  387. {
  388. // Something failed
  389. delete pocsa;
  390. ClearAppInfoData(&ai);
  391. }
  392. }
  393. // NOTE: we do NOT release the pointer (pocsa) here,
  394. // its lifetime is passed to the CAppData object
  395. }
  396. }
  397. hres = S_OK;
  398. break;
  399. case ENUM_CATEGORIES:
  400. SHELLAPPCATEGORYLIST sacl = {0};
  401. // Get the list of categories
  402. hres = _psam->GetPublishedAppCategories(&sacl);
  403. if (SUCCEEDED(hres))
  404. {
  405. SHELLAPPCATEGORY * psac = sacl.pCategory;
  406. // If we've been asked to bail, do so
  407. if (IsKilled())
  408. {
  409. ReleaseShellCategory(psac);
  410. break;
  411. }
  412. UINT i;
  413. for (i = 0; i < sacl.cCategories; i++, psac++)
  414. {
  415. // Now save all this information away
  416. pcad = new CAppData(psac);
  417. if (pcad)
  418. {
  419. _pmtxarray->AddItem(pcad, NULL);
  420. pcad->Release();
  421. }
  422. else
  423. {
  424. // Something failed
  425. ReleaseShellCategory(psac);
  426. }
  427. }
  428. // NOTE: we do NOT release the pointer (sacl) here,
  429. // its lifetime is passed to the CAppData object
  430. }
  431. break;
  432. }
  433. return hres;
  434. }
  435. /*-------------------------------------------------------------------------
  436. Purpose: CDataSrc::_ThreadStartProc()
  437. The thread proc for the background thread that enumerates applications
  438. */
  439. DWORD CDataSrc::_ThreadStartProc()
  440. {
  441. TraceMsg(TF_TASKS, "[%x] Starting enumerator thread", _dwThreadId);
  442. // Enumerate the applications, this function does the real work
  443. _EnumAppItems(_dwEnum, _cbstrCategory);
  444. // Claim to the world that we are done
  445. _fAppsEnumed = TRUE;
  446. _fInEnumOp = FALSE;
  447. // Tell Trident that dataset has changed
  448. PostWorkerMessage(WORKERWIN_FIRE_DATASETCHANGED, 0, 0);
  449. // Call our base class and do clean up.
  450. return CWorkerThread::_ThreadStartProc();
  451. }
  452. /*-------------------------------------------------------------------------
  453. Purpose: IARPSimpleProvider::Recalculate
  454. Recalculate the number of rows and columns and apply the sorting criteria
  455. for installed apps, load it's slowappinfo.
  456. */
  457. STDMETHODIMP CDataSrc::Recalculate(void)
  458. {
  459. HRESULT hres = E_PENDING;
  460. if (_fAppsEnumed)
  461. {
  462. // Calculate the columns used and cache that away.
  463. _cCols = _CalcCols();
  464. _cRows = _CalcRows();
  465. // Presort the items according to the existing sort criteria
  466. _ApplySortCriteria(FALSE);
  467. if (0 < _cRows)
  468. _parpevt->RowsAvailable(0, _cRows);
  469. _parpevt->LoadCompleted();
  470. // We only get slow info for the installed apps
  471. if (ENUM_INSTALLED == _dwEnum)
  472. {
  473. _loadstate = LS_LOADING_SLOWINFO;
  474. // Create and kick off the worker thread
  475. IWorkerEvent * pwrkevt;
  476. IARPWorker * pmtxworker;
  477. QueryInterface(IID_IWorkerEvent, (LPVOID *)&pwrkevt);
  478. ASSERT(pwrkevt); // this should never fail
  479. hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker);
  480. if (SUCCEEDED(hres))
  481. {
  482. // Tell the worker thread to notify us
  483. pmtxworker->SetListenerWT(pwrkevt);
  484. hres = pmtxworker->StartWT(THREAD_PRIORITY_BELOW_NORMAL);
  485. pmtxworker->Release();
  486. }
  487. pwrkevt->Release();
  488. }
  489. else
  490. _loadstate = LS_DONE;
  491. hres = S_OK;
  492. }
  493. else
  494. {
  495. //
  496. // ISSUE-2000/09/01-BrianAu Watch this message.
  497. // This used to be an assert. Based on comments from the Trident devs
  498. // and from what I can glean from this code, the assert is unnecessary.
  499. // When the enumeration is complete we fire a 'dataMemberChanged' event which
  500. // results in Recalculate being called again. Since the _fAppsEnumed
  501. // flag is set only after enumeration is complete, any prior calls to
  502. // this function are harmless.
  503. //
  504. TraceMsg(TF_ALWAYS, "This function should only be called when app enumeration is done");
  505. }
  506. return hres;
  507. }
  508. /*-------------------------------------------------------------------------
  509. Purpose: IARPSimpleProvider::EnumerateItemsAsync
  510. Enumerate the app items asynchronously. This call returns
  511. when all the items have been enumerated. The caller should call
  512. Initialize first.
  513. */
  514. STDMETHODIMP CDataSrc::EnumerateItemsAsync(void)
  515. {
  516. HRESULT hres = S_OK;
  517. ASSERT(_parpevt); // Caller should have called Initialize() first
  518. ASSERT(_psam);
  519. if (!_fInEnumOp)
  520. {
  521. _fInEnumOp = TRUE;
  522. // Make sure the slow info worker thread isn't already running. Stop it if it is.
  523. _KillMtxWorkerThread();
  524. // If we already have a list, nuke it
  525. ATOMICRELEASE(_pmtxarray);
  526. hres = THR(CMtxArray_CreateInstance(IID_IMtxArray, (LPVOID *)&_pmtxarray));
  527. if (SUCCEEDED(hres))
  528. {
  529. _pmtxarray->Initialize(_dwEnum);
  530. // Start enumerating items
  531. SetListenerWT(this);
  532. // Can't AddRef and worker thread
  533. hres = THR(StartWT(THREAD_PRIORITY_NORMAL));
  534. }
  535. else
  536. // Let people try again.
  537. _fInEnumOp = FALSE;
  538. }
  539. else
  540. {
  541. // This function should only be called before any enumeration started
  542. ASSERTMSG(FALSE, "This function should only be called before any enumeration started");
  543. hres = E_PENDING;
  544. }
  545. return hres;
  546. }
  547. /*-------------------------------------------------------------------------
  548. Purpose: Sorts the data
  549. */
  550. HRESULT CDataSrc::_ApplySortCriteria(BOOL bFireDataSetChanged)
  551. {
  552. HRESULT hres = E_FAIL;
  553. if (_pmtxarray)
  554. {
  555. _pmtxarray->SetSortCriteria(_cbstrSort);
  556. hres = _pmtxarray->SortItems();
  557. if (SUCCEEDED(hres))
  558. {
  559. // Mark the duplicated name entries for published apps
  560. if ((ENUM_PUBLISHED == _dwEnum) && !StrCmpW(_cbstrSort, L"displayname"))
  561. _pmtxarray->MarkDupEntries();
  562. // Tell the databinding agent that our dataset changed
  563. if (bFireDataSetChanged)
  564. _parpevt->DataSetChanged();
  565. }
  566. }
  567. return hres;
  568. }
  569. /*-------------------------------------------------------------------------
  570. Purpose: IARPSimpleProvider::SetSortCriteria
  571. Set the sort criterion for the datasource.
  572. Returns S_OK if the sort criteria is different, S_FALSE if it is
  573. no different.
  574. bstrSortExpr Name of column to sort by ("" = no sorting)
  575. */
  576. STDMETHODIMP CDataSrc::SetSortCriteria(BSTR bstrSortExpr)
  577. {
  578. HRESULT hres = S_FALSE;
  579. // Is this a new sort criteria?
  580. if (NULL == (LPWSTR)_cbstrSort || 0 != StrCmpIW(bstrSortExpr, _cbstrSort))
  581. {
  582. // Yes
  583. _cbstrSort = bstrSortExpr;
  584. hres = S_OK;
  585. _fSortDirty = TRUE;
  586. }
  587. return hres;
  588. }
  589. /*-------------------------------------------------------------------------
  590. Purpose: IARPSimpleProvider::SetFilter
  591. Set the filter criterion for the datasource. Right now this only
  592. works for published apps, via a category.
  593. Returns S_OK if the filter criteria is different, S_FALSE if it is
  594. no different.
  595. bstrSortExpr Name of column to sort by ("" = no sorting)
  596. */
  597. STDMETHODIMP CDataSrc::SetFilter(BSTR bstrFilter)
  598. {
  599. HRESULT hres = S_FALSE;
  600. // Is this a new filter criteria?
  601. if (NULL == (LPWSTR)_cbstrCategory || 0 != StrCmpIW(bstrFilter, _cbstrCategory))
  602. {
  603. // Yes
  604. _cbstrCategory = bstrFilter;
  605. hres = S_OK;
  606. }
  607. return hres;
  608. }
  609. /*-------------------------------------------------------------------------
  610. Purpose: IARPSimpleProvider::Sort
  611. Initiates a sort operation if any of the changes invalidates the
  612. existing criteria.
  613. */
  614. STDMETHODIMP CDataSrc::Sort(void)
  615. {
  616. HRESULT hres = S_OK;
  617. if (_fSortDirty)
  618. {
  619. // Is the datasource started?
  620. if (LS_NOTSTARTED != _loadstate)
  621. {
  622. // Yes; we can apply the sort now
  623. hres = _ApplySortCriteria(TRUE);
  624. if (SUCCEEDED(hres))
  625. _fSortDirty = FALSE;
  626. }
  627. }
  628. return hres;
  629. }
  630. /*-------------------------------------------------------------------------
  631. Purpose: IARPSimpleProvider::TransferData
  632. Transfer the contents of given datasource object to this datasource.
  633. This is useful for operations that change the dataset in-place,
  634. like sorting.
  635. */
  636. STDMETHODIMP CDataSrc::TransferData(IARPSimpleProvider * parposp)
  637. {
  638. HRESULT hres;
  639. ISequentialStream * pstream;
  640. ASSERT(parposp);
  641. hres = parposp->QueryInterface(IID_ISequentialStream, (LPVOID *)&pstream);
  642. if (SUCCEEDED(hres))
  643. {
  644. IARPWorker * pmtxworker;
  645. ARPDSODATA dsodata;
  646. ULONG cb;
  647. // Transfer the state and data from that datasource to this
  648. // datasource.
  649. pstream->Read(&dsodata, sizeof(dsodata), &cb);
  650. Write(&dsodata, cb, NULL);
  651. if (_pmtxarray)
  652. {
  653. hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker);
  654. if (SUCCEEDED(hres))
  655. {
  656. // Tell the worker thread that this is the new datasource
  657. // object to receive events
  658. IWorkerEvent * pwrkevt;
  659. QueryInterface(IID_IWorkerEvent, (LPVOID *)&pwrkevt);
  660. ASSERT(pwrkevt); // this should never fail
  661. pmtxworker->SetListenerWT(pwrkevt);
  662. pmtxworker->Release();
  663. pwrkevt->Release();
  664. }
  665. _fAppsEnumed = TRUE;
  666. }
  667. pstream->Release();
  668. }
  669. return hres;
  670. }
  671. /*-------------------------------------------------------------------------
  672. Purpose: IARPSimpleProvider::DoCommand
  673. Commit a specific action on the record. Unlike standard
  674. ADO commands that affect a recordset, these commands
  675. are intended to be specific to managing the apps themselves
  676. (like installing or uninstalling).
  677. NOTE: this method is called indirectly via script.
  678. */
  679. STDMETHODIMP CDataSrc::DoCommand(HWND hwndParent, APPCMD appcmd, DBROWCOUNT iRow)
  680. {
  681. HRESULT hres = S_OK;
  682. IAppData * pappdata = _GetAppData(iRow);
  683. if (pappdata)
  684. {
  685. if (_IsValidDataRow(iRow))
  686. {
  687. hres = pappdata->DoCommand(hwndParent, appcmd);
  688. // Was the app succesfully uninstalled/changed/whatever?
  689. if (S_OK == hres)
  690. {
  691. // Yes
  692. DBROWCOUNT lDeleted;
  693. switch (appcmd)
  694. {
  695. case APPCMD_UNINSTALL:
  696. // Fire the event to the databinding agent
  697. deleteRows(iRow, 1, &lDeleted);
  698. break;
  699. case APPCMD_UPGRADE:
  700. case APPCMD_REPAIR:
  701. case APPCMD_MODIFY:
  702. case APPCMD_INSTALL:
  703. // Fire the event
  704. _parpevt->RowChanged(iRow);
  705. break;
  706. }
  707. }
  708. }
  709. pappdata->Release();
  710. }
  711. return hres;
  712. }
  713. /*----------------------------------------------------------
  714. Purpose: OLEDBSimpleProvider::getRowCount
  715. Return the number of rows in the table.
  716. */
  717. STDMETHODIMP CDataSrc::getRowCount(DBROWCOUNT *pcRows)
  718. {
  719. ASSERT(IS_VALID_WRITE_PTR(pcRows, DBROWCOUNT));
  720. *pcRows = _cRows;
  721. TraceMsg(TF_DSO, "(Mtx) getRowCount returning %d", _cRows);
  722. return S_OK;
  723. }
  724. /*----------------------------------------------------------
  725. Purpose: OLEDBSimpleProvider::getColumnCount
  726. Return the number of columns in the table.
  727. */
  728. STDMETHODIMP CDataSrc::getColumnCount(DB_LORDINAL *pcCols)
  729. {
  730. ASSERT(IS_VALID_WRITE_PTR(pcCols, DB_LORDINAL));
  731. *pcCols = _cCols;
  732. TraceMsg(TF_DSO, "(Mtx) getColumnCount returning %d", _cCols);
  733. return S_OK;
  734. }
  735. /*----------------------------------------------------------
  736. Purpose: OLEDBSimpleProvider::getRWStatus
  737. Gets the read/write status of a cell, row, column or the
  738. entire array.
  739. This implementation cannot set the read/write status on any
  740. cell, so all data cells are presumed to have the default
  741. access and all column heading cells are presumed to be
  742. read-only. Therefore, we don't keep track of this info
  743. in the individual cells.
  744. E_INVALIDARG - returned if indices are out of bounds
  745. */
  746. STDMETHODIMP CDataSrc::getRWStatus(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPRW *prwStatus)
  747. {
  748. HRESULT hres = E_INVALIDARG;
  749. if ((_IsValidRow(iRow) || -1 == iRow) &&
  750. (_IsValidCol(iCol) || -1 == iCol))
  751. {
  752. if (iRow == -1)
  753. {
  754. *prwStatus = OSPRW_MIXED;
  755. }
  756. else if (iRow == 0)
  757. *prwStatus = OSPRW_READONLY;
  758. else
  759. *prwStatus = OSPRW_DEFAULT;
  760. hres = S_OK;
  761. }
  762. if (FAILED(hres))
  763. TraceMsg(TF_WARNING, "(Mtx) getRWStatus(%d, %d) failed %s", iRow, iCol, Dbg_GetHRESULT(hres));
  764. else
  765. TraceMsg(TF_DSO, "(Mtx) getRWStatus(%d, %d) returning %s", iRow, iCol, Dbg_GetOSPRW(*prwStatus));
  766. return hres;
  767. }
  768. /*----------------------------------------------------------
  769. Purpose: OLEDBSimpleProvider::getVariant
  770. Retrieves a variant value for a cell.
  771. */
  772. STDMETHODIMP CDataSrc::getVariant(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPFORMAT format, VARIANT * pvar)
  773. {
  774. HRESULT hres = E_INVALIDARG;
  775. TraceMsg(TF_DSO, "(Mtx) getVariant(%d, %d)", iRow, iCol);
  776. ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT));
  777. if (_IsValidCell(iRow, iCol))
  778. {
  779. VARIANT var;
  780. // Massage col to be 0-based
  781. iCol--;
  782. // Are they asking for the field name?
  783. if (0 == iRow)
  784. {
  785. // Yes; get the field name
  786. if (_pmtxarray)
  787. hres = _pmtxarray->GetFieldName(iCol, &var);
  788. else
  789. hres = E_FAIL;
  790. }
  791. else
  792. {
  793. // No; get the field value
  794. IAppData * pappdata = _GetAppData(iRow);
  795. if (pappdata)
  796. {
  797. hres = pappdata->GetVariant(iCol, &var);
  798. pappdata->Release();
  799. }
  800. else
  801. hres = E_FAIL;
  802. }
  803. if (SUCCEEDED(hres))
  804. {
  805. if (OSPFORMAT_RAW == format)
  806. {
  807. // Copy the raw variant value
  808. *pvar = var;
  809. }
  810. else if (OSPFORMAT_FORMATTED == format || OSPFORMAT_HTML == format)
  811. {
  812. // Consumer wants it in text format
  813. if (VT_BSTR == var.vt || VT_EMPTY == var.vt)
  814. {
  815. // Already done
  816. *pvar = var;
  817. }
  818. else if (VT_UI4 == var.vt)
  819. {
  820. // Coerce
  821. VarBstrFromUI4( var.lVal, 0, 0, &(pvar->bstrVal));
  822. if (pvar->bstrVal != NULL)
  823. {
  824. pvar->vt = VT_BSTR;
  825. }
  826. else
  827. hres = E_OUTOFMEMORY;
  828. }
  829. else
  830. hres = E_NOTIMPL;
  831. }
  832. else
  833. hres = E_INVALIDARG;
  834. if (FAILED(hres))
  835. {
  836. VariantClear(&var);
  837. pvar->vt = VT_BSTR;
  838. pvar->bstrVal = SysAllocString(L"#Error");
  839. }
  840. }
  841. }
  842. if (FAILED(hres))
  843. TraceMsg(TF_WARNING, "(Mtx) getVariant failed %s", Dbg_GetHRESULT(hres));
  844. return hres;
  845. }
  846. /*----------------------------------------------------------
  847. Purpose: OLEDBSimpleProvider::setVariant
  848. Set a cell's variant value from a given variant. The given variant
  849. type is coerced into the columns underlying type.
  850. */
  851. STDMETHODIMP CDataSrc::setVariant(DBROWCOUNT iRow, DB_LORDINAL iCol, OSPFORMAT format, VARIANT var)
  852. {
  853. HRESULT hres = E_INVALIDARG;
  854. TraceMsg(TF_DSO, "(Mtx) setVariant(%d, %d)", iRow, iCol);
  855. #ifdef NYI
  856. if (_IsValidCol(iCol))
  857. {
  858. // Massage col to be 0-based
  859. iCol--;
  860. // Is the data agent trying to change an existing cell?
  861. if (_IsValidDataRow(iRow))
  862. {
  863. // Yes
  864. IAppData * pappdata = _GetAppData(iRow);
  865. if (pappdata)
  866. {
  867. hres = pappdata->SetVariant(iCol, &var);
  868. pappdata->Release();
  869. }
  870. else
  871. hres = E_FAIL;
  872. }
  873. else
  874. {
  875. // No; it wants to add a new row
  876. }
  877. }
  878. #else
  879. hres = E_NOTIMPL;
  880. #endif
  881. if (FAILED(hres))
  882. TraceMsg(TF_WARNING, "(Mtx) setVariant failed %s", Dbg_GetHRESULT(hres));
  883. return hres;
  884. }
  885. /*----------------------------------------------------------
  886. Purpose: OLEDBSimpleProvider::getLocale
  887. Returns to the consumer the locale of the data we
  888. are providing. App management data is in the locale
  889. of the system, so return an empty bstr.
  890. */
  891. STDMETHODIMP CDataSrc::getLocale(BSTR *pbstrLocale)
  892. {
  893. TraceMsg(TF_DSO, "(Mtx) getLocale");
  894. *pbstrLocale = SysAllocString(L"");
  895. return *pbstrLocale ? S_OK : E_OUTOFMEMORY;
  896. }
  897. /*----------------------------------------------------------
  898. Purpose: OLEDBSimpleProvider::deleteRows
  899. Used to delete rows from the array. Bounds are checked
  900. to make sure that the rows can all be deleted. Label
  901. rows cannot be deleted.
  902. E_INVALIDARG - returned if any rows to be deleted are
  903. out of bounds
  904. */
  905. STDMETHODIMP CDataSrc::deleteRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT *pcRowsDeleted)
  906. {
  907. HRESULT hres = E_INVALIDARG;
  908. TraceMsg(TF_DSO, "(Mtx) deleteRows(%d, %d)", iRow, cRows);
  909. *pcRowsDeleted = 0;
  910. if (_IsValidDataRow(iRow) && cRows >= 0 &&
  911. _IsValidDataRow(iRow + cRows - 1))
  912. {
  913. _parpevt->AboutToDeleteRows(iRow, cRows);
  914. *pcRowsDeleted = cRows;
  915. if (cRows > 0)
  916. {
  917. // Delete the rows from the array
  918. _pmtxarray->DeleteItems(iRow - 1, cRows);
  919. _cRows = _CalcRows();
  920. // Notify the event-handler of the deletion
  921. _parpevt->DeletedRows(iRow, cRows);
  922. }
  923. hres = S_OK;
  924. }
  925. if (FAILED(hres))
  926. TraceMsg(TF_WARNING, "(Mtx) deleteRows failed %s", Dbg_GetHRESULT(hres));
  927. return hres;
  928. }
  929. //+-----------------------------------------------------------------------
  930. //
  931. // Member: InsertRows()
  932. //
  933. // Synopsis: Allows for the insertion of new rows. This can either be
  934. // used to insert new rows between existing rows, or to
  935. // append new rows to the end of the table. Thus, to
  936. // insert new rows at the end of the table, a user would
  937. // specify the initial row as 1 greater than the current
  938. // row dimension.
  939. // Note that iRow is checked to ensure that it is within the
  940. // proper bounds (1..<current # of rows>+1).
  941. // User cannot delete column heading row.
  942. //
  943. // Arguments: iRow rows will be inserted *before* row 'iRow'
  944. // cRows how many rows to insert
  945. // pcRowsInserted actual number of rows inserted (OUT)
  946. //
  947. // Returns: S_OK upon success, i.e. all rows could be inserted.
  948. // E_INVALIDARG if row is out of allowed bounds.
  949. // It is possible that fewer than the requested rows were
  950. // inserted. In this case, E_OUTOFMEMORY would be returned,
  951. // and the actual number of rows inserted would be set.
  952. //
  953. //------------------------------------------------------------------------
  954. /*----------------------------------------------------------
  955. Purpose: OLEDBSimpleProvider::insertRows
  956. */
  957. STDMETHODIMP CDataSrc::insertRows(DBROWCOUNT iRow, DBROWCOUNT cRows, DBROWCOUNT *pcRowsInserted)
  958. {
  959. HRESULT hres = E_NOTIMPL;
  960. TraceMsg(TF_DSO, "(Mtx) insertRows(%d, %d)", iRow, cRows);
  961. if (FAILED(hres))
  962. TraceMsg(TF_WARNING, "(Mtx) insertRows failed %s", Dbg_GetHRESULT(hres));
  963. return hres;
  964. }
  965. //+-----------------------------------------------------------------------
  966. //
  967. // Member: Find()
  968. //
  969. // Synopsis: Searches for a row matching the specified criteria
  970. //
  971. // Arguments: iRowStart The starting row for the search
  972. // iCol The column being tested
  973. // vTest The value against which cells in the
  974. // test column are tested
  975. // findFlags Flags indicating whether to search up/down
  976. // and whether comparisons are case sensitive.
  977. // compType The comparison operator for matching (find a
  978. // cell =, >=, <=, >, <, <> the test value)
  979. // piRowFound The row with a matching cell [OUT]
  980. //
  981. // Returns: S_OK upon success, i.e. a row was found (piRowFound set).
  982. // E_FAIL upon failure, i.e. a row was not found.
  983. // E_INVALIDARG if starting row 'iRowStart' or test column 'iCol'
  984. // are out of bounds.
  985. // DISP_E_TYPEMISMATCH if the test value's type does not match
  986. // the test column's type.
  987. //
  988. //------------------------------------------------------------------------
  989. /*----------------------------------------------------------
  990. Purpose: OLEDBSimpleProvider::find
  991. */
  992. STDMETHODIMP CDataSrc::find(DBROWCOUNT iRowStart, DB_LORDINAL iCol, VARIANT vTest,
  993. OSPFIND findFlags, OSPCOMP compType, DBROWCOUNT *piRowFound)
  994. {
  995. HRESULT hres = E_NOTIMPL;
  996. TraceMsg(TF_DSO, "(Mtx) find(%d, %d)", iRowStart, iCol);
  997. if (FAILED(hres))
  998. TraceMsg(TF_WARNING, "(Mtx) find failed %s", Dbg_GetHRESULT(hres));
  999. return hres;
  1000. }
  1001. /*----------------------------------------------------------
  1002. Purpose: OLEDBSimpleProvider::addOLEDBSimpleProviderListener
  1003. Sets or clears a reference to the OSP listener.
  1004. */
  1005. STDMETHODIMP CDataSrc::addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pospl)
  1006. {
  1007. HRESULT hres;
  1008. TraceMsg(TF_DSO, "(Mtx) addOLEDBSimpleProviderListener <%s>", Dbg_GetLS(_loadstate));
  1009. if (_parpevt == NULL)
  1010. hres = E_FAIL;
  1011. else
  1012. {
  1013. _parpevt->SetOSPListener(pospl);
  1014. // If the event sink has been added, and we're already loaded,
  1015. // then fire transferComplete, because we probably couldn't before.
  1016. if (LS_NOTSTARTED < _loadstate)
  1017. _parpevt->LoadCompleted();
  1018. hres = S_OK;
  1019. }
  1020. return hres;
  1021. }
  1022. /*----------------------------------------------------------
  1023. Purpose: OLEDBSimpleProvider::removeOLEDBSimpleProviderListener
  1024. */
  1025. STDMETHODIMP CDataSrc::removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener * pospl)
  1026. {
  1027. if (_parpevt && S_OK == _parpevt->IsOSPListener(pospl))
  1028. {
  1029. TraceMsg(TF_DSO, "(Mtx) removeOLEDBSimpleProviderListener");
  1030. _parpevt->SetOSPListener(NULL);
  1031. }
  1032. return S_OK;
  1033. }
  1034. /*----------------------------------------------------------
  1035. Purpose: OLEDBSimpleProvider::getEstimatedRows
  1036. Returns an estimated number of rows in the matrix.
  1037. Return -1 if unknown.
  1038. */
  1039. STDMETHODIMP CDataSrc::getEstimatedRows(DBROWCOUNT *pcRows)
  1040. {
  1041. if (LS_NOTSTARTED == _loadstate)
  1042. *pcRows = -1;
  1043. else
  1044. *pcRows = _cRows;
  1045. TraceMsg(TF_DSO, "(Mtx) getEstimatedRows returning %d <%s>", *pcRows, Dbg_GetLS(_loadstate));
  1046. return S_OK;
  1047. }
  1048. /*----------------------------------------------------------
  1049. Purpose: OLEDBSimpleProvider::isAsync
  1050. */
  1051. STDMETHODIMP CDataSrc::isAsync(BOOL *pbAsync)
  1052. {
  1053. // This OSP always behaves as if it is async. Specifically, we always fire
  1054. // TransferComplete, even if we have to buffer the notification until our
  1055. // addOLEDBSimplerProviderListener is actually called.
  1056. *pbAsync = TRUE;
  1057. return S_OK;
  1058. }
  1059. /*----------------------------------------------------------
  1060. Purpose: OLEDBSimpleProvider::stopTransfer
  1061. The data download has been cancelled.
  1062. */
  1063. STDMETHODIMP CDataSrc::stopTransfer()
  1064. {
  1065. TraceMsg(TF_DSO, "(Mtx) stopTransfer <%s>", Dbg_GetLS(_loadstate));
  1066. // Force the load state into UNINITIALISED or LOADED ...
  1067. //
  1068. switch (_loadstate)
  1069. {
  1070. case LS_NOTSTARTED:
  1071. case LS_DONE:
  1072. // No need to do anything, because we either haven't started
  1073. // or are already finished.
  1074. break;
  1075. case LS_LOADING_SLOWINFO:
  1076. // Stop the worker thread.
  1077. _KillMtxWorkerThread();
  1078. // Say we're done
  1079. _loadstate = LS_DONE;
  1080. TraceMsg(TF_DSO, "(Mtx) Setting state to <%s>", Dbg_GetLS(_loadstate));
  1081. // Fire an abort event
  1082. _parpevt->LoadAborted();
  1083. break;
  1084. }
  1085. return S_OK;
  1086. }
  1087. /*-------------------------------------------------------------------------
  1088. Purpose: Helper method to kill the worker thread
  1089. */
  1090. HRESULT CDataSrc::_KillMtxWorkerThread(void)
  1091. {
  1092. HRESULT hres = S_OK;
  1093. if (_pmtxarray)
  1094. {
  1095. IARPWorker * pmtxworker;
  1096. hres = _pmtxarray->QueryInterface(IID_IARPWorker, (LPVOID *)&pmtxworker);
  1097. if (SUCCEEDED(hres))
  1098. {
  1099. hres = pmtxworker->KillWT();
  1100. pmtxworker->Release();
  1101. }
  1102. }
  1103. return hres;
  1104. }
  1105. /*----------------------------------------------------------
  1106. Purpose: Create-instance function for CDataSrc
  1107. */
  1108. HRESULT CDataSrc_CreateInstance(REFIID riid, LPVOID * ppvObj)
  1109. {
  1110. HRESULT hres = E_OUTOFMEMORY;
  1111. *ppvObj = NULL;
  1112. CDataSrc * pObj = new CDataSrc();
  1113. if (pObj)
  1114. {
  1115. hres = pObj->QueryInterface(riid, ppvObj);
  1116. pObj->Release();
  1117. }
  1118. return hres;
  1119. }
  1120. #endif //DOWNLEVEL_PLATFORM