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.

473 lines
14 KiB

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "runtask.h"
  4. #include "legacy.h"
  5. #include <ntquery.h> // defines some values used for fmtid and pid
  6. #define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
  7. DEFINE_SCID(SCID_WRITETIME, PSGUID_STORAGE, PID_STG_WRITETIME);
  8. class CThumbnail : public IThumbnail2, public CLogoBase
  9. {
  10. public:
  11. CThumbnail(void);
  12. // IUnknown
  13. STDMETHODIMP_(ULONG) AddRef(void);
  14. STDMETHODIMP_(ULONG) Release(void);
  15. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  16. // IThumbnail
  17. STDMETHODIMP Init(HWND hwnd, UINT uMsg);
  18. STDMETHODIMP GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight);
  19. // IThumbnail2
  20. STDMETHODIMP GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight);
  21. private:
  22. ~CThumbnail(void);
  23. LONG _cRef;
  24. HWND _hwnd;
  25. UINT _uMsg;
  26. IShellImageStore *_pImageStore;
  27. virtual IShellFolder * GetSF() {ASSERT(0);return NULL;};
  28. virtual HWND GetHWND() {ASSERT(0); return _hwnd;};
  29. HRESULT UpdateLogoCallback(DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache);
  30. REFTASKOWNERID GetTOID();
  31. BOOL _InCache(LPCWSTR pszItemPath, LPCWSTR pszGLocation, const FILETIME * pftDateStamp);
  32. HRESULT _BitmapFromIDList(LPCITEMIDLIST pidl, LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight);
  33. HRESULT _InitTaskCancelItems();
  34. };
  35. static const GUID TOID_Thumbnail = { 0xadec3450, 0xe907, 0x11d0, {0xa5, 0x7b, 0x00, 0xc0, 0x4f, 0xc2, 0xf7, 0x6a} };
  36. HRESULT CDiskCacheTask_Create(CLogoBase * pView,
  37. IShellImageStore *pImageStore,
  38. LPCWSTR pszItem,
  39. LPCWSTR pszGLocation,
  40. DWORD dwItem,
  41. IRunnableTask ** ppTask);
  42. class CDiskCacheTask : public CRunnableTask
  43. {
  44. public:
  45. CDiskCacheTask();
  46. STDMETHODIMP RunInitRT(void);
  47. friend HRESULT CDiskCacheTask_Create(CLogoBase * pView,
  48. IShellImageStore *pImageStore,
  49. LPCWSTR pszItem,
  50. LPCWSTR pszGLocation,
  51. DWORD dwItem,
  52. const SIZE * prgSize,
  53. IRunnableTask ** ppTask);
  54. private:
  55. ~CDiskCacheTask();
  56. HRESULT PrepImage(HBITMAP * phBmp);
  57. IShellImageStore *_pImageStore;
  58. WCHAR _szItem[MAX_PATH];
  59. WCHAR _szGLocation[MAX_PATH];
  60. CLogoBase * _pView;
  61. DWORD _dwItem;
  62. SIZE m_rgSize;
  63. };
  64. // CreateInstance
  65. HRESULT CThumbnail_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  66. {
  67. *ppunk = NULL;
  68. CThumbnail *pthumbnail = new CThumbnail();
  69. if (pthumbnail)
  70. {
  71. *ppunk = SAFECAST(pthumbnail, IThumbnail*);
  72. return S_OK;
  73. }
  74. return E_OUTOFMEMORY;
  75. }
  76. // Constructor / Destructor
  77. CThumbnail::CThumbnail(void) : _cRef(1)
  78. {
  79. DllAddRef();
  80. }
  81. CThumbnail::~CThumbnail(void)
  82. {
  83. if (_pTaskScheduler)
  84. {
  85. _pTaskScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE);
  86. _pTaskScheduler->Release();
  87. _pTaskScheduler = NULL;
  88. }
  89. DllRelease();
  90. }
  91. HRESULT CThumbnail::QueryInterface(REFIID riid, void **ppvObj)
  92. {
  93. static const QITAB qit[] = {
  94. QITABENT(CThumbnail, IThumbnail2),
  95. QITABENTMULTI(CThumbnail, IThumbnail, IThumbnail2),
  96. { 0 },
  97. };
  98. return QISearch(this, qit, riid, ppvObj);
  99. }
  100. ULONG CThumbnail::AddRef(void)
  101. {
  102. return InterlockedIncrement(&_cRef);
  103. }
  104. ULONG CThumbnail::Release(void)
  105. {
  106. if (InterlockedDecrement(&_cRef) > 0)
  107. return _cRef;
  108. delete this;
  109. return 0;
  110. }
  111. // IThumbnail
  112. HRESULT CThumbnail::Init(HWND hwnd, UINT uMsg)
  113. {
  114. _hwnd = hwnd;
  115. _uMsg = uMsg;
  116. ASSERT(NULL == _pTaskScheduler);
  117. return S_OK;
  118. }
  119. HRESULT CThumbnail::_InitTaskCancelItems()
  120. {
  121. if (!_pTaskScheduler)
  122. {
  123. if (SUCCEEDED(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTaskScheduler, &_pTaskScheduler))))
  124. {
  125. // make sure RemoveTasks() actually kills old tasks even if they're not done yet
  126. _pTaskScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE);
  127. }
  128. }
  129. if (_pTaskScheduler)
  130. {
  131. // Kill any old tasks in the scheduler.
  132. _pTaskScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE);
  133. }
  134. return _pTaskScheduler ? S_OK : E_FAIL;
  135. }
  136. HRESULT CThumbnail::_BitmapFromIDList(LPCITEMIDLIST pidl, LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight)
  137. {
  138. LPCITEMIDLIST pidlLast;
  139. IShellFolder *psf;
  140. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  141. if (SUCCEEDED(hr))
  142. {
  143. IExtractImage *pei;
  144. hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractImage, &pei));
  145. if (SUCCEEDED(hr))
  146. {
  147. DWORD dwPriority;
  148. DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
  149. SIZEL rgSize = {lWidth, lHeight};
  150. WCHAR szBufferW[MAX_PATH];
  151. hr = pei->GetLocation(szBufferW, ARRAYSIZE(szBufferW), &dwPriority, &rgSize, SHGetCurColorRes(), &dwFlags);
  152. if (SUCCEEDED(hr))
  153. {
  154. if (S_OK == hr)
  155. {
  156. HBITMAP hBitmap;
  157. hr = pei->Extract(&hBitmap);
  158. if (SUCCEEDED(hr))
  159. {
  160. hr = UpdateLogoCallback(dwItem, 0, hBitmap, NULL, TRUE);
  161. }
  162. }
  163. else
  164. hr = E_FAIL;
  165. }
  166. else if (E_PENDING == hr)
  167. {
  168. WCHAR szPath[MAX_PATH];
  169. if (NULL == pszFile)
  170. {
  171. DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
  172. pszFile = szPath;
  173. }
  174. // now get the date stamp and check the disk cache....
  175. FILETIME ftImageTimeStamp;
  176. BOOL fNoDateStamp = TRUE;
  177. // try it in the background...
  178. // od they support date stamps....
  179. IExtractImage2 *pei2;
  180. if (SUCCEEDED(pei->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2))))
  181. {
  182. if (SUCCEEDED(pei2->GetDateStamp(&ftImageTimeStamp)))
  183. {
  184. fNoDateStamp = FALSE; // we have a date stamp..
  185. }
  186. pei2->Release();
  187. }
  188. else
  189. {
  190. IShellFolder2 *psf2;
  191. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  192. {
  193. if (SUCCEEDED(GetDateProperty(psf2, pidlLast, &SCID_WRITETIME, &ftImageTimeStamp)))
  194. {
  195. fNoDateStamp = FALSE; // we have a date stamp..
  196. }
  197. psf2->Release();
  198. }
  199. }
  200. // if it is in the cache, and it is an uptodate image, then fetch from disk....
  201. // if the timestamps are wrong, then the extract code further down will then try
  202. // we only test the cache on NT5 because the templates are old on other platforms and
  203. // thus the image will be the wrong size...
  204. IRunnableTask *prt;
  205. if (IsOS(OS_WIN2000ORGREATER) && _InCache(pszFile, szBufferW, (fNoDateStamp ? NULL : &ftImageTimeStamp)))
  206. {
  207. hr = CDiskCacheTask_Create(this, _pImageStore, pszFile, szBufferW, dwItem, &rgSize, &prt);
  208. if (SUCCEEDED(hr))
  209. {
  210. // let go of the image store, so the task has the only ref and the lock..
  211. ATOMICRELEASE(_pImageStore);
  212. }
  213. }
  214. else
  215. {
  216. // Cannot hold the prt which is returned in a member variable since that
  217. // would be a circular reference
  218. hr = CExtractImageTask_Create(this, pei, L"", dwItem, -1, EITF_SAVEBITMAP | EITF_ALWAYSCALL, &prt);
  219. }
  220. if (SUCCEEDED(hr))
  221. {
  222. // Add the task to the scheduler.
  223. hr = _pTaskScheduler->AddTask(prt, TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, dwPriority);
  224. prt->Release();
  225. }
  226. }
  227. pei->Release();
  228. }
  229. psf->Release();
  230. }
  231. ATOMICRELEASE(_pImageStore);
  232. return hr;
  233. }
  234. STDMETHODIMP CThumbnail::GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight)
  235. {
  236. HRESULT hr = _InitTaskCancelItems();
  237. if (pszFile)
  238. {
  239. LPITEMIDLIST pidl = ILCreateFromPathW(pszFile);
  240. if (pidl)
  241. {
  242. hr = _BitmapFromIDList(pidl, pszFile, dwItem, lWidth, lHeight);
  243. ILFree(pidl);
  244. }
  245. else
  246. hr = E_FAIL;
  247. }
  248. return hr;
  249. }
  250. // IThumbnail2
  251. STDMETHODIMP CThumbnail::GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight)
  252. {
  253. HRESULT hr = _InitTaskCancelItems();
  254. if (pidl)
  255. {
  256. hr = _BitmapFromIDList(pidl, NULL, dwItem, lWidth, lHeight);
  257. }
  258. return hr;
  259. }
  260. // private stuff
  261. HRESULT CThumbnail::UpdateLogoCallback(DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache)
  262. {
  263. if (!PostMessage(_hwnd, _uMsg, dwItem, (LPARAM)hImage))
  264. {
  265. DeleteObject(hImage);
  266. }
  267. return S_OK;
  268. }
  269. REFTASKOWNERID CThumbnail::GetTOID()
  270. {
  271. return TOID_Thumbnail;
  272. }
  273. BOOL CThumbnail::_InCache(LPCWSTR pszItemPath, LPCWSTR pszGLocation, const FILETIME * pftDateStamp)
  274. {
  275. BOOL fRes = FALSE;
  276. HRESULT hr;
  277. if (_pImageStore)
  278. hr = S_OK;
  279. else
  280. {
  281. // init the cache only once, assume all items from same folder!
  282. WCHAR szName[MAX_PATH];
  283. StrCpyNW(szName, pszItemPath, ARRAYSIZE(szName));
  284. PathRemoveFileSpecW(szName);
  285. hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, szName, IID_PPV_ARG(IShellImageStore, &_pImageStore));
  286. }
  287. if (SUCCEEDED(hr))
  288. {
  289. DWORD dwStoreLock;
  290. hr = _pImageStore->Open(STGM_READ, &dwStoreLock);
  291. if (SUCCEEDED(hr))
  292. {
  293. FILETIME ftCacheDateStamp;
  294. hr = _pImageStore->IsEntryInStore(pszGLocation, &ftCacheDateStamp);
  295. if ((hr == S_OK) && (!pftDateStamp ||
  296. (pftDateStamp->dwLowDateTime == ftCacheDateStamp.dwLowDateTime &&
  297. pftDateStamp->dwHighDateTime == ftCacheDateStamp.dwHighDateTime)))
  298. {
  299. fRes = TRUE;
  300. }
  301. _pImageStore->Close(&dwStoreLock);
  302. }
  303. }
  304. return fRes;
  305. }
  306. HRESULT CDiskCacheTask_Create(CLogoBase * pView,
  307. IShellImageStore *pImageStore,
  308. LPCWSTR pszItem,
  309. LPCWSTR pszGLocation,
  310. DWORD dwItem,
  311. const SIZE * prgSize,
  312. IRunnableTask ** ppTask)
  313. {
  314. CDiskCacheTask *pTask = new CDiskCacheTask;
  315. if (pTask == NULL)
  316. {
  317. return E_OUTOFMEMORY;
  318. }
  319. StrCpyW(pTask->_szItem, pszItem);
  320. StrCpyW(pTask->_szGLocation, pszGLocation);
  321. pTask->_pView = pView;
  322. pTask->_pImageStore = pImageStore;
  323. pImageStore->AddRef();
  324. pTask->_dwItem = dwItem;
  325. pTask->m_rgSize = * prgSize;
  326. *ppTask = SAFECAST(pTask, IRunnableTask *);
  327. return S_OK;
  328. }
  329. STDMETHODIMP CDiskCacheTask::RunInitRT()
  330. {
  331. // otherwise, run the task ....
  332. HBITMAP hBmp = NULL;
  333. DWORD dwLock;
  334. HRESULT hr = _pImageStore->Open(STGM_READ, &dwLock);
  335. if (SUCCEEDED(hr))
  336. {
  337. // at this point, we assume that it IS in the cache, and we already have a read lock on the cache...
  338. hr = _pImageStore->GetEntry(_szGLocation, STGM_READ, &hBmp);
  339. // release the lock, we don't need it...
  340. _pImageStore->Close(&dwLock);
  341. }
  342. ATOMICRELEASE(_pImageStore);
  343. if (hBmp)
  344. {
  345. PrepImage(&hBmp);
  346. _pView->UpdateLogoCallback(_dwItem, 0, hBmp, _szItem, TRUE);
  347. }
  348. // ensure we don't return the "we've suspended" value...
  349. if (hr == E_PENDING)
  350. hr = E_FAIL;
  351. return hr;
  352. }
  353. CDiskCacheTask::CDiskCacheTask() : CRunnableTask(RTF_DEFAULT)
  354. {
  355. }
  356. CDiskCacheTask::~CDiskCacheTask()
  357. {
  358. ATOMICRELEASE(_pImageStore);
  359. }
  360. HRESULT CDiskCacheTask::PrepImage(HBITMAP * phBmp)
  361. {
  362. ASSERT(phBmp && *phBmp);
  363. DIBSECTION rgDIB;
  364. if (!GetObject(*phBmp, sizeof(rgDIB), &rgDIB))
  365. {
  366. return E_FAIL;
  367. }
  368. // the disk cache only supports 32 Bpp DIBS now, so we can ignore the palette issue...
  369. ASSERT(rgDIB.dsBm.bmBitsPixel == 32);
  370. HBITMAP hBmpNew = NULL;
  371. HPALETTE hPal = NULL;
  372. if (SHGetCurColorRes() == 8)
  373. {
  374. hPal = SHCreateShellPalette(NULL);
  375. }
  376. IScaleAndSharpenImage2 * pScale;
  377. HRESULT hr = CoCreateInstance(CLSID_ThumbnailScaler, NULL,
  378. CLSCTX_INPROC_SERVER, IID_PPV_ARG(IScaleAndSharpenImage2, &pScale));
  379. if (SUCCEEDED(hr))
  380. {
  381. hr = pScale->ScaleSharpen2((BITMAPINFO *) &rgDIB.dsBmih,
  382. rgDIB.dsBm.bmBits, &hBmpNew, &m_rgSize, SHGetCurColorRes(),
  383. hPal, 0, FALSE);
  384. pScale->Release();
  385. }
  386. if (hPal)
  387. DeletePalette(hPal);
  388. if (SUCCEEDED(hr) && hBmpNew)
  389. {
  390. DeleteObject(*phBmp);
  391. *phBmp = hBmpNew;
  392. }
  393. return S_OK;
  394. }