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.

480 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. ASSERT( 0 != _cRef );
  107. ULONG cRef = InterlockedDecrement(&_cRef);
  108. if ( 0 == cRef )
  109. {
  110. delete this;
  111. }
  112. return cRef;
  113. }
  114. // IThumbnail
  115. HRESULT CThumbnail::Init(HWND hwnd, UINT uMsg)
  116. {
  117. _hwnd = hwnd;
  118. _uMsg = uMsg;
  119. ASSERT(NULL == _pTaskScheduler);
  120. return S_OK;
  121. }
  122. HRESULT CThumbnail::_InitTaskCancelItems()
  123. {
  124. if (!_pTaskScheduler)
  125. {
  126. if (SUCCEEDED(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTaskScheduler, &_pTaskScheduler))))
  127. {
  128. // make sure RemoveTasks() actually kills old tasks even if they're not done yet
  129. _pTaskScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE);
  130. }
  131. }
  132. if (_pTaskScheduler)
  133. {
  134. // Kill any old tasks in the scheduler.
  135. _pTaskScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE);
  136. }
  137. return _pTaskScheduler ? S_OK : E_FAIL;
  138. }
  139. HRESULT CThumbnail::_BitmapFromIDList(LPCITEMIDLIST pidl, LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight)
  140. {
  141. LPCITEMIDLIST pidlLast;
  142. IShellFolder *psf;
  143. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  144. if (SUCCEEDED(hr))
  145. {
  146. IExtractImage *pei;
  147. hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractImage, &pei));
  148. if (SUCCEEDED(hr))
  149. {
  150. DWORD dwPriority;
  151. DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
  152. SIZEL rgSize = {lWidth, lHeight};
  153. WCHAR szBufferW[MAX_PATH];
  154. hr = pei->GetLocation(szBufferW, ARRAYSIZE(szBufferW), &dwPriority, &rgSize, SHGetCurColorRes(), &dwFlags);
  155. if (SUCCEEDED(hr))
  156. {
  157. if (S_OK == hr)
  158. {
  159. HBITMAP hBitmap;
  160. hr = pei->Extract(&hBitmap);
  161. if (SUCCEEDED(hr))
  162. {
  163. hr = UpdateLogoCallback(dwItem, 0, hBitmap, NULL, TRUE);
  164. }
  165. }
  166. else
  167. hr = E_FAIL;
  168. }
  169. else if (E_PENDING == hr)
  170. {
  171. WCHAR szPath[MAX_PATH];
  172. if (NULL == pszFile)
  173. {
  174. DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
  175. pszFile = szPath;
  176. }
  177. // now get the date stamp and check the disk cache....
  178. FILETIME ftImageTimeStamp;
  179. BOOL fNoDateStamp = TRUE;
  180. // try it in the background...
  181. // od they support date stamps....
  182. IExtractImage2 *pei2;
  183. if (SUCCEEDED(pei->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2))))
  184. {
  185. if (SUCCEEDED(pei2->GetDateStamp(&ftImageTimeStamp)))
  186. {
  187. fNoDateStamp = FALSE; // we have a date stamp..
  188. }
  189. pei2->Release();
  190. }
  191. else
  192. {
  193. IShellFolder2 *psf2;
  194. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  195. {
  196. if (SUCCEEDED(GetDateProperty(psf2, pidlLast, &SCID_WRITETIME, &ftImageTimeStamp)))
  197. {
  198. fNoDateStamp = FALSE; // we have a date stamp..
  199. }
  200. psf2->Release();
  201. }
  202. }
  203. // if it is in the cache, and it is an uptodate image, then fetch from disk....
  204. // if the timestamps are wrong, then the extract code further down will then try
  205. // we only test the cache on NT5 because the templates are old on other platforms and
  206. // thus the image will be the wrong size...
  207. IRunnableTask *prt;
  208. if (IsOS(OS_WIN2000ORGREATER) && _InCache(pszFile, szBufferW, (fNoDateStamp ? NULL : &ftImageTimeStamp)))
  209. {
  210. hr = CDiskCacheTask_Create(this, _pImageStore, pszFile, szBufferW, dwItem, &rgSize, &prt);
  211. if (SUCCEEDED(hr))
  212. {
  213. // let go of the image store, so the task has the only ref and the lock..
  214. ATOMICRELEASE(_pImageStore);
  215. }
  216. }
  217. else
  218. {
  219. // Cannot hold the prt which is returned in a member variable since that
  220. // would be a circular reference
  221. hr = CExtractImageTask_Create(this, pei, L"", dwItem, -1, EITF_SAVEBITMAP | EITF_ALWAYSCALL, &prt);
  222. }
  223. if (SUCCEEDED(hr))
  224. {
  225. // Add the task to the scheduler.
  226. hr = _pTaskScheduler->AddTask(prt, TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, dwPriority);
  227. prt->Release();
  228. }
  229. }
  230. pei->Release();
  231. }
  232. psf->Release();
  233. }
  234. ATOMICRELEASE(_pImageStore);
  235. return hr;
  236. }
  237. STDMETHODIMP CThumbnail::GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight)
  238. {
  239. HRESULT hr = _InitTaskCancelItems();
  240. if (pszFile)
  241. {
  242. LPITEMIDLIST pidl = ILCreateFromPathW(pszFile);
  243. if (pidl)
  244. {
  245. hr = _BitmapFromIDList(pidl, pszFile, dwItem, lWidth, lHeight);
  246. ILFree(pidl);
  247. }
  248. else
  249. hr = E_FAIL;
  250. }
  251. return hr;
  252. }
  253. // IThumbnail2
  254. STDMETHODIMP CThumbnail::GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight)
  255. {
  256. HRESULT hr = _InitTaskCancelItems();
  257. if (pidl)
  258. {
  259. hr = _BitmapFromIDList(pidl, NULL, dwItem, lWidth, lHeight);
  260. }
  261. return hr;
  262. }
  263. // private stuff
  264. HRESULT CThumbnail::UpdateLogoCallback(DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache)
  265. {
  266. if (!PostMessage(_hwnd, _uMsg, dwItem, (LPARAM)hImage))
  267. {
  268. DeleteObject(hImage);
  269. }
  270. return S_OK;
  271. }
  272. REFTASKOWNERID CThumbnail::GetTOID()
  273. {
  274. return TOID_Thumbnail;
  275. }
  276. BOOL CThumbnail::_InCache(LPCWSTR pszItemPath, LPCWSTR pszGLocation, const FILETIME * pftDateStamp)
  277. {
  278. BOOL fRes = FALSE;
  279. HRESULT hr;
  280. if (_pImageStore)
  281. hr = S_OK;
  282. else
  283. {
  284. // init the cache only once, assume all items from same folder!
  285. WCHAR szName[MAX_PATH];
  286. StrCpyNW(szName, pszItemPath, ARRAYSIZE(szName));
  287. PathRemoveFileSpecW(szName);
  288. hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, szName, IID_PPV_ARG(IShellImageStore, &_pImageStore));
  289. }
  290. if (SUCCEEDED(hr))
  291. {
  292. DWORD dwStoreLock;
  293. hr = _pImageStore->Open(STGM_READ, &dwStoreLock);
  294. if (SUCCEEDED(hr))
  295. {
  296. FILETIME ftCacheDateStamp;
  297. hr = _pImageStore->IsEntryInStore(pszGLocation, &ftCacheDateStamp);
  298. if ((hr == S_OK) && (!pftDateStamp ||
  299. (pftDateStamp->dwLowDateTime == ftCacheDateStamp.dwLowDateTime &&
  300. pftDateStamp->dwHighDateTime == ftCacheDateStamp.dwHighDateTime)))
  301. {
  302. fRes = TRUE;
  303. }
  304. _pImageStore->Close(&dwStoreLock);
  305. }
  306. }
  307. return fRes;
  308. }
  309. HRESULT CDiskCacheTask_Create(CLogoBase * pView,
  310. IShellImageStore *pImageStore,
  311. LPCWSTR pszItem,
  312. LPCWSTR pszGLocation,
  313. DWORD dwItem,
  314. const SIZE * prgSize,
  315. IRunnableTask ** ppTask)
  316. {
  317. HRESULT hr = E_OUTOFMEMORY;
  318. CDiskCacheTask *pTask = new CDiskCacheTask;
  319. if (pTask)
  320. {
  321. hr = StringCchCopyW(pTask->_szItem, ARRAYSIZE(pTask->_szItem), pszItem);
  322. if (SUCCEEDED(hr))
  323. {
  324. hr = StringCchCopyW(pTask->_szGLocation, ARRAYSIZE(pTask->_szGLocation), pszGLocation);
  325. if (SUCCEEDED(hr))
  326. {
  327. pTask->_pView = pView;
  328. pTask->_pImageStore = pImageStore;
  329. pImageStore->AddRef();
  330. pTask->_dwItem = dwItem;
  331. pTask->m_rgSize = * prgSize;
  332. *ppTask = SAFECAST(pTask, IRunnableTask *);
  333. }
  334. }
  335. }
  336. return hr;
  337. }
  338. STDMETHODIMP CDiskCacheTask::RunInitRT()
  339. {
  340. // otherwise, run the task ....
  341. HBITMAP hBmp = NULL;
  342. DWORD dwLock;
  343. HRESULT hr = _pImageStore->Open(STGM_READ, &dwLock);
  344. if (SUCCEEDED(hr))
  345. {
  346. // at this point, we assume that it IS in the cache, and we already have a read lock on the cache...
  347. hr = _pImageStore->GetEntry(_szGLocation, STGM_READ, &hBmp);
  348. // release the lock, we don't need it...
  349. _pImageStore->Close(&dwLock);
  350. }
  351. ATOMICRELEASE(_pImageStore);
  352. if (hBmp)
  353. {
  354. PrepImage(&hBmp);
  355. _pView->UpdateLogoCallback(_dwItem, 0, hBmp, _szItem, TRUE);
  356. }
  357. // ensure we don't return the "we've suspended" value...
  358. if (hr == E_PENDING)
  359. hr = E_FAIL;
  360. return hr;
  361. }
  362. CDiskCacheTask::CDiskCacheTask() : CRunnableTask(RTF_DEFAULT)
  363. {
  364. }
  365. CDiskCacheTask::~CDiskCacheTask()
  366. {
  367. ATOMICRELEASE(_pImageStore);
  368. }
  369. HRESULT CDiskCacheTask::PrepImage(HBITMAP * phBmp)
  370. {
  371. ASSERT(phBmp && *phBmp);
  372. DIBSECTION rgDIB;
  373. if (!GetObject(*phBmp, sizeof(rgDIB), &rgDIB))
  374. {
  375. return E_FAIL;
  376. }
  377. // the disk cache only supports 32 Bpp DIBS now, so we can ignore the palette issue...
  378. ASSERT(rgDIB.dsBm.bmBitsPixel == 32);
  379. HBITMAP hBmpNew = NULL;
  380. HPALETTE hPal = NULL;
  381. if (SHGetCurColorRes() == 8)
  382. {
  383. hPal = SHCreateShellPalette(NULL);
  384. }
  385. IScaleAndSharpenImage2 * pScale;
  386. HRESULT hr = CoCreateInstance(CLSID_ThumbnailScaler, NULL,
  387. CLSCTX_INPROC_SERVER, IID_PPV_ARG(IScaleAndSharpenImage2, &pScale));
  388. if (SUCCEEDED(hr))
  389. {
  390. hr = pScale->ScaleSharpen2((BITMAPINFO *) &rgDIB.dsBmih,
  391. rgDIB.dsBm.bmBits, &hBmpNew, &m_rgSize, SHGetCurColorRes(),
  392. hPal, 0, FALSE);
  393. pScale->Release();
  394. }
  395. if (hPal)
  396. DeletePalette(hPal);
  397. if (SUCCEEDED(hr) && hBmpNew)
  398. {
  399. DeleteObject(*phBmp);
  400. *phBmp = hBmpNew;
  401. }
  402. return S_OK;
  403. }