Leaked source code of windows server 2003
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.

1823 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, DWORD dwId)
  17. : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv), _dwId(dwId)
  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)_dwId);
  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. ASSERT( 0 != _cRef );
  959. ULONG cRef = InterlockedDecrement(&_cRef);
  960. if ( 0 == cRef )
  961. {
  962. delete this;
  963. }
  964. return cRef;
  965. }
  966. HRESULT CReadAheadTask_Create(CDefView *pView, IRunnableTask **ppTask)
  967. {
  968. HRESULT hr;
  969. CReadAheadTask *pTask = new CReadAheadTask(pView);
  970. if (pTask)
  971. {
  972. hr = pTask->Init();
  973. if (SUCCEEDED(hr))
  974. hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask));
  975. pTask->Release();
  976. }
  977. else
  978. {
  979. hr = E_OUTOFMEMORY;
  980. }
  981. return hr;
  982. }
  983. STDMETHODIMP CReadAheadTask::Run()
  984. {
  985. if (_lState == IRTIR_TASK_RUNNING)
  986. {
  987. return S_FALSE;
  988. }
  989. if (_lState == IRTIR_TASK_PENDING)
  990. {
  991. // it is about to die, so fail
  992. return E_FAIL;
  993. }
  994. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING);
  995. if (lRes == IRTIR_TASK_PENDING)
  996. {
  997. _lState = IRTIR_TASK_FINISHED;
  998. return S_OK;
  999. }
  1000. // otherwise, run the task ....
  1001. HRESULT hr = InternalResume();
  1002. if (hr != E_PENDING)
  1003. _lState = IRTIR_TASK_FINISHED;
  1004. return hr;
  1005. }
  1006. STDMETHODIMP CReadAheadTask::Suspend()
  1007. {
  1008. if (_lState != IRTIR_TASK_RUNNING)
  1009. {
  1010. return E_FAIL;
  1011. }
  1012. // suspend ourselves
  1013. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED);
  1014. if (lRes == IRTIR_TASK_FINISHED)
  1015. {
  1016. _lState = lRes;
  1017. return S_OK;
  1018. }
  1019. // if it is running, then there is an Event Handle, if we have passed where
  1020. // we are using it, then we are close to finish, so it will ignore the suspend
  1021. // request
  1022. ASSERT(_hEvent);
  1023. SetEvent(_hEvent);
  1024. return S_OK;
  1025. }
  1026. STDMETHODIMP CReadAheadTask::Resume()
  1027. {
  1028. if (_lState != IRTIR_TASK_SUSPENDED)
  1029. {
  1030. return E_FAIL;
  1031. }
  1032. ResetEvent(_hEvent);
  1033. _lState = IRTIR_TASK_RUNNING;
  1034. HRESULT hr = InternalResume();
  1035. if (hr != E_PENDING)
  1036. {
  1037. _lState= IRTIR_TASK_FINISHED;
  1038. }
  1039. return hr;
  1040. }
  1041. STDMETHODIMP CReadAheadTask::Kill(BOOL fWait)
  1042. {
  1043. if (_lState == IRTIR_TASK_RUNNING)
  1044. {
  1045. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING);
  1046. if (lRes == IRTIR_TASK_FINISHED)
  1047. {
  1048. _lState = lRes;
  1049. }
  1050. else if (_hEvent)
  1051. {
  1052. // signal the event it is likely to be waiting on
  1053. SetEvent(_hEvent);
  1054. }
  1055. return S_OK;
  1056. }
  1057. else if (_lState == IRTIR_TASK_PENDING || _lState == IRTIR_TASK_FINISHED)
  1058. {
  1059. return S_FALSE;
  1060. }
  1061. return E_FAIL;
  1062. }
  1063. STDMETHODIMP_(ULONG) CReadAheadTask::IsRunning()
  1064. {
  1065. return _lState;
  1066. }
  1067. HRESULT CReadAheadTask::InternalResume()
  1068. {
  1069. HRESULT hr = S_OK;
  1070. // pfortier: this algorithm of determining which guys are off the page or not, seems kind of broken.
  1071. // For example, grouping will screw it up. Also, the Z-order of the items, is not necessarily
  1072. // the same as the item order, and we're going by item order.
  1073. // Also, _ulCnt is calculated before dui view is present, so the value is off.
  1074. TraceMsg(TF_DEFVIEW, "ReadAhead: Start");
  1075. for (; _ulCnt < _ulCntTotal; ++_ulCnt)
  1076. {
  1077. // See if we need to suspend
  1078. if (WaitForSingleObject(_hEvent, 0) == WAIT_OBJECT_0)
  1079. {
  1080. // why were we signalled ...
  1081. if (_lState == IRTIR_TASK_SUSPENDED)
  1082. {
  1083. hr = E_PENDING;
  1084. break;
  1085. }
  1086. else
  1087. {
  1088. hr = E_FAIL;
  1089. break;
  1090. }
  1091. }
  1092. LV_ITEMW rgItem;
  1093. rgItem.iItem = (int)_ulCnt;
  1094. rgItem.mask = LVIF_IMAGE;
  1095. rgItem.iSubItem = 0;
  1096. TraceMsg(TF_DEFVIEW, "Thumbnail readahead for item %d", _ulCnt);
  1097. // This will force the extraction of the image if necessary. We will extract it at the right
  1098. // priority, by determining if the item is visible during GetDisplayInfo.
  1099. int iItem = ListView_GetItem(_pView->_hwndListview, &rgItem);
  1100. }
  1101. TraceMsg(TF_DEFVIEW, "ReadAhead: Done (hr:%x)", hr);
  1102. return hr;
  1103. }
  1104. class CFileTypePropertiesTask : public CRunnableTask
  1105. {
  1106. public:
  1107. CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId);
  1108. STDMETHODIMP RunInitRT();
  1109. STDMETHODIMP InternalResumeRT();
  1110. private:
  1111. ~CFileTypePropertiesTask();
  1112. CDefView *_pdsv;
  1113. LPITEMIDLIST _pidl;
  1114. UINT _uMaxPropertiesToShow;
  1115. UINT _uId;
  1116. };
  1117. CFileTypePropertiesTask::CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId)
  1118. : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv), _uMaxPropertiesToShow(uMaxPropertiesToShow), _uId(uId)
  1119. {
  1120. *phr = SHILClone(pidl, &_pidl);
  1121. }
  1122. CFileTypePropertiesTask::~CFileTypePropertiesTask()
  1123. {
  1124. ILFree(_pidl);
  1125. }
  1126. STDMETHODIMP CFileTypePropertiesTask::RunInitRT()
  1127. {
  1128. return S_OK;
  1129. }
  1130. STDMETHODIMP CFileTypePropertiesTask::InternalResumeRT(void)
  1131. {
  1132. // If Columns are not loaded yet, this means this window is just starting up
  1133. // so we want to give it some time to finish the startup (let it paint and such)
  1134. // before we proceed here because the first call to GetImportantColumns will
  1135. // causes all column handlers to be loaded, a slow process.
  1136. if (!_pdsv->_bLoadedColumns)
  1137. {
  1138. if (WaitForSingleObject(_hDone, 750) == WAIT_OBJECT_0)
  1139. {
  1140. return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
  1141. }
  1142. }
  1143. UINT rgColumns[8]; // currently _uMaxPropertiesToShow is 2, this is big enough if that grows
  1144. UINT cColumns = min(_uMaxPropertiesToShow, ARRAYSIZE(rgColumns));
  1145. if (SUCCEEDED(_pdsv->_GetImportantColumns(_pidl, rgColumns, &cColumns)))
  1146. {
  1147. CBackgroundTileInfo *pbgTileInfo = new CBackgroundTileInfo(_pidl, _uId, rgColumns, cColumns);
  1148. if (pbgTileInfo)
  1149. {
  1150. _pidl = NULL; // give up ownership of this, ILFree checks for null
  1151. if (!PostMessage(_pdsv->_hwndView, WM_DSV_SETIMPORTANTCOLUMNS, 0, (LPARAM)pbgTileInfo))
  1152. delete pbgTileInfo;
  1153. }
  1154. }
  1155. return S_OK;
  1156. }
  1157. HRESULT CFileTypePropertiesTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId, IRunnableTask **ppTask)
  1158. {
  1159. HRESULT hr;
  1160. CFileTypePropertiesTask *pFTPTask = new CFileTypePropertiesTask(&hr, pdsv, pidl, uMaxPropertiesToShow, uId);
  1161. if (pFTPTask)
  1162. {
  1163. if (SUCCEEDED(hr))
  1164. *ppTask = SAFECAST(pFTPTask, IRunnableTask*);
  1165. else
  1166. pFTPTask->Release();
  1167. }
  1168. else
  1169. hr = E_OUTOFMEMORY;
  1170. return hr;
  1171. }
  1172. class CExtractImageTask : public IRunnableTask
  1173. {
  1174. public:
  1175. CExtractImageTask(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract,
  1176. LPCWSTR pszPath, LPCITEMIDLIST pidl,
  1177. FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority);
  1178. HRESULT Init(LPCITEMIDLIST pidl);
  1179. // IUnknown
  1180. STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
  1181. STDMETHOD_(ULONG, AddRef)();
  1182. STDMETHOD_(ULONG, Release)();
  1183. // IRunnableTask
  1184. STDMETHOD (Run)(void);
  1185. STDMETHOD (Kill)(BOOL fWait);
  1186. STDMETHOD (Suspend)();
  1187. STDMETHOD (Resume)();
  1188. STDMETHOD_(ULONG, IsRunning)(void);
  1189. private:
  1190. ~CExtractImageTask();
  1191. HRESULT InternalResume();
  1192. LONG _cRef;
  1193. LONG _lState;
  1194. IExtractImage *_pExtract;
  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. ILFree(_pidl);
  1219. if (_hBmp)
  1220. DeleteObject(_hBmp);
  1221. }
  1222. STDMETHODIMP CExtractImageTask::QueryInterface(REFIID riid, void **ppv)
  1223. {
  1224. static const QITAB qit[] = {
  1225. QITABENT(CExtractImageTask, IRunnableTask),
  1226. { 0 },
  1227. };
  1228. return QISearch(this, qit, riid, ppv);
  1229. }
  1230. STDMETHODIMP_(ULONG) CExtractImageTask::AddRef()
  1231. {
  1232. return InterlockedIncrement(&_cRef);
  1233. }
  1234. STDMETHODIMP_(ULONG) CExtractImageTask::Release()
  1235. {
  1236. ASSERT( 0 != _cRef );
  1237. ULONG cRef = InterlockedDecrement(&_cRef);
  1238. if ( 0 == cRef )
  1239. {
  1240. delete this;
  1241. }
  1242. return cRef;
  1243. }
  1244. HRESULT CExtractImageTask::Init(LPCITEMIDLIST pidl)
  1245. {
  1246. return SHILClone(pidl, &_pidl);
  1247. }
  1248. HRESULT CExtractImageTask_Create(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract,
  1249. LPCWSTR pszPath, LPCITEMIDLIST pidl,
  1250. FILETIME ftNewDateStamp, int iItem, DWORD dwFlags,
  1251. DWORD dwPriority, IRunnableTask **ppTask)
  1252. {
  1253. HRESULT hr;
  1254. CExtractImageTask *pTask = new CExtractImageTask(dwTaskID, pView, pExtract,
  1255. pszPath, pidl, ftNewDateStamp, iItem, dwFlags, dwPriority);
  1256. if (pTask)
  1257. {
  1258. hr = pTask->Init(pidl);
  1259. if (SUCCEEDED(hr))
  1260. hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask));
  1261. pTask->Release();
  1262. }
  1263. else
  1264. {
  1265. hr = E_OUTOFMEMORY;
  1266. }
  1267. return hr;
  1268. }
  1269. STDMETHODIMP CExtractImageTask::Run(void)
  1270. {
  1271. HRESULT hr = E_FAIL;
  1272. if (_lState == IRTIR_TASK_RUNNING)
  1273. {
  1274. hr = S_FALSE;
  1275. }
  1276. else if (_lState == IRTIR_TASK_PENDING)
  1277. {
  1278. hr = E_FAIL;
  1279. }
  1280. else if (_lState == IRTIR_TASK_NOT_RUNNING)
  1281. {
  1282. LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING);
  1283. if (lRes == IRTIR_TASK_PENDING)
  1284. {
  1285. _lState = IRTIR_TASK_FINISHED;
  1286. return S_OK;
  1287. }
  1288. if (_lState == IRTIR_TASK_RUNNING)
  1289. {
  1290. TraceMsg(TF_DEFVIEW, "CExtractImageTask *START* (path=%s, priority=%x)", _szPath, _dwPriority);
  1291. // start the extractor....
  1292. // extractor can return S_FALSE and set _hBmp to NULL. We will use _hBmp to recognize this situation
  1293. ASSERT(_hBmp == NULL);
  1294. if (FAILED(_pExtract->Extract(&_hBmp)))
  1295. {
  1296. _hBmp = NULL;
  1297. }
  1298. }
  1299. if (_hBmp && _lState == IRTIR_TASK_RUNNING)
  1300. {
  1301. TraceMsg(TF_DEFVIEW, "CExtractImageTask *EXTRACT* (path=%s, priority=%x)", _szPath, _dwPriority);
  1302. hr = InternalResume();
  1303. }
  1304. if (_lState != IRTIR_TASK_SUSPENDED || hr != E_PENDING)
  1305. {
  1306. _lState = IRTIR_TASK_FINISHED;
  1307. }
  1308. }
  1309. return hr;
  1310. }
  1311. STDMETHODIMP CExtractImageTask::Kill(BOOL fWait)
  1312. {
  1313. return E_NOTIMPL;
  1314. }
  1315. STDMETHODIMP CExtractImageTask::Suspend(void)
  1316. {
  1317. return E_NOTIMPL;
  1318. }
  1319. STDMETHODIMP CExtractImageTask::Resume(void)
  1320. {
  1321. return E_NOTIMPL;
  1322. }
  1323. HRESULT CExtractImageTask::InternalResume()
  1324. {
  1325. ASSERT(_hBmp != NULL);
  1326. BOOL bCache = (_dwFlags & IEIFLAG_CACHE);
  1327. if (bCache)
  1328. {
  1329. IShellFolder* psf = NULL;
  1330. if (SUCCEEDED(_pView->GetShellFolder(&psf)))
  1331. {
  1332. TCHAR szPath[MAX_PATH];
  1333. if (SUCCEEDED(DisplayNameOf(psf, _pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath))))
  1334. {
  1335. // Make sure we don't request to cache an item that is encrypted in a folder that is not
  1336. if (SFGAO_ENCRYPTED == SHGetAttributes(psf, _pidl, SFGAO_ENCRYPTED))
  1337. {
  1338. bCache = FALSE;
  1339. LPITEMIDLIST pidlParent = _pView->_GetViewPidl();
  1340. if (pidlParent)
  1341. {
  1342. if (SFGAO_ENCRYPTED == SHGetAttributes(NULL, pidlParent, SFGAO_ENCRYPTED))
  1343. {
  1344. bCache = TRUE;
  1345. }
  1346. #ifdef DEBUG
  1347. else
  1348. {
  1349. TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s is encrypted in unencrypted folder)", szPath);
  1350. }
  1351. #endif
  1352. ILFree(pidlParent);
  1353. }
  1354. }
  1355. // Make sure we don't request to cache an item that has differing ACLs applied
  1356. if (bCache)
  1357. {
  1358. PACL pdacl;
  1359. PSECURITY_DESCRIPTOR psd;
  1360. bCache = FALSE;
  1361. if (ERROR_SUCCESS == GetNamedSecurityInfo(szPath,
  1362. SE_FILE_OBJECT,
  1363. DACL_SECURITY_INFORMATION,
  1364. NULL,
  1365. NULL,
  1366. &pdacl,
  1367. NULL,
  1368. &psd))
  1369. {
  1370. SECURITY_DESCRIPTOR_CONTROL sdc;
  1371. DWORD dwRevision;
  1372. if (GetSecurityDescriptorControl(psd, &sdc, &dwRevision) && !(sdc & SE_DACL_PROTECTED))
  1373. {
  1374. if (pdacl)
  1375. {
  1376. PKNOWN_ACE pACE = (PKNOWN_ACE) FirstAce(pdacl);
  1377. if ((pACE->Header.AceType != ACCESS_DENIED_ACE_TYPE) || (pACE->Header.AceFlags & INHERITED_ACE))
  1378. {
  1379. bCache = TRUE;
  1380. }
  1381. #ifdef DEBUG
  1382. else
  1383. {
  1384. TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s has a non-inherited deny acl)", szPath);
  1385. }
  1386. #endif
  1387. }
  1388. else
  1389. {
  1390. bCache = TRUE; // NULL dacl == everyone all access
  1391. }
  1392. }
  1393. #ifdef DEBUG
  1394. else
  1395. {
  1396. TraceMsg(TF_DEFVIEW,"CExtractImageTask (%s has a protected dacl)", szPath);
  1397. }
  1398. #endif
  1399. LocalFree(psd);
  1400. }
  1401. }
  1402. }
  1403. psf->Release();
  1404. }
  1405. if (!bCache && _pView->_pDiskCache) // If we were asked to cache and are not for security reasons
  1406. {
  1407. DWORD dwLock;
  1408. if (SUCCEEDED(_pView->_pDiskCache->Open(STGM_WRITE, &dwLock)))
  1409. {
  1410. _pView->_pDiskCache->DeleteEntry(_szPath);
  1411. _pView->_pDiskCache->ReleaseLock(&dwLock);
  1412. SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL); // Keep open for 2 seconds, just in case
  1413. }
  1414. }
  1415. }
  1416. HRESULT hr = _pView->UpdateImageForItem(_dwTaskID, _hBmp, _iItem, _pidl, _szPath, _ftDateStamp, bCache, _dwPriority);
  1417. // UpdateImageForItem returns S_FALSE if it assumes ownership of bitmap
  1418. if (hr == S_FALSE)
  1419. {
  1420. _hBmp = NULL;
  1421. }
  1422. _lState = IRTIR_TASK_FINISHED;
  1423. return hr;
  1424. }
  1425. STDMETHODIMP_(ULONG) CExtractImageTask::IsRunning(void)
  1426. {
  1427. return _lState;
  1428. }
  1429. class CCategoryTask : public CRunnableTask
  1430. {
  1431. public:
  1432. STDMETHOD (RunInitRT)();
  1433. CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl);
  1434. private:
  1435. ~CCategoryTask();
  1436. CDefView* _pView;
  1437. LPITEMIDLIST _pidl;
  1438. ICategorizer* _pcat;
  1439. UINT _uId;
  1440. };
  1441. CCategoryTask::CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl)
  1442. : CRunnableTask(RTF_DEFAULT), _uId(uId), _pView(pView), _pcat(pView->_pcat)
  1443. {
  1444. _pcat->AddRef();
  1445. _pidl = ILClone(pidl);
  1446. _pView->_GlobeAnimation(TRUE);
  1447. _pView->_ShowSearchUI(TRUE);
  1448. InterlockedIncrement(&_pView->_cTasksOutstanding);
  1449. }
  1450. CCategoryTask::~CCategoryTask()
  1451. {
  1452. ATOMICRELEASE(_pcat);
  1453. ILFree(_pidl);
  1454. ENTERCRITICAL;
  1455. {
  1456. _pView->_cTasksCompleted++;
  1457. if (0 == InterlockedDecrement(&_pView->_cTasksOutstanding) && !_pView->_fGroupingMsgInFlight)
  1458. {
  1459. PostMessage(_pView->_hwndView, WM_DSV_GROUPINGDONE, 0, 0);
  1460. _pView->_fGroupingMsgInFlight = TRUE;
  1461. }
  1462. }
  1463. LEAVECRITICAL;
  1464. }
  1465. HRESULT CCategoryTask_Create(CDefView *pView, LPCITEMIDLIST pidl, UINT uId, IRunnableTask **ppTask)
  1466. {
  1467. *ppTask = NULL;
  1468. CCategoryTask * pNew = new CCategoryTask(pView, uId, pidl);
  1469. if (!pNew)
  1470. return E_OUTOFMEMORY;
  1471. *ppTask = SAFECAST(pNew, IRunnableTask *);
  1472. return S_OK;
  1473. }
  1474. STDMETHODIMP CCategoryTask::RunInitRT()
  1475. {
  1476. BOOL fSuccess = FALSE;
  1477. if (_pidl)
  1478. {
  1479. DWORD dwGroup = -1;
  1480. _pcat->GetCategory(1, (LPCITEMIDLIST*)&_pidl, &dwGroup);
  1481. CBackgroundGroupInfo* pbggi = new CBackgroundGroupInfo(_pidl, _uId, dwGroup);
  1482. if (pbggi)
  1483. {
  1484. _pidl = NULL; // Transferred ownership to BackgroundInfo
  1485. ENTERCRITICAL;
  1486. {
  1487. fSuccess = (-1 != DPA_AppendPtr(_pView->_hdpaGroupingListActive, pbggi));
  1488. }
  1489. LEAVECRITICAL;
  1490. if (!fSuccess)
  1491. {
  1492. delete pbggi;
  1493. }
  1494. }
  1495. }
  1496. return S_OK;
  1497. }
  1498. class CGetCommandStateTask : public CRunnableTask
  1499. {
  1500. public:
  1501. STDMETHODIMP RunInitRT();
  1502. STDMETHODIMP InternalResumeRT();
  1503. CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray);
  1504. private:
  1505. ~CGetCommandStateTask();
  1506. CDefView *_pView;
  1507. IUICommand *_puiCommand;
  1508. IShellItemArray *_psiItemArray;
  1509. };
  1510. HRESULT CGetCommandStateTask_Create(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray, IRunnableTask **ppTask)
  1511. {
  1512. *ppTask = NULL;
  1513. CGetCommandStateTask *pNew = new CGetCommandStateTask(pView, puiCommand, psiItemArray);
  1514. if (!pNew)
  1515. return E_OUTOFMEMORY;
  1516. *ppTask = SAFECAST(pNew, IRunnableTask *);
  1517. return S_OK;
  1518. }
  1519. CGetCommandStateTask::CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray)
  1520. : CRunnableTask(RTF_SUPPORTKILLSUSPEND)
  1521. {
  1522. _pView = pView;
  1523. _puiCommand = puiCommand;
  1524. _puiCommand->AddRef();
  1525. _psiItemArray = psiItemArray;
  1526. if (_psiItemArray)
  1527. _psiItemArray->AddRef();
  1528. }
  1529. CGetCommandStateTask::~CGetCommandStateTask()
  1530. {
  1531. ATOMICRELEASE(_puiCommand);
  1532. ATOMICRELEASE(_psiItemArray);
  1533. }
  1534. STDMETHODIMP CGetCommandStateTask::RunInitRT()
  1535. {
  1536. return S_OK;
  1537. }
  1538. STDMETHODIMP CGetCommandStateTask::InternalResumeRT()
  1539. {
  1540. // Don't want to interfere with the explorer view starting up, so give it a head start.
  1541. // we were told to either suspend or quit...
  1542. if (WaitForSingleObject(_hDone, 1000) == WAIT_OBJECT_0)
  1543. {
  1544. return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
  1545. }
  1546. UISTATE uis;
  1547. HRESULT hr = _puiCommand->get_State(_psiItemArray, TRUE, &uis);
  1548. if (SUCCEEDED(hr) && (uis==UIS_ENABLED))
  1549. {
  1550. _pView->_PostSelectionChangedMessage(LVIS_SELECTED);
  1551. }
  1552. return S_OK;
  1553. }