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.

1896 lines
54 KiB

  1. #include "shellprv.h"
  2. #include <runtask.h>
  3. #include "defviewp.h"
  4. #include "dvtasks.h"
  5. #include "ids.h"
  6. #include "guids.h"
  7. #include "prop.h" // for SCID_Comment
  8. #include "infotip.h"
  9. // ACL stuff from public/internal/base/inc/seopaque.h
  10. typedef struct _KNOWN_ACE {
  11. ACE_HEADER Header;
  12. ACCESS_MASK Mask;
  13. ULONG SidStart;
  14. } KNOWN_ACE, *PKNOWN_ACE;
  15. #define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
  16. CDefviewEnumTask::CDefviewEnumTask(CDefView *pdsv)
  17. : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv)
  18. {
  19. }
  20. CDefviewEnumTask::~CDefviewEnumTask()
  21. {
  22. ATOMICRELEASE(_peunk);
  23. DPA_FreeIDArray(_hdpaEnum); // accepts NULL
  24. if (_hdpaPending)
  25. DPA_DeleteAllPtrs(_hdpaPending); // the pidl's are owned by defview/listview
  26. }
  27. HRESULT CDefviewEnumTask::FillObjectsToDPA(BOOL fInteractive)
  28. {
  29. DWORD dwTimeout, dwTime = GetTickCount();
  30. if (_pdsv->_IsDesktop())
  31. dwTimeout = 30000; // 30 seconds
  32. else if (_pdsv->_fs.fFlags & FWF_BESTFITWINDOW)
  33. dwTimeout = 3000; // 3 seconds
  34. else
  35. dwTimeout = 500; // 1/2 sec
  36. // Make sure _GetEnumFlags calculates the correct bits
  37. _pdsv->_UpdateEnumerationFlags();
  38. HRESULT hr = _pdsv->_pshf->EnumObjects(fInteractive ? _pdsv->_hwndMain : NULL, _pdsv->_GetEnumFlags(), &_peunk);
  39. if (S_OK == hr)
  40. {
  41. IUnknown_SetSite(_peunk, SAFECAST(_pdsv, IOleCommandTarget *)); // give enum a ref to defview
  42. _hdpaEnum = DPA_Create(16);
  43. if (_hdpaEnum)
  44. {
  45. // let callback force background enum
  46. //
  47. // NOTE: If it is desktop, avoid the Background enumeration. Otherwise, it results in
  48. // a lot of flickering when ActiveDesktop is ON. Bug #394940. Fixed by: Sankar.
  49. if ((!_pdsv->_fAllowSearchingWindow && !_pdsv->_IsDesktop()) || S_OK == _pdsv->CallCB(SFVM_BACKGROUNDENUM, 0, 0) || ((GetTickCount() - dwTime) > dwTimeout))
  50. {
  51. _fBackground = TRUE;
  52. }
  53. else
  54. {
  55. LPITEMIDLIST pidl;
  56. ULONG celt;
  57. while (S_OK == _peunk->Next(1, &pidl, &celt))
  58. {
  59. ASSERT(1==celt);
  60. if (DPA_AppendPtr(_hdpaEnum, pidl) == -1)
  61. SHFree(pidl);
  62. // Are we taking too long?
  63. if (((GetTickCount() - dwTime) > dwTimeout))
  64. {
  65. _fBackground = TRUE;
  66. break;
  67. }
  68. }
  69. }
  70. }
  71. else
  72. {
  73. hr = E_OUTOFMEMORY;
  74. }
  75. IUnknown_SetSite(_peunk, NULL); // Break the site back pointer.
  76. }
  77. _hrRet = hr;
  78. // Let the callback have a chance to "sniff" the items we just enumerated
  79. _pdsv->CallCB(SFVM_ENUMERATEDITEMS, (WPARAM)DPACount(), (LPARAM)DPAArray());
  80. return hr;
  81. }
  82. HRESULT CDefviewEnumTask::FillObjectsDPAToDone()
  83. {
  84. HRESULT hr = S_OK;
  85. if (_fBackground)
  86. {
  87. ASSERT(S_OK == _hrRet);
  88. ASSERT(_peunk);
  89. // let defview do it's background thing
  90. _pdsv->_OnStartBackgroundEnum();
  91. // put ourself on the background scheduler
  92. hr = _pdsv->_AddTask(this, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_BKGRND_FILL, ADDTASK_ATEND);
  93. if (FAILED(hr))
  94. {
  95. // we can't do background, pretend we're done
  96. hr = _pdsv->_OnStopBackgroundEnum();
  97. }
  98. }
  99. if (!_fBackground)
  100. {
  101. _pdsv->FillDone();
  102. }
  103. return hr;
  104. }
  105. HRESULT CDefviewEnumTask::FillObjectsDoneToView()
  106. {
  107. if (SUCCEEDED(_hrRet))
  108. {
  109. HDPA hdpaView = NULL;
  110. int cItems = ListView_GetItemCount(_pdsv->_hwndListview);
  111. if (cItems)
  112. {
  113. hdpaView = DPA_Create(16);
  114. if (hdpaView)
  115. {
  116. for (int i = 0; i < cItems; i++)
  117. {
  118. LPCITEMIDLIST pidl = _pdsv->_GetPIDL(i);
  119. ASSERT(IsValidPIDL(pidl));
  120. if (pidl)
  121. {
  122. DPA_AppendPtr(hdpaView, (void *)pidl);
  123. }
  124. }
  125. }
  126. }
  127. // We only need to sort _hdpaView and _hdpaEnum if they both exist
  128. if (hdpaView && _hdpaEnum)
  129. {
  130. _SortForFilter(hdpaView);
  131. if (!_fEnumSorted)
  132. _SortForFilter(_hdpaEnum);
  133. }
  134. _FilterDPAs(_hdpaEnum, hdpaView);
  135. DPA_Destroy(hdpaView);
  136. }
  137. return _hrRet;
  138. }
  139. // 99/05/13 vtan: Only use CDefView::_CompareExact if you know that
  140. // IShellFolder2 is implemented. SHCIDS_ALLFIELDS is IShellFolder2
  141. // specific. Use CDefView::_GetCanonicalCompareFunction() to get the function
  142. // to pass to DPA_Sort() if you don't want to make this determination.
  143. // p1 and p2 are pointers to the lv_item's LPARAM, which is currently the pidl
  144. int CALLBACK CDefviewEnumTask::_CompareExactCanonical(void *p1, void *p2, LPARAM lParam)
  145. {
  146. CDefView *pdv = (CDefView *)lParam;
  147. return pdv->_CompareIDsDirection(0 | SHCIDS_ALLFIELDS | SHCIDS_CANONICALONLY, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
  148. }
  149. PFNDPACOMPARE CDefviewEnumTask::_GetCanonicalCompareFunction(void)
  150. {
  151. if (_pdsv->_pshf2)
  152. return _CompareExactCanonical;
  153. else
  154. return &(CDefView::_Compare);
  155. }
  156. LPARAM CDefviewEnumTask::_GetCanonicalCompareBits()
  157. {
  158. if (_pdsv->_pshf2)
  159. return 0 | SHCIDS_ALLFIELDS | SHCIDS_CANONICALONLY;
  160. else
  161. return 0;
  162. }
  163. void CDefviewEnumTask::_SortForFilter(HDPA hdpa)
  164. {
  165. DPA_Sort(hdpa, _GetCanonicalCompareFunction(), (LPARAM)_pdsv);
  166. }
  167. // We refreshed the view. Take the old pidls and new pidls and compare
  168. // them, doing a _AddObject for all the new pidls, _RemoveObject
  169. // for the deleted pidls, and _UpdateObject for the inplace modifies.
  170. void CDefviewEnumTask::_FilterDPAs(HDPA hdpaNew, HDPA hdpaOld)
  171. {
  172. LPARAM lParamSort = _GetCanonicalCompareBits();
  173. for (;;)
  174. {
  175. LPITEMIDLIST pidlNew, pidlOld;
  176. int iCompare;
  177. int cOld = hdpaOld ? DPA_GetPtrCount(hdpaOld) : 0;
  178. int cNew = hdpaNew ? DPA_GetPtrCount(hdpaNew) : 0;
  179. if (!cOld && !cNew)
  180. break;
  181. if (!cOld)
  182. {
  183. // only new ones left. Insert all of them.
  184. iCompare = -1;
  185. pidlNew = (LPITEMIDLIST)DPA_FastGetPtr(hdpaNew, 0);
  186. }
  187. else if (!cNew)
  188. {
  189. // only old ones left. remove them all.
  190. iCompare = 1;
  191. pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0);
  192. }
  193. else
  194. {
  195. pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0);
  196. pidlNew = (LPITEMIDLIST)DPA_FastGetPtr(hdpaNew, 0);
  197. iCompare = _pdsv->_CompareIDsDirection(lParamSort, pidlNew, pidlOld);
  198. }
  199. if (iCompare == 0)
  200. {
  201. // they're the same, remove one of each.
  202. ILFree(pidlNew);
  203. DPA_DeletePtr(hdpaNew, 0);
  204. DPA_DeletePtr(hdpaOld, 0);
  205. }
  206. else
  207. {
  208. // Not identical. See if it's just a modify.
  209. if (cOld && cNew && (lParamSort&SHCIDS_ALLFIELDS))
  210. {
  211. iCompare = _pdsv->_CompareIDsDirection((lParamSort&~SHCIDS_ALLFIELDS), pidlNew, pidlOld);
  212. }
  213. if (iCompare == 0)
  214. {
  215. _pdsv->_UpdateObject(pidlOld, pidlNew);
  216. ILFree(pidlNew);
  217. DPA_DeletePtr(hdpaNew, 0);
  218. DPA_DeletePtr(hdpaOld, 0);
  219. }
  220. else if (iCompare < 0) // we have a new item!
  221. {
  222. _pdsv->_AddObject(pidlNew); // takes over pidl ownership.
  223. DPA_DeletePtr(hdpaNew, 0);
  224. }
  225. else // there's an old item in the view!
  226. {
  227. if (!_DeleteFromPending(pidlOld))
  228. _pdsv->_RemoveObject(pidlOld, TRUE);
  229. DPA_DeletePtr(hdpaOld, 0);
  230. }
  231. }
  232. }
  233. }
  234. BOOL CDefviewEnumTask::_DeleteFromPending(LPCITEMIDLIST pidl)
  235. {
  236. if (_hdpaPending)
  237. {
  238. for (int i = 0; i < DPA_GetPtrCount(_hdpaPending); i++)
  239. {
  240. LPCITEMIDLIST pidlPending = (LPCITEMIDLIST) DPA_FastGetPtr(_hdpaPending, i);
  241. if (S_OK == _pdsv->_CompareIDsFallback(0, pidl, pidlPending))
  242. {
  243. // remove this from the pending list
  244. DPA_DeletePtr(_hdpaPending, i); // the pidl is owned by defview/listview
  245. return TRUE;
  246. }
  247. }
  248. }
  249. return FALSE;
  250. }
  251. void CDefviewEnumTask::_AddToPending(LPCITEMIDLIST pidl)
  252. {
  253. if (!_hdpaPending)
  254. _hdpaPending = DPA_Create(16);
  255. if (_hdpaPending)
  256. DPA_AppendPtr(_hdpaPending, (void *)pidl);
  257. }
  258. STDMETHODIMP CDefviewEnumTask::RunInitRT()
  259. {
  260. return S_OK;
  261. }
  262. STDMETHODIMP CDefviewEnumTask::InternalResumeRT()
  263. {
  264. ULONG celt;
  265. LPITEMIDLIST pidl;
  266. IUnknown_SetSite(_peunk, SAFECAST(_pdsv, IOleCommandTarget *)); // give enum a ref to defview
  267. while (S_OK == _peunk->Next(1, &pidl, &celt))
  268. {
  269. if (DPA_AppendPtr(_hdpaEnum, pidl) == -1)
  270. {
  271. SHFree(pidl);
  272. }
  273. // we were told to either suspend or quit...
  274. if (WaitForSingleObject(_hDone, 0) == WAIT_OBJECT_0)
  275. {
  276. return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
  277. }
  278. }
  279. IUnknown_SetSite(_peunk, NULL); // Break the site back pointer.
  280. // Sort on this thread so we do not hang the main thread for as long
  281. DPA_Sort(_hdpaEnum, _GetCanonicalCompareFunction(), (LPARAM)_pdsv);
  282. _fEnumSorted = TRUE;
  283. // notify DefView (async) that we're done
  284. PostMessage(_pdsv->_hwndView, WM_DSV_BACKGROUNDENUMDONE, 0, (LPARAM)this);
  285. return S_OK;
  286. }
  287. class CExtendedColumnTask : public CRunnableTask
  288. {
  289. public:
  290. CExtendedColumnTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn);
  291. STDMETHODIMP RunInitRT(void);
  292. private:
  293. ~CExtendedColumnTask();
  294. CDefView *_pdsv;
  295. LPITEMIDLIST _pidl;
  296. const int _fmt;
  297. const UINT _uiCol;
  298. const UINT _uId;
  299. };
  300. CExtendedColumnTask::CExtendedColumnTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn)
  301. : CRunnableTask(RTF_DEFAULT), _pdsv(pdsv), _fmt(fmt), _uiCol(uiColumn), _uId(uId)
  302. {
  303. *phr = SHILClone(pidl, &_pidl);
  304. }
  305. CExtendedColumnTask::~CExtendedColumnTask()
  306. {
  307. ILFree(_pidl);
  308. }
  309. STDMETHODIMP CExtendedColumnTask::RunInitRT(void)
  310. {
  311. DETAILSINFO di;
  312. di.pidl = _pidl;
  313. di.fmt = _fmt;
  314. if (SUCCEEDED(_pdsv->_GetDetailsHelper(_uiCol, &di)))
  315. {
  316. CBackgroundColInfo *pbgci = new CBackgroundColInfo(_pidl, _uId, _uiCol, di.str);
  317. if (pbgci)
  318. {
  319. _pidl = NULL; // give up ownership of this, ILFree checks for null
  320. if (!PostMessage(_pdsv->_hwndView, WM_DSV_UPDATECOLDATA, 0, (LPARAM)pbgci))
  321. delete pbgci;
  322. }
  323. }
  324. return S_OK;
  325. }
  326. HRESULT CExtendedColumnTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn, IRunnableTask **ppTask)
  327. {
  328. HRESULT hr;
  329. CExtendedColumnTask *pECTask = new CExtendedColumnTask(&hr, pdsv, pidl, uId, fmt, uiColumn);
  330. if (pECTask)
  331. {
  332. if (SUCCEEDED(hr))
  333. *ppTask = SAFECAST(pECTask, IRunnableTask*);
  334. else
  335. pECTask->Release();
  336. }
  337. else
  338. hr = E_OUTOFMEMORY;
  339. return hr;
  340. }
  341. class CIconOverlayTask : public CRunnableTask
  342. {
  343. public:
  344. CIconOverlayTask(HRESULT *phr, LPCITEMIDLIST pidl, int iList, CDefView *pdsv);
  345. STDMETHODIMP RunInitRT(void);
  346. private:
  347. ~CIconOverlayTask();
  348. CDefView *_pdsv;
  349. LPITEMIDLIST _pidl;
  350. int _iList;
  351. };
  352. CIconOverlayTask::CIconOverlayTask(HRESULT *phr, LPCITEMIDLIST pidl, int iList, CDefView *pdsv)
  353. : CRunnableTask(RTF_DEFAULT), _iList(iList), _pdsv(pdsv)
  354. {
  355. *phr = SHILClone(pidl, &_pidl);
  356. }
  357. CIconOverlayTask::~CIconOverlayTask()
  358. {
  359. ILFree(_pidl);
  360. }
  361. STDMETHODIMP CIconOverlayTask::RunInitRT()
  362. {
  363. int iOverlay = 0;
  364. // get the overlay index for this item.
  365. _pdsv->_psio->GetOverlayIndex(_pidl, &iOverlay);
  366. if (iOverlay > 0)
  367. {
  368. // now post the result back to the main thread
  369. PostMessage(_pdsv->_hwndView, WM_DSV_UPDATEOVERLAY, (WPARAM)_iList, (LPARAM)iOverlay);
  370. }
  371. return S_OK;
  372. }
  373. HRESULT CIconOverlayTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, int iList, IRunnableTask **ppTask)
  374. {
  375. *ppTask = NULL;
  376. HRESULT hr;
  377. CIconOverlayTask * pNewTask = new CIconOverlayTask(&hr, pidl, iList, pdsv);
  378. if (pNewTask)
  379. {
  380. if (SUCCEEDED(hr))
  381. *ppTask = SAFECAST(pNewTask, IRunnableTask *);
  382. else
  383. pNewTask->Release();
  384. }
  385. else
  386. {
  387. hr = E_OUTOFMEMORY;
  388. }
  389. return hr;
  390. }
  391. CStatusBarAndInfoTipTask::CStatusBarAndInfoTipTask(HRESULT *phr,
  392. LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidl,
  393. UINT uMsg, int nMsgParam, CBackgroundInfoTip *pbit,
  394. HWND hwnd, IShellTaskScheduler2* pScheduler)
  395. : CRunnableTask(RTF_DEFAULT), _uMsg(uMsg), _nMsgParam(nMsgParam), _pbit(pbit), _hwnd(hwnd), _pScheduler(pScheduler)
  396. {
  397. // If we have a pidl, then the number of objects selected must be 1
  398. // This assert applies to the status bar text, but not to the InfoTip text
  399. ASSERT(pbit || !pidl || nMsgParam == 1);
  400. *phr = pidl ? SHILClone(pidl, &_pidl) : S_OK;
  401. if (SUCCEEDED(*phr))
  402. {
  403. *phr = SHILClone(pidlFolder, &_pidlFolder);
  404. if (FAILED(*phr))
  405. {
  406. ILFree(_pidl);
  407. }
  408. }
  409. if (_pbit)
  410. _pbit->AddRef();
  411. }
  412. CStatusBarAndInfoTipTask::~CStatusBarAndInfoTipTask()
  413. {
  414. ILFree(_pidl);
  415. ILFree(_pidlFolder);
  416. ATOMICRELEASE(_pbit);
  417. }
  418. HRESULT CleanTipForSingleLine(LPWSTR pwszTip)
  419. {
  420. HRESULT hr = E_FAIL; // NULL string, same as failure
  421. if (pwszTip)
  422. {
  423. // Infotips often contain \t\r\n characters, so
  424. // map control characters to spaces. Also collapse
  425. // consecutive spaces to make us look less badf.
  426. LPWSTR pwszDst, pwszSrc;
  427. // Since we are unicode, we don't have to worry about DBCS.
  428. for (pwszDst = pwszSrc = pwszTip; *pwszSrc; pwszSrc++)
  429. {
  430. if ((UINT)*pwszSrc <= (UINT)L' ')
  431. {
  432. if (pwszDst == pwszTip || pwszDst[-1] != L' ')
  433. {
  434. *pwszDst++ = L' ';
  435. }
  436. }
  437. else
  438. {
  439. *pwszDst++ = *pwszSrc;
  440. }
  441. }
  442. *pwszDst = 0;
  443. // GetInfoTip can return a Null String too.
  444. if (*pwszTip)
  445. hr = S_OK;
  446. else
  447. SHFree(pwszTip);
  448. }
  449. return hr;
  450. }
  451. STDMETHODIMP CStatusBarAndInfoTipTask::RunInitRT()
  452. {
  453. LPWSTR pwszTip = NULL;
  454. HRESULT hr;
  455. if (_pidl)
  456. {
  457. IShellFolder* psf;
  458. hr = SHBindToObjectEx(NULL, _pidlFolder, NULL, IID_PPV_ARG(IShellFolder, &psf));
  459. if (SUCCEEDED(hr))
  460. {
  461. IQueryInfo *pqi;
  462. hr = psf->GetUIObjectOf(_hwnd, 1, (LPCITEMIDLIST*)&_pidl, IID_X_PPV_ARG(IQueryInfo, 0, &pqi));
  463. IShellFolder2* psf2;
  464. if (FAILED(hr) && SUCCEEDED(hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  465. {
  466. hr = CreateInfoTipFromItem(psf2, _pidl, TEXT("prop:Comment"), IID_PPV_ARG(IQueryInfo, &pqi));
  467. psf2->Release();
  468. }
  469. if (SUCCEEDED(hr))
  470. {
  471. DWORD dwFlags = _pbit ? QITIPF_USESLOWTIP : 0;
  472. if (_pbit && _pbit->_lvSetInfoTip.pszText[0])
  473. {
  474. ICustomizeInfoTip *pcit;
  475. if (SUCCEEDED(pqi->QueryInterface(IID_PPV_ARG(ICustomizeInfoTip, &pcit))))
  476. {
  477. pcit->SetPrefixText(_pbit->_lvSetInfoTip.pszText);
  478. pcit->Release();
  479. }
  480. }
  481. hr = pqi->GetInfoTip(dwFlags, &pwszTip);
  482. // Prepare for status bar if we have not requested the InfoTip
  483. if (SUCCEEDED(hr) && !_pbit)
  484. hr = CleanTipForSingleLine(pwszTip);
  485. pqi->Release();
  486. }
  487. psf->Release();
  488. }
  489. if (FAILED(hr))
  490. {
  491. pwszTip = NULL;
  492. _uMsg = IDS_FSSTATUSSELECTED;
  493. }
  494. }
  495. if (_pbit)
  496. {
  497. // regular info tip case
  498. CoTaskMemFree(_pbit->_lvSetInfoTip.pszText);
  499. _pbit->_lvSetInfoTip.pszText = pwszTip;
  500. _pbit->_fReady = TRUE;
  501. if (_pScheduler->CountTasks(TOID_DVBackgroundInfoTip) == 1)
  502. PostMessage(_hwnd, WM_DSV_DELAYINFOTIP, (WPARAM)_pbit, 0);
  503. }
  504. else
  505. {
  506. // status bar case
  507. // Now prepare the text and post it to the view which will set the status bar text
  508. LPWSTR pszStatus = pwszTip;
  509. if (pwszTip)
  510. {
  511. pszStatus = StrDupW(pwszTip);
  512. SHFree(pwszTip);
  513. }
  514. else
  515. {
  516. WCHAR szTemp[30];
  517. pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(_uMsg),
  518. AddCommas(_nMsgParam, szTemp, ARRAYSIZE(szTemp)));
  519. }
  520. if (pszStatus && _pScheduler->CountTasks(TOID_DVBackgroundStatusBar) != 1 ||
  521. !PostMessage(_hwnd, WM_DSV_DELAYSTATUSBARUPDATE, 0, (LPARAM)pszStatus))
  522. {
  523. LocalFree((void *)pszStatus);
  524. }
  525. }
  526. return S_OK;
  527. }
  528. HRESULT CStatusBarAndInfoTipTask_CreateInstance(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidl,
  529. UINT uMsg, int nMsgParam, CBackgroundInfoTip *pbit,
  530. HWND hwnd, IShellTaskScheduler2* pScheduler,
  531. CStatusBarAndInfoTipTask **ppTask)
  532. {
  533. *ppTask = NULL;
  534. HRESULT hr;
  535. CStatusBarAndInfoTipTask * pNewTask = new CStatusBarAndInfoTipTask(&hr, pidlFolder, pidl, uMsg, nMsgParam, pbit, hwnd, pScheduler);
  536. if (pNewTask)
  537. {
  538. if (SUCCEEDED(hr))
  539. *ppTask = pNewTask;
  540. else
  541. pNewTask->Release();
  542. }
  543. else
  544. {
  545. hr = E_OUTOFMEMORY;
  546. }
  547. return hr;
  548. }
  549. HRESULT CDUIInfotipTask_CreateInstance(CDefView *pDefView, HWND hwndContaining, UINT uToolID, LPCITEMIDLIST pidl, CDUIInfotipTask **ppTask)
  550. {
  551. HRESULT hr;
  552. CDUIInfotipTask* pTask = new CDUIInfotipTask();
  553. if (pTask)
  554. {
  555. hr = pTask->Initialize(pDefView, hwndContaining, uToolID, pidl);
  556. if (SUCCEEDED(hr))
  557. *ppTask = pTask;
  558. else
  559. pTask->Release();
  560. }
  561. else
  562. hr = E_OUTOFMEMORY;
  563. return hr;
  564. }
  565. CDUIInfotipTask::~CDUIInfotipTask()
  566. {
  567. if (_pDefView)
  568. _pDefView->Release();
  569. if (_pidl)
  570. ILFree(_pidl);
  571. }
  572. HRESULT CDUIInfotipTask::Initialize(CDefView *pDefView, HWND hwndContaining, UINT uToolID, LPCITEMIDLIST pidl)
  573. {
  574. HRESULT hr;
  575. if (pDefView && hwndContaining && pidl)
  576. {
  577. ASSERT(!_pDefView && !_hwndContaining && !_uToolID && !_pidl);
  578. _hwndContaining = hwndContaining; // DUI task's containing hwnd
  579. _uToolID = uToolID; // DUI task's identifier
  580. hr = SHILClone(pidl, &_pidl); // DUI task's destination pidl
  581. if (SUCCEEDED(hr))
  582. {
  583. _pDefView = pDefView;
  584. pDefView->AddRef();
  585. }
  586. }
  587. else
  588. {
  589. hr = E_INVALIDARG;
  590. }
  591. return hr;
  592. }
  593. STDMETHODIMP CDUIInfotipTask::RunInitRT()
  594. {
  595. HRESULT hr;
  596. ASSERT(_pDefView);
  597. ASSERT(_hwndContaining);
  598. ASSERT(_pidl);
  599. // Retrieve an IQueryInfo for the _pidl.
  600. IQueryInfo *pqi;
  601. hr = SHGetUIObjectFromFullPIDL(_pidl, _hwndContaining, IID_PPV_ARG(IQueryInfo, &pqi));
  602. if (SUCCEEDED(hr))
  603. {
  604. // Retrieve infotip text from IQueryInfo.
  605. LPWSTR pwszInfotip;
  606. hr = pqi->GetInfoTip(QITIPF_USESLOWTIP, &pwszInfotip);
  607. if (SUCCEEDED(hr))
  608. {
  609. // Create infotip.
  610. hr = _pDefView->PostCreateInfotip(_hwndContaining, _uToolID, pwszInfotip, 0);
  611. CoTaskMemFree(pwszInfotip);
  612. }
  613. pqi->Release();
  614. }
  615. return hr;
  616. }
  617. STDMETHODIMP CTestCacheTask::RunInitRT()
  618. {
  619. HRESULT hr = E_FAIL;
  620. if (!_fForce)
  621. {
  622. // make sure the disk cache is open for reading.
  623. DWORD dwLock = 0;
  624. hr = _pView->_pDiskCache ? _pView->_pDiskCache->Open(STGM_READ, &dwLock) : E_FAIL;
  625. if (SUCCEEDED(hr))
  626. {
  627. // start the timer, once every two seconds....
  628. SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL);
  629. // is it in the cache....
  630. FILETIME ftCacheTimeStamp;
  631. hr = _pView->_pDiskCache->IsEntryInStore(_szPath, &ftCacheTimeStamp);
  632. // if it is in the cache, and it is an uptodate image, then fetch from disk....
  633. // if the timestamps are wrong, then the extract code further down will then try
  634. // and write its image back to the cache to update it anyway.....
  635. if ((hr == S_OK) &&
  636. ((0 == CompareFileTime(&ftCacheTimeStamp, &_ftDateStamp)) || IsNullTime(&_ftDateStamp)))
  637. {
  638. DWORD dwPriority = _dwPriority - PRIORITY_DELTA_DISKCACHE;
  639. if ((!_pView->_fDestroying) &&
  640. (S_OK != _pView->_pScheduler->MoveTask(TOID_DiskCacheTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT)))
  641. {
  642. // try it in the background...
  643. IRunnableTask *pTask;
  644. hr = CDiskCacheTask_Create(_dwTaskID, _pView, dwPriority, _iItem, _pidl, _szPath, _ftDateStamp, _pExtract, _dwFlags, &pTask);
  645. if (SUCCEEDED(hr))
  646. {
  647. // add the task to the scheduler...
  648. TraceMsg(TF_DEFVIEW, "CTestCacheTask *ADDING* CDiskCacheTask (path=%s, priority=%x)", _szPath, dwPriority);
  649. hr = _pView->_pScheduler->AddTask2(pTask, TOID_DiskCacheTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT);
  650. if (SUCCEEDED(hr))
  651. hr = S_FALSE;
  652. pTask->Release();
  653. }
  654. }
  655. else
  656. {
  657. hr = S_FALSE;
  658. }
  659. }
  660. else
  661. {
  662. TraceMsg(TF_DEFVIEW, "CTestCacheTask *MISS* (hr:%x)", hr);
  663. hr = E_FAIL;
  664. }
  665. _pView->_pDiskCache->ReleaseLock(&dwLock);
  666. }
  667. else
  668. {
  669. TraceMsg(TF_DEFVIEW, "CTestCacheTask *WARNING* Could not open thumbnail cache");
  670. }
  671. }
  672. if (FAILED(hr))
  673. {
  674. // Extract It....
  675. // does it not support Async, or were we told to run it forground ?
  676. if (!_fAsync || !_fBackground)
  677. {
  678. IRunnableTask *pTask;
  679. if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask)))
  680. {
  681. if (!_fBackground)
  682. {
  683. // make sure there is no extract task already underway as we
  684. // are not adding this to the queue...
  685. _pView->_pScheduler->RemoveTasks(TOID_ExtractImageTask, _dwTaskID, TRUE);
  686. }
  687. hr = pTask->Run();
  688. pTask->Release();
  689. }
  690. }
  691. else
  692. {
  693. DWORD dwPriority = _dwPriority - PRIORITY_DELTA_EXTRACT;
  694. if (S_OK != _pView->_pScheduler->MoveTask(TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT))
  695. {
  696. IRunnableTask *pTask;
  697. if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask)))
  698. {
  699. // add the task to the scheduler...
  700. TraceMsg(TF_DEFVIEW, "CTestCacheTask *ADDING* CExtractImageTask (path=%s, priority=%x)", _szPath, dwPriority);
  701. hr = _pView->_pScheduler->AddTask2(pTask, TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT);
  702. pTask->Release();
  703. }
  704. }
  705. // signify we want a default icon for now....
  706. hr = S_FALSE;
  707. }
  708. }
  709. return hr;
  710. }
  711. CTestCacheTask::CTestCacheTask(DWORD dwTaskID, CDefView* pView, IExtractImage *pExtract,
  712. LPCWSTR pszPath, FILETIME ftDateStamp,
  713. int iItem, DWORD dwFlags, DWORD dwPriority,
  714. BOOL fAsync, BOOL fBackground, BOOL fForce) :
  715. CRunnableTask(RTF_DEFAULT), _iItem(iItem), _dwTaskID(dwTaskID), _dwFlags(dwFlags), _dwPriority(dwPriority),
  716. _fAsync(fAsync), _fBackground(fBackground), _fForce(fForce), _pExtract(pExtract), _pView(pView), _ftDateStamp(ftDateStamp)
  717. {
  718. StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath));
  719. _pExtract->AddRef();
  720. }
  721. CTestCacheTask::~CTestCacheTask()
  722. {
  723. ILFree(_pidl);
  724. _pExtract->Release();
  725. }
  726. HRESULT CTestCacheTask::Init(LPCITEMIDLIST pidl)
  727. {
  728. return SHILClone(pidl, &_pidl);
  729. }
  730. HRESULT CTestCacheTask_Create(DWORD dwTaskID, CDefView* pView, IExtractImage *pExtract,
  731. LPCWSTR pszPath, FILETIME ftDateStamp, LPCITEMIDLIST pidl,
  732. int iItem, DWORD dwFlags, DWORD dwPriority,
  733. BOOL fAsync, BOOL fBackground, BOOL fForce,
  734. CTestCacheTask **ppTask)
  735. {
  736. *ppTask = NULL;
  737. HRESULT hr;
  738. CTestCacheTask * pNew = new CTestCacheTask(dwTaskID, pView, pExtract,
  739. pszPath, ftDateStamp, iItem, dwFlags, dwPriority,
  740. fAsync, fBackground, fForce);
  741. if (pNew)
  742. {
  743. hr = pNew->Init(pidl);
  744. if (SUCCEEDED(hr))
  745. {
  746. *ppTask = pNew;
  747. hr = S_OK;
  748. }
  749. else
  750. pNew->Release();
  751. }
  752. else
  753. hr = E_OUTOFMEMORY;
  754. return hr;
  755. }
  756. class CDiskCacheTask : public CRunnableTask
  757. {
  758. public:
  759. STDMETHODIMP RunInitRT(void);
  760. CDiskCacheTask(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags);
  761. HRESULT Init(LPCITEMIDLIST pidl);
  762. private:
  763. ~CDiskCacheTask();
  764. int _iItem;
  765. LPITEMIDLIST _pidl;
  766. CDefView* _pView;
  767. WCHAR _szPath[MAX_PATH];
  768. FILETIME _ftDateStamp;
  769. DWORD _dwTaskID;
  770. DWORD _dwPriority;
  771. IExtractImage *_pExtract;
  772. DWORD _dwFlags;
  773. };
  774. CDiskCacheTask::CDiskCacheTask(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags)
  775. : CRunnableTask(RTF_DEFAULT), _pView(pView), _dwTaskID(dwTaskID), _dwPriority(dwPriority), _iItem(iItem), _ftDateStamp(ftDateStamp),
  776. _pExtract(pExtract), _dwFlags(dwFlags)
  777. {
  778. StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath));
  779. _pExtract->AddRef();
  780. }
  781. CDiskCacheTask::~CDiskCacheTask()
  782. {
  783. ILFree(_pidl);
  784. _pExtract->Release();
  785. }
  786. HRESULT CDiskCacheTask::Init(LPCITEMIDLIST pidl)
  787. {
  788. return SHILClone(pidl, &_pidl);
  789. }
  790. STDMETHODIMP CDiskCacheTask::RunInitRT()
  791. {
  792. DWORD dwLock;
  793. HRESULT hr = E_FAIL;
  794. if (_dwFlags & IEIFLAG_CACHE)
  795. {
  796. hr = _pView->_pDiskCache->Open(STGM_READ, &dwLock);
  797. if (SUCCEEDED(hr))
  798. {
  799. HBITMAP hBmp;
  800. hr = _pView->_pDiskCache->GetEntry(_szPath, STGM_READ, &hBmp);
  801. if (SUCCEEDED(hr))
  802. {
  803. TraceMsg(TF_DEFVIEW, "CDiskCacheTask *CACHE* (path=%s, priority=%x)", _szPath, _dwPriority);
  804. hr = _pView->UpdateImageForItem(_dwTaskID, hBmp, _iItem, _pidl, _szPath, _ftDateStamp, FALSE, _dwPriority);
  805. if (hr != S_FALSE)
  806. DeleteObject(hBmp);
  807. }
  808. // set the tick count so we know when we last accessed the disk cache
  809. SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL);
  810. _pView->_pDiskCache->ReleaseLock(&dwLock);
  811. }
  812. }
  813. if (FAILED(hr)) // We couldn't pull it out of the disk cache, try an extract
  814. {
  815. DWORD dwPriority = _dwPriority - PRIORITY_DELTA_EXTRACT;
  816. if (S_OK != _pView->_pScheduler->MoveTask(TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT))
  817. {
  818. IRunnableTask *pTask;
  819. if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask)))
  820. {
  821. // add the task to the scheduler...
  822. TraceMsg(TF_DEFVIEW, "CDiskCacheTask *ADDING* CExtractImageTask (path=%s, priority=%x)", _szPath, dwPriority);
  823. hr = _pView->_pScheduler->AddTask2(pTask, TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT);
  824. pTask->Release();
  825. }
  826. }
  827. }
  828. return hr;
  829. }
  830. HRESULT CDiskCacheTask_Create(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCITEMIDLIST pidl,
  831. LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags, IRunnableTask **ppTask)
  832. {
  833. HRESULT hr;
  834. CDiskCacheTask *pTask = new CDiskCacheTask(dwTaskID, pView, dwPriority, iItem, pszPath, ftDateStamp, pExtract, dwFlags);
  835. if (pTask)
  836. {
  837. hr = pTask->Init(pidl);
  838. if (SUCCEEDED(hr))
  839. hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask));
  840. pTask->Release();
  841. }
  842. else
  843. hr = E_OUTOFMEMORY;
  844. return hr;
  845. }
  846. class CWriteCacheTask : public CRunnableTask
  847. {
  848. public:
  849. STDMETHOD (RunInitRT)();
  850. CWriteCacheTask(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp, HBITMAP hImage);
  851. private:
  852. ~CWriteCacheTask();
  853. LONG _lState;
  854. CDefView* _pView;
  855. WCHAR _szPath[MAX_PATH];
  856. FILETIME _ftDateStamp;
  857. HBITMAP _hImage;
  858. DWORD _dwTaskID;
  859. };
  860. CWriteCacheTask::CWriteCacheTask(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp, HBITMAP hImage)
  861. : CRunnableTask(RTF_DEFAULT), _dwTaskID(dwTaskID), _hImage(hImage), _pView(pView), _ftDateStamp(ftDateStamp)
  862. {
  863. StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath));
  864. }
  865. CWriteCacheTask::~CWriteCacheTask()
  866. {
  867. DeleteObject(_hImage);
  868. }
  869. HRESULT CWriteCacheTask_Create(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp,
  870. HBITMAP hImage, IRunnableTask **ppTask)
  871. {
  872. *ppTask = NULL;
  873. CWriteCacheTask * pNew = new CWriteCacheTask(dwTaskID, pView, pszPath, ftDateStamp, hImage);
  874. if (!pNew)
  875. return E_OUTOFMEMORY;
  876. *ppTask = SAFECAST(pNew, IRunnableTask *);
  877. return S_OK;
  878. }
  879. STDMETHODIMP CWriteCacheTask::RunInitRT()
  880. {
  881. DWORD dwLock;
  882. HRESULT hr = _pView->_pDiskCache->Open(STGM_WRITE, &dwLock);
  883. if (hr == STG_E_FILENOTFOUND)
  884. {
  885. hr = _pView->_pDiskCache->Create(STGM_WRITE, &dwLock);
  886. }
  887. if (SUCCEEDED(hr))
  888. {
  889. hr = _pView->_pDiskCache->AddEntry(_szPath, IsNullTime(&_ftDateStamp) ? NULL : &_ftDateStamp, STGM_WRITE, _hImage);
  890. // set the tick count so that when the timer goes off, we can know when we
  891. // last used it...
  892. SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL);
  893. hr = _pView->_pDiskCache->ReleaseLock(&dwLock);
  894. }
  895. return hr;
  896. }
  897. class CReadAheadTask : public IRunnableTask
  898. {
  899. public:
  900. CReadAheadTask(CDefView *pView);
  901. HRESULT Init();
  902. // IUnknown
  903. STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
  904. STDMETHOD_(ULONG, AddRef)();
  905. STDMETHOD_(ULONG, Release)();
  906. // IRunnableTask
  907. STDMETHOD (Run)(void);
  908. STDMETHOD (Kill)(BOOL fWait);
  909. STDMETHOD (Suspend)();
  910. STDMETHOD (Resume)();
  911. STDMETHOD_(ULONG, IsRunning)(void);
  912. private:
  913. ~CReadAheadTask();
  914. HRESULT InternalResume();
  915. LONG _cRef;
  916. LONG _lState;
  917. CDefView *_pView;
  918. HANDLE _hEvent;
  919. ULONG _ulCntPerPage;
  920. ULONG _ulCntTotal;
  921. ULONG _ulCnt;
  922. };
  923. CReadAheadTask::~CReadAheadTask()
  924. {
  925. if (_hEvent)
  926. CloseHandle(_hEvent);
  927. }
  928. CReadAheadTask::CReadAheadTask(CDefView *pView) : _cRef(1), _pView(pView)
  929. {
  930. _ulCntPerPage = pView->_ApproxItemsPerView();
  931. _ulCntTotal = ListView_GetItemCount(pView->_hwndListview);
  932. #ifndef DEBUG
  933. // Because we define a small cache in debug we need to only do this
  934. // in retail. Otherwise we would not be able to debug readahead.
  935. _ulCntTotal = min(_ulCntTotal, (ULONG)pView->_iMaxCacheSize);
  936. #endif
  937. _ulCnt = _ulCntPerPage;
  938. }
  939. HRESULT CReadAheadTask::Init()
  940. {
  941. _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  942. return _hEvent ? S_OK : E_OUTOFMEMORY;
  943. }
  944. STDMETHODIMP CReadAheadTask::QueryInterface(REFIID riid, void **ppv)
  945. {
  946. static const QITAB qit[] = {
  947. QITABENT(CReadAheadTask, IRunnableTask),
  948. { 0 },
  949. };
  950. return QISearch(this, qit, riid, ppv);
  951. }
  952. STDMETHODIMP_(ULONG) CReadAheadTask::AddRef()
  953. {
  954. return InterlockedIncrement(&_cRef);
  955. }
  956. STDMETHODIMP_(ULONG) CReadAheadTask::Release()
  957. {
  958. if (InterlockedDecrement(&_cRef))
  959. return _cRef;
  960. delete this;
  961. return 0;
  962. }
  963. HRESULT CReadAheadTask_Create(CDefView *pView, IRunnableTask **ppTask)
  964. {
  965. HRESULT hr;
  966. CReadAheadTask *pTask = new CReadAheadTask(pView);
  967. if (pTask)
  968. {
  969. hr = pTask->Init();
  970. if (SUCCEEDED(hr))
  971. hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask));
  972. pTask->Release();
  973. }
  974. else
  975. {
  976. hr = E_OUTOFMEMORY;
  977. }
  978. return hr;
  979. }
  980. STDMETHODIMP CReadAheadTask::Run()
  981. {
  982. if (_lState == IRTIR_TASK_RUNNING)
  983. {
  984. return S_FALSE;
  985. }
  986. if (_lState == IRTIR_TASK_PENDING)
  987. {
  988. // it is about to die, so fail
  989. return E_FAIL;
  990. }
  991. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING);
  992. if (lRes == IRTIR_TASK_PENDING)
  993. {
  994. _lState = IRTIR_TASK_FINISHED;
  995. return S_OK;
  996. }
  997. // otherwise, run the task ....
  998. HRESULT hr = InternalResume();
  999. if (hr != E_PENDING)
  1000. _lState = IRTIR_TASK_FINISHED;
  1001. return hr;
  1002. }
  1003. STDMETHODIMP CReadAheadTask::Suspend()
  1004. {
  1005. if (_lState != IRTIR_TASK_RUNNING)
  1006. {
  1007. return E_FAIL;
  1008. }
  1009. // suspend ourselves
  1010. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED);
  1011. if (lRes == IRTIR_TASK_FINISHED)
  1012. {
  1013. _lState = lRes;
  1014. return S_OK;
  1015. }
  1016. // if it is running, then there is an Event Handle, if we have passed where
  1017. // we are using it, then we are close to finish, so it will ignore the suspend
  1018. // request
  1019. ASSERT(_hEvent);
  1020. SetEvent(_hEvent);
  1021. return S_OK;
  1022. }
  1023. STDMETHODIMP CReadAheadTask::Resume()
  1024. {
  1025. if (_lState != IRTIR_TASK_SUSPENDED)
  1026. {
  1027. return E_FAIL;
  1028. }
  1029. ResetEvent(_hEvent);
  1030. _lState = IRTIR_TASK_RUNNING;
  1031. HRESULT hr = InternalResume();
  1032. if (hr != E_PENDING)
  1033. {
  1034. _lState= IRTIR_TASK_FINISHED;
  1035. }
  1036. return hr;
  1037. }
  1038. STDMETHODIMP CReadAheadTask::Kill(BOOL fWait)
  1039. {
  1040. if (_lState == IRTIR_TASK_RUNNING)
  1041. {
  1042. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING);
  1043. if (lRes == IRTIR_TASK_FINISHED)
  1044. {
  1045. _lState = lRes;
  1046. }
  1047. else if (_hEvent)
  1048. {
  1049. // signal the event it is likely to be waiting on
  1050. SetEvent(_hEvent);
  1051. }
  1052. return S_OK;
  1053. }
  1054. else if (_lState == IRTIR_TASK_PENDING || _lState == IRTIR_TASK_FINISHED)
  1055. {
  1056. return S_FALSE;
  1057. }
  1058. return E_FAIL;
  1059. }
  1060. STDMETHODIMP_(ULONG) CReadAheadTask::IsRunning()
  1061. {
  1062. return _lState;
  1063. }
  1064. HRESULT CReadAheadTask::InternalResume()
  1065. {
  1066. HRESULT hr = S_OK;
  1067. // pfortier: this algorithm of determining which guys are off the page or not, seems kind of broken.
  1068. // For example, grouping will screw it up. Also, the Z-order of the items, is not necessarily
  1069. // the same as the item order, and we're going by item order.
  1070. // Also, _ulCnt is calculated before dui view is present, so the value is off.
  1071. TraceMsg(TF_DEFVIEW, "ReadAhead: Start");
  1072. for (; _ulCnt < _ulCntTotal; ++_ulCnt)
  1073. {
  1074. // See if we need to suspend
  1075. if (WaitForSingleObject(_hEvent, 0) == WAIT_OBJECT_0)
  1076. {
  1077. // why were we signalled ...
  1078. if (_lState == IRTIR_TASK_SUSPENDED)
  1079. {
  1080. hr = E_PENDING;
  1081. break;
  1082. }
  1083. else
  1084. {
  1085. hr = E_FAIL;
  1086. break;
  1087. }
  1088. }
  1089. LV_ITEMW rgItem;
  1090. rgItem.iItem = (int)_ulCnt;
  1091. rgItem.mask = LVIF_IMAGE;
  1092. rgItem.iSubItem = 0;
  1093. TraceMsg(TF_DEFVIEW, "Thumbnail readahead for item %d", _ulCnt);
  1094. // This will force the extraction of the image if necessary. We will extract it at the right
  1095. // priority, by determining if the item is visible during GetDisplayInfo.
  1096. int iItem = ListView_GetItem(_pView->_hwndListview, &rgItem);
  1097. }
  1098. TraceMsg(TF_DEFVIEW, "ReadAhead: Done (hr:%x)", hr);
  1099. return hr;
  1100. }
  1101. class CFileTypePropertiesTask : public CRunnableTask
  1102. {
  1103. public:
  1104. CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId);
  1105. STDMETHODIMP RunInitRT();
  1106. STDMETHODIMP InternalResumeRT();
  1107. private:
  1108. ~CFileTypePropertiesTask();
  1109. CDefView *_pdsv;
  1110. LPITEMIDLIST _pidl;
  1111. UINT _uMaxPropertiesToShow;
  1112. UINT _uId;
  1113. };
  1114. CFileTypePropertiesTask::CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId)
  1115. : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv), _uMaxPropertiesToShow(uMaxPropertiesToShow), _uId(uId)
  1116. {
  1117. *phr = SHILClone(pidl, &_pidl);
  1118. }
  1119. CFileTypePropertiesTask::~CFileTypePropertiesTask()
  1120. {
  1121. ILFree(_pidl);
  1122. }
  1123. STDMETHODIMP CFileTypePropertiesTask::RunInitRT()
  1124. {
  1125. return S_OK;
  1126. }
  1127. STDMETHODIMP CFileTypePropertiesTask::InternalResumeRT(void)
  1128. {
  1129. // If Columns are not loaded yet, this means this window is just starting up
  1130. // so we want to give it some time to finish the startup (let it paint and such)
  1131. // before we proceed here because the first call to GetImportantColumns will
  1132. // causes all column handlers to be loaded, a slow process.
  1133. if (!_pdsv->_bLoadedColumns)
  1134. {
  1135. if (WaitForSingleObject(_hDone, 750) == WAIT_OBJECT_0)
  1136. {
  1137. return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
  1138. }
  1139. }
  1140. UINT rgColumns[8]; // currently _uMaxPropertiesToShow is 2, this is big enough if that grows
  1141. UINT cColumns = min(_uMaxPropertiesToShow, ARRAYSIZE(rgColumns));
  1142. if (SUCCEEDED(_pdsv->_GetImportantColumns(_pidl, rgColumns, &cColumns)))
  1143. {
  1144. CBackgroundTileInfo *pbgTileInfo = new CBackgroundTileInfo(_pidl, _uId, rgColumns, cColumns);
  1145. if (pbgTileInfo)
  1146. {
  1147. _pidl = NULL; // give up ownership of this, ILFree checks for null
  1148. if (!PostMessage(_pdsv->_hwndView, WM_DSV_SETIMPORTANTCOLUMNS, 0, (LPARAM)pbgTileInfo))
  1149. delete pbgTileInfo;
  1150. }
  1151. }
  1152. return S_OK;
  1153. }
  1154. HRESULT CFileTypePropertiesTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId, IRunnableTask **ppTask)
  1155. {
  1156. HRESULT hr;
  1157. CFileTypePropertiesTask *pFTPTask = new CFileTypePropertiesTask(&hr, pdsv, pidl, uMaxPropertiesToShow, uId);
  1158. if (pFTPTask)
  1159. {
  1160. if (SUCCEEDED(hr))
  1161. *ppTask = SAFECAST(pFTPTask, IRunnableTask*);
  1162. else
  1163. pFTPTask->Release();
  1164. }
  1165. else
  1166. hr = E_OUTOFMEMORY;
  1167. return hr;
  1168. }
  1169. class CExtractImageTask : public IRunnableTask
  1170. {
  1171. public:
  1172. CExtractImageTask(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract,
  1173. LPCWSTR pszPath, LPCITEMIDLIST pidl,
  1174. FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority);
  1175. HRESULT Init(LPCITEMIDLIST pidl);
  1176. // IUnknown
  1177. STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
  1178. STDMETHOD_(ULONG, AddRef)();
  1179. STDMETHOD_(ULONG, Release)();
  1180. // IRunnableTask
  1181. STDMETHOD (Run)(void);
  1182. STDMETHOD (Kill)(BOOL fWait);
  1183. STDMETHOD (Suspend)();
  1184. STDMETHOD (Resume)();
  1185. STDMETHOD_(ULONG, IsRunning)(void);
  1186. private:
  1187. ~CExtractImageTask();
  1188. HRESULT InternalResume();
  1189. LONG _cRef;
  1190. LONG _lState;
  1191. IExtractImage *_pExtract;
  1192. #if 0
  1193. IRunnableTask *_pTask;
  1194. #endif
  1195. WCHAR _szPath[MAX_PATH];
  1196. LPITEMIDLIST _pidl;
  1197. CDefView* _pView;
  1198. DWORD _dwMask;
  1199. DWORD _dwFlags;
  1200. int _iItem;
  1201. HBITMAP _hBmp;
  1202. FILETIME _ftDateStamp;
  1203. DWORD _dwTaskID;
  1204. DWORD _dwPriority;
  1205. };
  1206. CExtractImageTask::CExtractImageTask(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract, LPCWSTR pszPath,
  1207. LPCITEMIDLIST pidl, FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority)
  1208. : _cRef(1), _lState(IRTIR_TASK_NOT_RUNNING), _dwTaskID(dwTaskID), _ftDateStamp(ftNewDateStamp), _dwFlags(dwFlags), _pExtract(pExtract), _pView(pView), _dwPriority(dwPriority)
  1209. {
  1210. _pExtract->AddRef();
  1211. StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath));
  1212. _iItem = iItem == -1 ? _pView->_FindItem(pidl, NULL, FALSE) : iItem;
  1213. _dwMask = pView->_GetOverlayMask(pidl);
  1214. }
  1215. CExtractImageTask::~CExtractImageTask()
  1216. {
  1217. _pExtract->Release();
  1218. #if 0
  1219. if (_pTask)
  1220. _pTask->Release();
  1221. #endif
  1222. ILFree(_pidl);
  1223. if (_hBmp)
  1224. DeleteObject(_hBmp);
  1225. }
  1226. STDMETHODIMP CExtractImageTask::QueryInterface(REFIID riid, void **ppv)
  1227. {
  1228. static const QITAB qit[] = {
  1229. QITABENT(CExtractImageTask, IRunnableTask),
  1230. { 0 },
  1231. };
  1232. return QISearch(this, qit, riid, ppv);
  1233. }
  1234. STDMETHODIMP_(ULONG) CExtractImageTask::AddRef()
  1235. {
  1236. return InterlockedIncrement(&_cRef);
  1237. }
  1238. STDMETHODIMP_(ULONG) CExtractImageTask::Release()
  1239. {
  1240. if (InterlockedDecrement(&_cRef))
  1241. return _cRef;
  1242. delete this;
  1243. return 0;
  1244. }
  1245. HRESULT CExtractImageTask::Init(LPCITEMIDLIST pidl)
  1246. {
  1247. return SHILClone(pidl, &_pidl);
  1248. }
  1249. HRESULT CExtractImageTask_Create(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract,
  1250. LPCWSTR pszPath, LPCITEMIDLIST pidl,
  1251. FILETIME ftNewDateStamp, int iItem, DWORD dwFlags,
  1252. DWORD dwPriority, IRunnableTask **ppTask)
  1253. {
  1254. HRESULT hr;
  1255. CExtractImageTask *pTask = new CExtractImageTask(dwTaskID, pView, pExtract,
  1256. pszPath, pidl, ftNewDateStamp, iItem, dwFlags, dwPriority);
  1257. if (pTask)
  1258. {
  1259. hr = pTask->Init(pidl);
  1260. if (SUCCEEDED(hr))
  1261. hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask));
  1262. pTask->Release();
  1263. }
  1264. else
  1265. {
  1266. hr = E_OUTOFMEMORY;
  1267. }
  1268. return hr;
  1269. }
  1270. STDMETHODIMP CExtractImageTask::Run(void)
  1271. {
  1272. HRESULT hr = E_FAIL;
  1273. if (_lState == IRTIR_TASK_RUNNING)
  1274. {
  1275. hr = S_FALSE;
  1276. }
  1277. else if (_lState == IRTIR_TASK_PENDING)
  1278. {
  1279. hr = E_FAIL;
  1280. }
  1281. else if (_lState == IRTIR_TASK_NOT_RUNNING)
  1282. {
  1283. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING);
  1284. if (lRes == IRTIR_TASK_PENDING)
  1285. {
  1286. _lState = IRTIR_TASK_FINISHED;
  1287. return S_OK;
  1288. }
  1289. // extractor may support IRunnableTask so they can support being
  1290. // canceled in the middle of the extract call. CHtmlThumb & CImgCtxThumb
  1291. // extractors use this. CImgCtxThumb has been replaced that with our GDI+ extractor
  1292. #if 0
  1293. if (!_pTask)
  1294. {
  1295. _pExtract->QueryInterface(IID_PPV_ARG(IRunnableTask, &_pTask));
  1296. }
  1297. #endif
  1298. if (_lState == IRTIR_TASK_RUNNING)
  1299. {
  1300. TraceMsg(TF_DEFVIEW, "CExtractImageTask *START* (path=%s, priority=%x)", _szPath, _dwPriority);
  1301. // start the extractor....
  1302. // extractor can return S_FALSE and set _hBmp to NULL. We will use _hBmp to recognize this situation
  1303. ASSERT(_hBmp == NULL);
  1304. if (FAILED(_pExtract->Extract(&_hBmp)))
  1305. {
  1306. _hBmp = NULL;
  1307. }
  1308. }
  1309. if (_hBmp && _lState == IRTIR_TASK_RUNNING)
  1310. {
  1311. TraceMsg(TF_DEFVIEW, "CExtractImageTask *EXTRACT* (path=%s, priority=%x)", _szPath, _dwPriority);
  1312. hr = InternalResume();
  1313. }
  1314. if (_lState != IRTIR_TASK_SUSPENDED || hr != E_PENDING)
  1315. {
  1316. _lState = IRTIR_TASK_FINISHED;
  1317. }
  1318. }
  1319. return hr;
  1320. }
  1321. STDMETHODIMP CExtractImageTask::Kill(BOOL fWait)
  1322. {
  1323. // This is broken: by not setting the fSuspended flag on the task,
  1324. // the scheduler doesn't know to call Resume back. Instead, it calls
  1325. // Run. This causes the task to never finish.
  1326. #if 0
  1327. if (_lState != IRTIR_TASK_RUNNING)
  1328. {
  1329. return S_FALSE;
  1330. }
  1331. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING);
  1332. if (lRes == IRTIR_TASK_FINISHED)
  1333. {
  1334. _lState = lRes;
  1335. return S_OK;
  1336. }
  1337. // does it support IRunnableTask ? Can we kill it ?
  1338. if (_pExtract)
  1339. {
  1340. IRunnableTask *pTask;
  1341. if (SUCCEEDED(_pExtract->QueryInterface(IID_PPV_ARG(IRunnableTask, &pTask))))
  1342. {
  1343. pTask->Kill(FALSE);
  1344. pTask->Release();
  1345. }
  1346. }
  1347. return S_OK;
  1348. #else // 0
  1349. return E_NOTIMPL;
  1350. #endif // 0
  1351. }
  1352. STDMETHODIMP CExtractImageTask::Suspend(void)
  1353. {
  1354. // This is broken: by not setting the fSuspended flag on the task,
  1355. // the scheduler doesn't know to call Resume back. Instead, it calls
  1356. // Run. This causes the task to never finish.
  1357. #if 0
  1358. if (!_pTask)
  1359. return E_NOTIMPL;
  1360. if (_lState != IRTIR_TASK_RUNNING)
  1361. return E_FAIL;
  1362. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED);
  1363. HRESULT hr = _pTask->Suspend();
  1364. if (SUCCEEDED(hr))
  1365. {
  1366. lRes = (LONG) _pTask->IsRunning();
  1367. if (lRes == IRTIR_TASK_SUSPENDED)
  1368. {
  1369. if (lRes != IRTIR_TASK_RUNNING)
  1370. {
  1371. _lState = lRes;
  1372. }
  1373. }
  1374. }
  1375. else
  1376. {
  1377. _lState = lRes;
  1378. }
  1379. return hr;
  1380. #else // 0
  1381. return E_NOTIMPL;
  1382. #endif // 0
  1383. }
  1384. STDMETHODIMP CExtractImageTask::Resume(void)
  1385. {
  1386. #if 0
  1387. if (!_pTask)
  1388. return E_NOTIMPL;
  1389. if (_lState != IRTIR_TASK_SUSPENDED)
  1390. {
  1391. return E_FAIL;
  1392. }
  1393. _lState = IRTIR_TASK_RUNNING;
  1394. HRESULT hr = _pTask->Resume();
  1395. if (SUCCEEDED(hr))
  1396. {
  1397. hr = InternalResume();
  1398. }
  1399. return hr;
  1400. #else // 0
  1401. return E_NOTIMPL;
  1402. #endif // 0
  1403. }
  1404. HRESULT CExtractImageTask::InternalResume()
  1405. {
  1406. ASSERT(_hBmp != NULL);
  1407. BOOL bCache = (_dwFlags & IEIFLAG_CACHE);
  1408. if (bCache)
  1409. {
  1410. IShellFolder* psf = NULL;
  1411. if (SUCCEEDED(_pView->GetShellFolder(&psf)))
  1412. {
  1413. TCHAR szPath[MAX_PATH];
  1414. if (SUCCEEDED(DisplayNameOf(psf, _pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath))))
  1415. {
  1416. // Make sure we don't request to cache an item that is encrypted in a folder that is not
  1417. if (SFGAO_ENCRYPTED == SHGetAttributes(psf, _pidl, SFGAO_ENCRYPTED))
  1418. {
  1419. bCache = FALSE;
  1420. LPITEMIDLIST pidlParent = _pView->_GetViewPidl();
  1421. if (pidlParent)
  1422. {
  1423. if (SFGAO_ENCRYPTED == SHGetAttributes(NULL, pidlParent, SFGAO_ENCRYPTED))
  1424. {
  1425. bCache = TRUE;
  1426. }
  1427. #ifdef DEBUG
  1428. else
  1429. {
  1430. TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s is encrypted in unencrypted folder)", szPath);
  1431. }
  1432. #endif
  1433. ILFree(pidlParent);
  1434. }
  1435. }
  1436. // Make sure we don't request to cache an item that has differing ACLs applied
  1437. if (bCache)
  1438. {
  1439. PACL pdacl;
  1440. PSECURITY_DESCRIPTOR psd;
  1441. bCache = FALSE;
  1442. if (ERROR_SUCCESS == GetNamedSecurityInfo(szPath,
  1443. SE_FILE_OBJECT,
  1444. DACL_SECURITY_INFORMATION,
  1445. NULL,
  1446. NULL,
  1447. &pdacl,
  1448. NULL,
  1449. &psd))
  1450. {
  1451. SECURITY_DESCRIPTOR_CONTROL sdc;
  1452. DWORD dwRevision;
  1453. if (GetSecurityDescriptorControl(psd, &sdc, &dwRevision) && !(sdc & SE_DACL_PROTECTED))
  1454. {
  1455. if (pdacl)
  1456. {
  1457. PKNOWN_ACE pACE = (PKNOWN_ACE) FirstAce(pdacl);
  1458. if ((pACE->Header.AceType != ACCESS_DENIED_ACE_TYPE) || (pACE->Header.AceFlags & INHERITED_ACE))
  1459. {
  1460. bCache = TRUE;
  1461. }
  1462. #ifdef DEBUG
  1463. else
  1464. {
  1465. TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s has a non-inherited deny acl)", szPath);
  1466. }
  1467. #endif
  1468. }
  1469. else
  1470. {
  1471. bCache = TRUE; // NULL dacl == everyone all access
  1472. }
  1473. }
  1474. #ifdef DEBUG
  1475. else
  1476. {
  1477. TraceMsg(TF_DEFVIEW,"CExtractImageTask (%s has a protected dacl)", szPath);
  1478. }
  1479. #endif
  1480. LocalFree(psd);
  1481. }
  1482. }
  1483. }
  1484. psf->Release();
  1485. }
  1486. if (!bCache && _pView->_pDiskCache) // If we were asked to cache and are not for security reasons
  1487. {
  1488. DWORD dwLock;
  1489. if (SUCCEEDED(_pView->_pDiskCache->Open(STGM_WRITE, &dwLock)))
  1490. {
  1491. _pView->_pDiskCache->DeleteEntry(_szPath);
  1492. _pView->_pDiskCache->ReleaseLock(&dwLock);
  1493. SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL); // Keep open for 2 seconds, just in case
  1494. }
  1495. }
  1496. }
  1497. HRESULT hr = _pView->UpdateImageForItem(_dwTaskID, _hBmp, _iItem, _pidl, _szPath, _ftDateStamp, bCache, _dwPriority);
  1498. // UpdateImageForItem returns S_FALSE if it assumes ownership of bitmap
  1499. if (hr == S_FALSE)
  1500. {
  1501. _hBmp = NULL;
  1502. }
  1503. _lState = IRTIR_TASK_FINISHED;
  1504. return hr;
  1505. }
  1506. STDMETHODIMP_(ULONG) CExtractImageTask::IsRunning(void)
  1507. {
  1508. return _lState;
  1509. }
  1510. class CCategoryTask : public CRunnableTask
  1511. {
  1512. public:
  1513. STDMETHOD (RunInitRT)();
  1514. CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl);
  1515. private:
  1516. ~CCategoryTask();
  1517. CDefView* _pView;
  1518. LPITEMIDLIST _pidl;
  1519. ICategorizer* _pcat;
  1520. UINT _uId;
  1521. };
  1522. CCategoryTask::CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl)
  1523. : CRunnableTask(RTF_DEFAULT), _uId(uId), _pView(pView), _pcat(pView->_pcat)
  1524. {
  1525. _pcat->AddRef();
  1526. _pidl = ILClone(pidl);
  1527. }
  1528. CCategoryTask::~CCategoryTask()
  1529. {
  1530. ATOMICRELEASE(_pcat);
  1531. ILFree(_pidl);
  1532. PostMessage(_pView->_hwndView, WM_DSV_GROUPINGDONE, 0, 0);
  1533. }
  1534. HRESULT CCategoryTask_Create(CDefView *pView, LPCITEMIDLIST pidl, UINT uId, IRunnableTask **ppTask)
  1535. {
  1536. *ppTask = NULL;
  1537. CCategoryTask * pNew = new CCategoryTask(pView, uId, pidl);
  1538. if (!pNew)
  1539. return E_OUTOFMEMORY;
  1540. *ppTask = SAFECAST(pNew, IRunnableTask *);
  1541. return S_OK;
  1542. }
  1543. STDMETHODIMP CCategoryTask::RunInitRT()
  1544. {
  1545. if (_pidl)
  1546. {
  1547. DWORD dwGroup = -1;
  1548. _pcat->GetCategory(1, (LPCITEMIDLIST*)&_pidl, &dwGroup);
  1549. CBackgroundGroupInfo* pbggi = new CBackgroundGroupInfo(_pidl, _uId, dwGroup);
  1550. if (pbggi)
  1551. {
  1552. _pidl = NULL; // Transferred ownership to BackgroundInfo
  1553. if (!PostMessage(_pView->_hwndView, WM_DSV_SETITEMGROUP, NULL, (LPARAM)pbggi))
  1554. delete pbggi;
  1555. }
  1556. }
  1557. return S_OK;
  1558. }
  1559. class CGetCommandStateTask : public CRunnableTask
  1560. {
  1561. public:
  1562. STDMETHODIMP RunInitRT();
  1563. STDMETHODIMP InternalResumeRT();
  1564. CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray);
  1565. private:
  1566. ~CGetCommandStateTask();
  1567. CDefView *_pView;
  1568. IUICommand *_puiCommand;
  1569. IShellItemArray *_psiItemArray;
  1570. };
  1571. HRESULT CGetCommandStateTask_Create(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray, IRunnableTask **ppTask)
  1572. {
  1573. *ppTask = NULL;
  1574. CGetCommandStateTask *pNew = new CGetCommandStateTask(pView, puiCommand, psiItemArray);
  1575. if (!pNew)
  1576. return E_OUTOFMEMORY;
  1577. *ppTask = SAFECAST(pNew, IRunnableTask *);
  1578. return S_OK;
  1579. }
  1580. CGetCommandStateTask::CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray)
  1581. : CRunnableTask(RTF_SUPPORTKILLSUSPEND)
  1582. {
  1583. _pView = pView;
  1584. _puiCommand = puiCommand;
  1585. _puiCommand->AddRef();
  1586. _psiItemArray = psiItemArray;
  1587. if (_psiItemArray)
  1588. _psiItemArray->AddRef();
  1589. }
  1590. CGetCommandStateTask::~CGetCommandStateTask()
  1591. {
  1592. ATOMICRELEASE(_puiCommand);
  1593. ATOMICRELEASE(_psiItemArray);
  1594. }
  1595. STDMETHODIMP CGetCommandStateTask::RunInitRT()
  1596. {
  1597. return S_OK;
  1598. }
  1599. STDMETHODIMP CGetCommandStateTask::InternalResumeRT()
  1600. {
  1601. // Don't want to interfere with the explorer view starting up, so give it a head start.
  1602. // we were told to either suspend or quit...
  1603. if (WaitForSingleObject(_hDone, 1000) == WAIT_OBJECT_0)
  1604. {
  1605. return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
  1606. }
  1607. UISTATE uis;
  1608. HRESULT hr = _puiCommand->get_State(_psiItemArray, TRUE, &uis);
  1609. if (SUCCEEDED(hr) && (uis==UIS_ENABLED))
  1610. {
  1611. _pView->_PostSelectionChangedMessage(LVIS_SELECTED);
  1612. }
  1613. return S_OK;
  1614. }