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.

370 lines
10 KiB

  1. #include "shellprv.h"
  2. #include "runtask.h"
  3. #include "prop.h"
  4. #include "thumbutil.h"
  5. #include <cowsite.h>
  6. static const GUID TOID_Thumbnail = { 0xadec3450, 0xe907, 0x11d0, {0xa5, 0x7b, 0x00, 0xc0, 0x4f, 0xc2, 0xf7, 0x6a} };
  7. class CThumbnail : public IThumbnail2, public IParentAndItem, public CObjectWithSite
  8. {
  9. public:
  10. CThumbnail(void);
  11. // IUnknown
  12. STDMETHODIMP_(ULONG) AddRef(void);
  13. STDMETHODIMP_(ULONG) Release(void);
  14. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  15. // IThumbnail
  16. STDMETHODIMP Init(HWND hwnd, UINT uMsg);
  17. STDMETHODIMP GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight);
  18. // IThumbnail2
  19. STDMETHODIMP GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight);
  20. // IParentAndItem
  21. STDMETHODIMP SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidlChild);
  22. STDMETHODIMP GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidlChild);
  23. private:
  24. ~CThumbnail(void);
  25. HRESULT _CreateTask(IShellFolder *psf, LPCITEMIDLIST pidlLast, DWORD dwItem, SIZE rgSize, IExtractImage *pei, IRunnableTask **pprt);
  26. HRESULT _BitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight);
  27. HRESULT _InitTaskCancelItems();
  28. LONG _cRef;
  29. HWND _hwnd;
  30. UINT _uMsg;
  31. IShellTaskScheduler *_pScheduler;
  32. IShellFolder *_psf;
  33. LPITEMIDLIST _pidl;
  34. };
  35. class CGetThumbnailTask : public CRunnableTask
  36. {
  37. public:
  38. CGetThumbnailTask(IShellFolder *psf, LPCITEMIDLIST pidl, IExtractImage *pei, HWND hwnd, UINT uMsg, DWORD dwItem, SIZE rgSize);
  39. STDMETHODIMP RunInitRT(void);
  40. private:
  41. ~CGetThumbnailTask();
  42. HRESULT _PrepImage(HBITMAP *phBmp);
  43. HRESULT _BitmapReady(HBITMAP hImage);
  44. IShellFolder *_psf;
  45. IExtractImage *_pei;
  46. HWND _hwnd;
  47. UINT _uMsg;
  48. DWORD _dwItem;
  49. SIZE _rgSize;
  50. LPITEMIDLIST _pidlFolder; // folder where we test the cache
  51. LPITEMIDLIST _pidlLast;
  52. WCHAR _szPath[MAX_PATH]; // the item in that in folder parsing name for cache test
  53. };
  54. CThumbnail::CThumbnail(void) : _cRef(1)
  55. {
  56. DllAddRef();
  57. }
  58. CThumbnail::~CThumbnail(void)
  59. {
  60. if (_pScheduler)
  61. {
  62. _pScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE);
  63. _pScheduler->Release();
  64. _pScheduler = NULL;
  65. }
  66. if (_psf)
  67. _psf->Release();
  68. ILFree(_pidl);
  69. DllRelease();
  70. }
  71. STDAPI CThumbnail_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  72. {
  73. HRESULT hr;
  74. CThumbnail *pThumb = new CThumbnail();
  75. if (pThumb)
  76. {
  77. hr = pThumb->QueryInterface(riid, ppv);
  78. pThumb->Release();
  79. }
  80. else
  81. hr = E_OUTOFMEMORY;
  82. return hr;
  83. }
  84. HRESULT CThumbnail::QueryInterface(REFIID riid, void **ppv)
  85. {
  86. static const QITAB qit[] = {
  87. QITABENT(CThumbnail, IThumbnail2),
  88. QITABENTMULTI(CThumbnail, IThumbnail, IThumbnail2),
  89. QITABENT(CThumbnail, IParentAndItem),
  90. QITABENT(CThumbnail, IObjectWithSite),
  91. { 0 },
  92. };
  93. return QISearch(this, qit, riid, ppv);
  94. }
  95. ULONG CThumbnail::AddRef(void)
  96. {
  97. return InterlockedIncrement(&_cRef);
  98. }
  99. ULONG CThumbnail::Release(void)
  100. {
  101. if (InterlockedDecrement(&_cRef) > 0)
  102. return _cRef;
  103. delete this;
  104. return 0;
  105. }
  106. // IThumbnail
  107. HRESULT CThumbnail::Init(HWND hwnd, UINT uMsg)
  108. {
  109. _hwnd = hwnd;
  110. _uMsg = uMsg;
  111. ASSERT(NULL == _pScheduler);
  112. return S_OK;
  113. }
  114. HRESULT CThumbnail::_InitTaskCancelItems()
  115. {
  116. if (!_pScheduler)
  117. {
  118. if (!_punkSite || FAILED(IUnknown_QueryService(_punkSite, SID_ShellTaskScheduler,
  119. IID_PPV_ARG(IShellTaskScheduler, &_pScheduler))))
  120. {
  121. CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER,
  122. IID_PPV_ARG(IShellTaskScheduler, &_pScheduler));
  123. }
  124. if (_pScheduler)
  125. {
  126. // make sure RemoveTasks() actually kills old tasks even if they're not done yet
  127. _pScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE);
  128. }
  129. }
  130. if (_pScheduler)
  131. {
  132. // Kill any old tasks in the scheduler.
  133. _pScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE);
  134. }
  135. return _pScheduler ? S_OK : E_FAIL;
  136. }
  137. HRESULT CThumbnail::_CreateTask(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwItem, SIZE rgSize, IExtractImage *pei, IRunnableTask **pprt)
  138. {
  139. *pprt = new CGetThumbnailTask(psf, pidl, pei, _hwnd, _uMsg, dwItem, rgSize);
  140. return *pprt ? S_OK : E_OUTOFMEMORY;
  141. }
  142. HRESULT CThumbnail::_BitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight)
  143. {
  144. LPCITEMIDLIST pidlLast;
  145. IShellFolder *psf;
  146. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  147. if (SUCCEEDED(hr))
  148. {
  149. IExtractImage *pei;
  150. hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractImage, &pei));
  151. if (SUCCEEDED(hr))
  152. {
  153. DWORD dwPriority = 0;
  154. DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
  155. SIZEL rgSize = {lWidth, lHeight};
  156. WCHAR szLocation[MAX_PATH];
  157. hr = pei->GetLocation(szLocation, ARRAYSIZE(szLocation), &dwPriority, &rgSize, SHGetCurColorRes(), &dwFlags);
  158. if (SUCCEEDED(hr))
  159. {
  160. if (S_OK == hr)
  161. {
  162. HBITMAP hbm;
  163. hr = pei->Extract(&hbm);
  164. if (SUCCEEDED(hr))
  165. {
  166. if (!PostMessage(_hwnd, _uMsg, dwItem, (LPARAM)hbm))
  167. {
  168. DeleteObject(hbm);
  169. }
  170. }
  171. }
  172. else
  173. hr = E_FAIL;
  174. }
  175. else if (E_PENDING == hr)
  176. {
  177. IRunnableTask *prt;
  178. hr = _CreateTask(psf, pidlLast, dwItem, rgSize, pei, &prt);
  179. if (SUCCEEDED(hr))
  180. {
  181. // Add the task to the scheduler.
  182. hr = _pScheduler->AddTask(prt, TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, dwPriority);
  183. prt->Release();
  184. }
  185. }
  186. pei->Release();
  187. }
  188. psf->Release();
  189. }
  190. return hr;
  191. }
  192. STDMETHODIMP CThumbnail::GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight)
  193. {
  194. HRESULT hr = _InitTaskCancelItems();
  195. if (pszFile)
  196. {
  197. LPITEMIDLIST pidl = ILCreateFromPathW(pszFile);
  198. if (pidl)
  199. {
  200. hr = _BitmapFromIDList(pidl, dwItem, lWidth, lHeight);
  201. ILFree(pidl);
  202. }
  203. else
  204. hr = E_FAIL;
  205. }
  206. return hr;
  207. }
  208. // IThumbnail2
  209. STDMETHODIMP CThumbnail::GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight)
  210. {
  211. HRESULT hr = _InitTaskCancelItems();
  212. if (pidl)
  213. {
  214. hr = _BitmapFromIDList(pidl, dwItem, lWidth, lHeight);
  215. }
  216. return hr;
  217. }
  218. // IParentAndItem
  219. STDMETHODIMP CThumbnail::SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidlChild)
  220. {
  221. return E_NOTIMPL;
  222. }
  223. STDMETHODIMP CThumbnail::GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidl)
  224. {
  225. return E_NOTIMPL;
  226. }
  227. CGetThumbnailTask::CGetThumbnailTask(IShellFolder *psf, LPCITEMIDLIST pidl, IExtractImage *pei, HWND hwnd, UINT uMsg, DWORD dwItem, SIZE rgSize)
  228. : CRunnableTask(RTF_DEFAULT), _pei(pei), _hwnd(hwnd), _uMsg(uMsg), _dwItem(dwItem), _psf(psf), _rgSize(rgSize)
  229. {
  230. SHGetIDListFromUnk(psf, &_pidlFolder); // failure handled later
  231. _pidlLast = ILClone(pidl); // failure handled later
  232. DisplayNameOf(psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, _szPath, ARRAYSIZE(_szPath));
  233. _pei->AddRef();
  234. _psf->AddRef();
  235. }
  236. CGetThumbnailTask::~CGetThumbnailTask()
  237. {
  238. ILFree(_pidlLast);
  239. ILFree(_pidlFolder);
  240. _pei->Release();
  241. _psf->Release();
  242. }
  243. HRESULT CGetThumbnailTask::_PrepImage(HBITMAP *phBmp)
  244. {
  245. HRESULT hr = E_FAIL;
  246. DIBSECTION ds;
  247. if (GetObject(*phBmp, sizeof(ds), &ds))
  248. {
  249. // the disk cache only supports 32 Bpp DIBS now, so we can ignore the palette issue...
  250. ASSERT(ds.dsBm.bmBitsPixel == 32);
  251. HPALETTE hPal = (SHGetCurColorRes() == 8) ? SHCreateShellPalette(NULL) : NULL;
  252. HBITMAP hBmpNew;
  253. if (ConvertDIBSECTIONToThumbnail((BITMAPINFO *)&ds.dsBmih, ds.dsBm.bmBits, &hBmpNew, &_rgSize,
  254. SHGetCurColorRes(), hPal, 0, FALSE))
  255. {
  256. DeleteObject(*phBmp);
  257. *phBmp = hBmpNew;
  258. }
  259. if (hPal)
  260. DeletePalette(hPal);
  261. }
  262. return hr;
  263. }
  264. HRESULT CGetThumbnailTask::_BitmapReady(HBITMAP hImage)
  265. {
  266. if (!PostMessage(_hwnd, _uMsg, _dwItem, (LPARAM)hImage))
  267. {
  268. DeleteObject(hImage);
  269. }
  270. return S_OK;
  271. }
  272. STDMETHODIMP CGetThumbnailTask::RunInitRT()
  273. {
  274. HRESULT hr = E_FAIL;
  275. // now get the date stamp and check the disk cache....
  276. FILETIME ftImageTimeStamp = {0,0};
  277. // do they support date stamps....
  278. IExtractImage2 *pei2;
  279. if (SUCCEEDED(_pei->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2))))
  280. {
  281. pei2->GetDateStamp(&ftImageTimeStamp);
  282. pei2->Release();
  283. }
  284. IShellFolder2 *psf2;
  285. if (IsNullTime(&ftImageTimeStamp) && _pidlLast && SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  286. {
  287. // fall back to this (most common case)
  288. GetDateProperty(psf2, _pidlLast, &SCID_WRITETIME, &ftImageTimeStamp);
  289. psf2->Release();
  290. }
  291. IShellImageStore *pStore;
  292. if (_pidlFolder &&
  293. SUCCEEDED(LoadFromIDList(CLSID_ShellThumbnailDiskCache, _pidlFolder, IID_PPV_ARG(IShellImageStore, &pStore))))
  294. {
  295. DWORD dwStoreLock;
  296. if (SUCCEEDED(pStore->Open(STGM_READ, &dwStoreLock)))
  297. {
  298. FILETIME ftCacheDateStamp;
  299. if ((S_OK == pStore->IsEntryInStore(_szPath, &ftCacheDateStamp)) &&
  300. ((0 == CompareFileTime(&ftCacheDateStamp, &ftImageTimeStamp)) || IsNullTime(&ftImageTimeStamp)))
  301. {
  302. HBITMAP hBmp;
  303. if (SUCCEEDED(pStore->GetEntry(_szPath, STGM_READ, &hBmp)))
  304. {
  305. _PrepImage(&hBmp);
  306. hr = _BitmapReady(hBmp);
  307. }
  308. }
  309. pStore->Close(&dwStoreLock);
  310. }
  311. pStore->Release();
  312. }
  313. if (FAILED(hr))
  314. {
  315. HBITMAP hbm;
  316. if (SUCCEEDED(_pei->Extract(&hbm)))
  317. {
  318. hr = _BitmapReady(hbm);
  319. }
  320. }
  321. return hr;
  322. }