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.

1409 lines
49 KiB

  1. #include "shellprv.h"
  2. #include "filefldr.h"
  3. #include "ids.h"
  4. #include "prop.h"
  5. #include "copy.h"
  6. // If these values are modified, the logic in Extract() must be modified too.
  7. #define SMALLEST_THUMBNAIL_WITH_4_PREVIEWS 96
  8. #define MAX_MINIPREVIEWS_COLLECT 8 // collect more than we're going to show, just in case one fails
  9. #define MAX_MINIPREVIEWS 4
  10. #define FOLDER_GUID TEXT("{A42CD7B6-E9B9-4D02-B7A6-288B71AD28BA}")
  11. // Function in defview
  12. void SHGetThumbnailSize(SIZE *psize);
  13. typedef enum
  14. {
  15. MINIPREVIEW_LAYOUT_1 = 0,
  16. MINIPREVIEW_LAYOUT_4 = 1,
  17. } MINIPREVIEW_LAYOUT;
  18. // The size of the mini-thumbnails for each thumbnail size. For each thumbnail
  19. // size, there is a mini-thumbnail size for single layout and 2x2 layout.
  20. LONG const alFolder120MinipreviewSize[] = {104, 48};
  21. LONG const alFolder96MinipreviewSize[] = {82, 40};
  22. LONG const alFolder80MinipreviewSize[] = {69, 32};
  23. // These are the margins at which the mini-thumbnails appear within the main thumbnail.
  24. // For thumbnails with only one large minipreview, we can just use x1,y1.
  25. LONG const alFolder120MinipreviewOffsets[] = { 8, 64, 13, 67 }; // x1, x2, y1, y2
  26. LONG const alFolder96MinipreviewOffsets[] = { 7, 49, 11, 52 }; // x1, x2, y1, y2
  27. LONG const alFolder80MinipreviewOffsets[] = { 5, 42, 9, 45 }; // x1, x2, y1, y2
  28. void FreeMiniPreviewPidls(LPITEMIDLIST apidlPreviews[], UINT cpidlPreviews);
  29. // Helper functions
  30. MINIPREVIEW_LAYOUT _GetMiniPreviewLayout(SIZE size);
  31. void _GetMiniPreviewLocations(MINIPREVIEW_LAYOUT uLayout, SIZE sizeRequested, SIZE *psizeFolderBmp,
  32. POINT aptOrigins[], SIZE *psizeMiniPreview);
  33. HRESULT _DrawMiniPreviewBackground(HDC hdc, SIZE sizeFolderBmp, BOOL fAlpha, BOOL* pIsAlpha, RGBQUAD *prgb);
  34. HBITMAP _CreateDIBSection(HDC hdcBmp, int cx, int cy);
  35. HRESULT _CreateMainRenderingDC(HDC* phdc, HBITMAP* phBmpThumbnail, HBITMAP* phbmpOld, int cx, int cy, RGBQUAD** pprgb);
  36. void _DestroyMainRenderingDC(HDC hdc, HBITMAP hbmpOld);
  37. HRESULT _AddBitmap(HDC hdc, HBITMAP hbmpSub, POINT ptMargin, SIZE sizeDest, SIZE sizeSource, BOOL fAlphaSource, BOOL fAlphaDest, RGBQUAD *prgbDest, SIZE cxFolderSize);
  38. // The files that can serve as thumbnails for folders:
  39. const LPCWSTR c_szFolderThumbnailPaths[] = { L"folder.jpg", L"folder.gif" };
  40. // We always have four now.
  41. MINIPREVIEW_LAYOUT _GetMiniPreviewLayout(SIZE size)
  42. {
  43. return MINIPREVIEW_LAYOUT_4;
  44. }
  45. void FreeMiniPreviewPidls(LPITEMIDLIST apidlPreviews[], UINT cpidlPreviews)
  46. {
  47. for (UINT u = 0; u < cpidlPreviews; u++)
  48. {
  49. ILFree(apidlPreviews[u]);
  50. }
  51. }
  52. /**
  53. * In: uLayout - The layout (1 or 4 mini previews)
  54. * sizeRequested - The size of the thumbnail we are trying to generate
  55. *
  56. * Out:
  57. * -psizeFolderBmp is set to
  58. * the size of the bitmap.
  59. *
  60. * -aptOrigins array is filled in with the locations of the n minipreviews
  61. * (note, aptOrigins is assumed to have MAX_MINIPREVIEWS cells)
  62. * The size of the minipreviews (square) is returned in pSizeMinipreview;
  63. */
  64. void _GetMiniPreviewLocations(MINIPREVIEW_LAYOUT uLayout, SIZE sizeRequested, SIZE *psizeFolderBmp,
  65. POINT aptOrigins[], SIZE *psizeMiniPreview)
  66. {
  67. const LONG *alOffsets;
  68. LONG lSize; // One of the standard sizes, that we have a folder bitmap for.
  69. LONG lSmallestDimension = min(sizeRequested.cx, sizeRequested.cy);
  70. if (lSmallestDimension > 96) // For stuff bigger than 96, we use the 120 size
  71. {
  72. lSize = 120;
  73. alOffsets = alFolder120MinipreviewOffsets;
  74. psizeMiniPreview->cx = psizeMiniPreview->cy = alFolder120MinipreviewSize[uLayout];
  75. }
  76. else if (lSmallestDimension > 80) // For stuff bigger than 80, but <= 96, we use the 96 size.
  77. {
  78. lSize = 96;
  79. alOffsets = alFolder96MinipreviewOffsets;
  80. psizeMiniPreview->cx = psizeMiniPreview->cy = alFolder96MinipreviewSize[uLayout];
  81. }
  82. else // For stuff <= 80, we use 80.
  83. {
  84. lSize = 80;
  85. alOffsets = alFolder80MinipreviewOffsets;
  86. psizeMiniPreview->cx = psizeMiniPreview->cy = alFolder80MinipreviewSize[uLayout];
  87. }
  88. psizeFolderBmp->cx = psizeFolderBmp->cy = lSize;
  89. COMPILETIME_ASSERT(4 == MAX_MINIPREVIEWS);
  90. aptOrigins[0].x = alOffsets[0];
  91. aptOrigins[0].y = alOffsets[2];
  92. aptOrigins[1].x = alOffsets[1];
  93. aptOrigins[1].y = alOffsets[2];
  94. aptOrigins[2].x = alOffsets[0];
  95. aptOrigins[2].y = alOffsets[3];
  96. aptOrigins[3].x = alOffsets[1];
  97. aptOrigins[3].y = alOffsets[3];
  98. }
  99. HBITMAP _CreateDIBSection(HDC h, int cx, int cy, RGBQUAD** pprgb)
  100. {
  101. BITMAPINFO bi = {0};
  102. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  103. bi.bmiHeader.biWidth = cx;
  104. bi.bmiHeader.biHeight = cy;
  105. bi.bmiHeader.biPlanes = 1;
  106. bi.bmiHeader.biBitCount = 32;
  107. bi.bmiHeader.biCompression = BI_RGB;
  108. return CreateDIBSection(h, &bi, DIB_RGB_COLORS, (void**)pprgb, NULL, 0);
  109. }
  110. // Pre multiplies alpha channel
  111. void PreProcessDIB(int cx, int cy, RGBQUAD* pargb)
  112. {
  113. int cTotal = cx * cy;
  114. for (int i = 0; i < cTotal; i++)
  115. {
  116. RGBQUAD* prgb = &pargb[i];
  117. if (prgb->rgbReserved != 0)
  118. {
  119. prgb->rgbRed = ((prgb->rgbRed * prgb->rgbReserved) + 128) / 255;
  120. prgb->rgbGreen = ((prgb->rgbGreen * prgb->rgbReserved) + 128) / 255;
  121. prgb->rgbBlue = ((prgb->rgbBlue * prgb->rgbReserved) + 128) / 255;
  122. }
  123. else
  124. {
  125. *((DWORD*)prgb) = 0;
  126. }
  127. }
  128. }
  129. // Is there an alpha channel? Check for a non-zero alpha byte.
  130. BOOL _HasAlpha(RECT rc, int cx, RGBQUAD *pargb)
  131. {
  132. for (int y = rc.top; y < rc.bottom; y++)
  133. {
  134. for (int x = rc.left; x < rc.right; x++)
  135. {
  136. int iOffset = y * cx;
  137. if (pargb[x + iOffset].rgbReserved != 0)
  138. return TRUE;
  139. }
  140. }
  141. return FALSE;
  142. }
  143. /** In:
  144. * fAlpha: Do we want the folder background to have an alpha channel?
  145. * sizeFolderBmp: size of the thumbnail
  146. *
  147. * Out:
  148. * pIsAlpha: Did we get what we wanted, if we wanted an alpha channel?
  149. * (e.g. we won't get it if we're in < 24bit mode.)
  150. */
  151. HRESULT _DrawMiniPreviewBackground(HDC hdc, SIZE sizeFolderBmp, BOOL fAlpha, BOOL* pfIsAlpha, RGBQUAD *prgb)
  152. {
  153. HRESULT hr = E_FAIL;
  154. HICON hicon = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_FOLDER), IMAGE_ICON, sizeFolderBmp.cx, sizeFolderBmp.cy, 0);
  155. if (hicon)
  156. {
  157. *pfIsAlpha = FALSE;
  158. if (fAlpha)
  159. {
  160. // Try to blt an alpha channel icon into the dc
  161. ICONINFO io;
  162. if (GetIconInfo(hicon, &io))
  163. {
  164. BITMAP bm;
  165. if (GetObject(io.hbmColor, sizeof(bm), &bm))
  166. {
  167. if (bm.bmBitsPixel == 32)
  168. {
  169. HDC hdcSrc = CreateCompatibleDC(hdc);
  170. if (hdcSrc)
  171. {
  172. HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcSrc, io.hbmColor);
  173. BitBlt(hdc, 0, 0, sizeFolderBmp.cx, sizeFolderBmp.cy, hdcSrc, 0, 0, SRCCOPY);
  174. // Preprocess the alpha
  175. PreProcessDIB(sizeFolderBmp.cx, sizeFolderBmp.cy, prgb);
  176. *pfIsAlpha = TRUE;
  177. SelectObject(hdcSrc, hbmpOld);
  178. DeleteDC(hdcSrc);
  179. }
  180. }
  181. }
  182. DeleteObject(io.hbmColor);
  183. DeleteObject(io.hbmMask);
  184. }
  185. }
  186. if (!*pfIsAlpha)
  187. {
  188. // Didn't create an alpha bitmap
  189. // We're filling the background with background window color.
  190. RECT rc = { 0, 0, (long)sizeFolderBmp.cx + 1, (long)sizeFolderBmp.cy + 1};
  191. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_WINDOW));
  192. // Then drawing the icon on top.
  193. DrawIconEx(hdc, 0, 0, hicon, sizeFolderBmp.cx, sizeFolderBmp.cy, 0, NULL, DI_NORMAL);
  194. // This may have resulted in an alpha channel - we need to know. (If it
  195. // did, then when we add a nonalpha minibitmap to this main one, we need to restore
  196. // the nuked out alpha channel)
  197. // Check if we have alpha (prgb is the bits for the DIB of size sizeFolderBmp):
  198. rc.right = sizeFolderBmp.cx;
  199. rc.bottom = sizeFolderBmp.cy;
  200. *pfIsAlpha = _HasAlpha(rc, sizeFolderBmp.cx, prgb);
  201. }
  202. DestroyIcon(hicon);
  203. hr = S_OK;
  204. }
  205. return hr;
  206. }
  207. BOOL DoesFolderContainLogo(LPCITEMIDLIST pidlFull)
  208. {
  209. BOOL bRet = FALSE;
  210. IPropertyBag * pPropBag;
  211. if (SUCCEEDED(SHGetViewStatePropertyBag(pidlFull, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &pPropBag))))
  212. {
  213. TCHAR szLogo[MAX_PATH];
  214. szLogo[0] = 0;
  215. if (SUCCEEDED(SHPropertyBag_ReadStr(pPropBag, TEXT("Logo"), szLogo, ARRAYSIZE(szLogo))) && szLogo[0])
  216. {
  217. bRet = TRUE;
  218. }
  219. pPropBag->Release();
  220. }
  221. return bRet;
  222. }
  223. BOOL DoesFolderContainFolderJPG(IShellFolder *psf, LPCITEMIDLIST pidl)
  224. {
  225. BOOL bRet = FALSE;
  226. // return false if there's not folder.jpg, or if folder.jpg is a folder (doh!)
  227. IShellFolder *psfSubfolder;
  228. // SHBTO can deal with NULL psf, he turns it into psfDesktop
  229. if (SUCCEEDED(SHBindToObject(psf, IID_X_PPV_ARG(IShellFolder, pidl, &psfSubfolder))))
  230. {
  231. for (int i = 0; i < ARRAYSIZE(c_szFolderThumbnailPaths); i++)
  232. {
  233. DWORD dwFlags = SFGAO_FILESYSTEM | SFGAO_FOLDER;
  234. LPITEMIDLIST pidlItem;
  235. if (SUCCEEDED(psfSubfolder->ParseDisplayName(NULL, NULL, (LPOLESTR)c_szFolderThumbnailPaths[i], NULL, &pidlItem, &dwFlags)))
  236. {
  237. ILFree(pidlItem);
  238. if ((dwFlags & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM)
  239. {
  240. bRet = TRUE;
  241. break;
  242. }
  243. }
  244. }
  245. psfSubfolder->Release();
  246. }
  247. return bRet;
  248. }
  249. BOOL _IsShortcutTargetACandidate(IShellFolder *psf, LPCITEMIDLIST pidlPreview, BOOL *pbTryCached)
  250. {
  251. BOOL bRet = FALSE;
  252. *pbTryCached = TRUE;
  253. IShellLink *psl;
  254. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlPreview, IID_PPV_ARG_NULL(IShellLink, &psl))))
  255. {
  256. LPITEMIDLIST pidlTarget = NULL;
  257. if (SUCCEEDED(psl->GetIDList(&pidlTarget)) && pidlTarget)
  258. {
  259. DWORD dwTargetFlags = SFGAO_FOLDER;
  260. if (SUCCEEDED(SHGetNameAndFlags(pidlTarget, 0, NULL, 0, &dwTargetFlags)))
  261. {
  262. // return true if its not a folder, or if the folder contains a logo
  263. // note that this is kinda like recursing into the below function again
  264. bRet = (0 == (dwTargetFlags & SFGAO_FOLDER));
  265. if (!bRet)
  266. {
  267. bRet = (DoesFolderContainLogo(pidlTarget) || DoesFolderContainFolderJPG(NULL, pidlTarget));
  268. if (bRet)
  269. {
  270. // It's a logo folder, don't try the cached image.
  271. *pbTryCached = FALSE;
  272. }
  273. }
  274. }
  275. ILFree(pidlTarget);
  276. }
  277. psl->Release();
  278. }
  279. return bRet;
  280. }
  281. BOOL _IsMiniPreviewCandidate(IShellFolder *psf, LPCITEMIDLIST pidl, BOOL *pbTryCached)
  282. {
  283. BOOL bRet = FALSE;
  284. DWORD dwAttr = SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_LINK | SFGAO_FILESYSANCESTOR);
  285. *pbTryCached = TRUE;
  286. // if its a folder, check and see if its got a logo
  287. // note that folder shortcuts will have both folder and link, and since we check folder first, we won't recurse into folder shortcuts
  288. // dont do anything unless pidl is a folder on a real filesystem (i.e. dont walk into zip/cab)
  289. if ((dwAttr & (SFGAO_FOLDER | SFGAO_FILESYSANCESTOR)) == (SFGAO_FOLDER | SFGAO_FILESYSANCESTOR))
  290. {
  291. LPITEMIDLIST pidlParent;
  292. if (SUCCEEDED(SHGetIDListFromUnk(psf, &pidlParent)))
  293. {
  294. LPITEMIDLIST pidlFull;
  295. if (SUCCEEDED(SHILCombine(pidlParent, pidl, &pidlFull)))
  296. {
  297. bRet = DoesFolderContainLogo(pidlFull);
  298. ILFree(pidlFull);
  299. }
  300. ILFree(pidlParent);
  301. }
  302. if (!bRet)
  303. {
  304. // no logo image, check for a "folder.jpg"
  305. // if its not there, then don't display pidl as a mini-preview, as it would recurse and produce dumb-looking 1/16 scale previews
  306. bRet = DoesFolderContainFolderJPG(psf, pidl);
  307. }
  308. if (bRet)
  309. {
  310. // For logo folders, we don't look for a cached image (cached image won't have alpha, which we want)
  311. *pbTryCached = FALSE;
  312. }
  313. }
  314. else
  315. {
  316. // Only if its not a link, or if its a link to a valid candidate, then we can get its extractor
  317. if (0 == (dwAttr & SFGAO_LINK) ||
  318. _IsShortcutTargetACandidate(psf, pidl, pbTryCached))
  319. {
  320. IExtractImage *pei;
  321. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IExtractImage, NULL, &pei))))
  322. {
  323. bRet = TRUE;
  324. pei->Release();
  325. }
  326. }
  327. }
  328. return bRet;
  329. }
  330. // We return the bits to the dibsection in the dc, if asked for. We need this for preprocessing the alpha channel,
  331. // if one exists.
  332. HRESULT _CreateMainRenderingDC(HDC* phdc, HBITMAP* phbmp, HBITMAP* phbmpOld, int cx, int cy, RGBQUAD** pprgb)
  333. {
  334. HRESULT hr = E_OUTOFMEMORY;
  335. HDC hdc = GetDC(NULL);
  336. if (hdc)
  337. {
  338. *phdc = CreateCompatibleDC(hdc);
  339. if (*phdc)
  340. {
  341. RGBQUAD *prgbDummy;
  342. *phbmp = _CreateDIBSection(*phdc, cx, cy, &prgbDummy);
  343. if (*phbmp)
  344. {
  345. *phbmpOld = (HBITMAP) SelectObject(*phdc, *phbmp);
  346. if (pprgb)
  347. *pprgb = prgbDummy;
  348. hr = S_OK;
  349. }
  350. else
  351. {
  352. DeleteDC(*phdc);
  353. }
  354. }
  355. ReleaseDC(NULL, hdc);
  356. }
  357. return hr;
  358. }
  359. void _DestroyMainRenderingDC(HDC hdc, HBITMAP hbmpOld) // Unselects the bitmap, and deletes the Dc
  360. {
  361. if (hbmpOld)
  362. SelectObject(hdc, hbmpOld);
  363. DeleteDC(hdc);
  364. }
  365. // We just blt'd a nonalpha guy into an alpha'd bitmap. This nuked out the alpha channel.
  366. // Repair it by setting alpha channel to 0xff (opaque).
  367. void _SetAlpha(RECT rc, SIZE sizeBmp, RGBQUAD *pargb)
  368. {
  369. for (int y = (sizeBmp.cy - rc.bottom); y < (sizeBmp.cy - rc.top); y++) // Origin at bottom left.
  370. {
  371. int iOffset = y * sizeBmp.cx;
  372. for (int x = rc.left; x < rc.right; x++)
  373. {
  374. pargb[x + iOffset].rgbReserved = 0xff;
  375. }
  376. }
  377. }
  378. /**
  379. * In
  380. * hbmpSub - little bitmap that we're adding to the thumbnail bitmap.
  381. * ptMargin - where we're adding it on the destination thumbnail bitmap.
  382. * sizeDest - how big it needs to be on the destination thumbnail bitmap.
  383. * sizeSource - how bit it is.
  384. * fAlphaSource - does the bitmap we're adding have an alpha channel?
  385. * fAlphaDest - does what we're adding it to, have an alpha channel?
  386. * prgbDest - the bits of the destination bitmap - needed if we add a non-alpha bitmap
  387. * to an alpha background, so we can reset the alpha.
  388. * sizeFolderBmp - the size of the destination bitmap - need this along with prgbDest.
  389. */
  390. HRESULT _AddBitmap(HDC hdc, HBITMAP hbmpSub, POINT ptMargin, SIZE sizeDest, SIZE sizeSource, BOOL fAlphaSource, BOOL fAlphaDest, RGBQUAD *prgbDest, SIZE sizeFolderBmp)
  391. {
  392. HRESULT hr = E_OUTOFMEMORY;
  393. HDC hdcFrom = CreateCompatibleDC(hdc);
  394. if (hdcFrom)
  395. {
  396. // Select the bitmap into the source hdc.
  397. HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcFrom, hbmpSub);
  398. if (hbmpOld)
  399. {
  400. // Adjust destination size to preserve aspect ratio
  401. SIZE sizeDestActual;
  402. if ((1000 * sizeDest.cx / sizeSource.cx) < // 1000 -> float simulation
  403. (1000 * sizeDest.cy / sizeSource.cy))
  404. {
  405. // Keep destination width
  406. sizeDestActual.cy = sizeSource.cy * sizeDest.cx / sizeSource.cx;
  407. sizeDestActual.cx = sizeDest.cx;
  408. ptMargin.y += (sizeDest.cy - sizeDestActual.cy) / 2; // Center
  409. }
  410. else
  411. {
  412. // Keep destination height
  413. sizeDestActual.cx = sizeSource.cx * sizeDest.cy / sizeSource.cy;
  414. sizeDestActual.cy = sizeDest.cy;
  415. ptMargin.x += (sizeDest.cx - sizeDestActual.cx) / 2; // Center
  416. }
  417. // Now blt the image onto our folder background.
  418. // Three alpha possibilities:
  419. // Dest: no alpha, Src: no alpha -> the normal case
  420. // Dest: no alpha, Src: alpha -> one of the minipreviews is a logo-ized folder.
  421. // Dest: alpha, Src: no alpha -> we're a logoized folder being rendered as a minipreview in
  422. // the parent folder's thumbnail.
  423. // If we got back an alpha image, we need to alphablend it.
  424. if (fAlphaSource)
  425. {
  426. // We shouldn't have gotten back an alpha image, if we're alpha'd too. That would imply we're
  427. // doing a minipreview of a minipreview (1/16 scale).
  428. //ASSERT(!fAlphaDest);
  429. BLENDFUNCTION bf;
  430. bf.BlendOp = AC_SRC_OVER;
  431. bf.SourceConstantAlpha = 255;
  432. bf.AlphaFormat = AC_SRC_ALPHA;
  433. bf.BlendFlags = 0;
  434. if (AlphaBlend(hdc, ptMargin.x, ptMargin.y, sizeDestActual.cx, sizeDestActual.cy, hdcFrom, 0 ,0, sizeSource.cx, sizeSource.cy, bf))
  435. hr = S_OK;
  436. }
  437. else
  438. {
  439. // Otherwise, just blt it.
  440. int iModeSave = SetStretchBltMode(hdc, HALFTONE);
  441. if (StretchBlt(hdc, ptMargin.x, ptMargin.y, sizeDestActual.cx, sizeDestActual.cy, hdcFrom, 0 ,0, sizeSource.cx, sizeSource.cy, SRCCOPY))
  442. hr = S_OK;
  443. SetStretchBltMode(hdc, iModeSave);
  444. // Are we alpha'd? We didn't have an alpha source, so where we blt'd it, we've
  445. // lost the alpha channel. Restore it.
  446. if (fAlphaDest)
  447. {
  448. // Set the alpha channel over where we just blt'd.
  449. RECT rc = {ptMargin.x, ptMargin.y, ptMargin.x + sizeDestActual.cx, ptMargin.y + sizeDestActual.cy};
  450. _SetAlpha(rc, sizeFolderBmp, prgbDest);
  451. }
  452. }
  453. SelectObject(hdcFrom, hbmpOld);
  454. }
  455. DeleteDC(hdcFrom);
  456. }
  457. return hr;
  458. }
  459. class CFolderExtractImage : public IExtractImage2,
  460. public IPersistPropertyBag,
  461. public IAlphaThumbnailExtractor,
  462. public IRunnableTask
  463. {
  464. public:
  465. CFolderExtractImage();
  466. STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
  467. STDMETHOD_(ULONG, AddRef) ();
  468. STDMETHOD_(ULONG, Release) ();
  469. // IExtractImage/IExtractLogo
  470. STDMETHOD (GetLocation)(LPWSTR pszPath, DWORD cch, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags);
  471. STDMETHOD (Extract)(HBITMAP *phbm);
  472. // IExtractImage2
  473. STDMETHOD (GetDateStamp)(FILETIME *pftDateStamp);
  474. // IPersist
  475. STDMETHOD(GetClassID)(CLSID *pClassID);
  476. // IPersistPropertyBag
  477. STDMETHOD(InitNew)();
  478. STDMETHOD(Load)(IPropertyBag *ppb, IErrorLog *pErr);
  479. STDMETHOD(Save)(IPropertyBag *ppb, BOOL fClearDirty, BOOL fSaveAll)
  480. { return E_NOTIMPL; }
  481. // IRunnableTask
  482. STDMETHOD (Run)(void);
  483. STDMETHOD (Kill)(BOOL fWait);
  484. STDMETHOD (Suspend)(void);
  485. STDMETHOD (Resume)(void);
  486. STDMETHOD_(ULONG, IsRunning)(void);
  487. // IAlphaThumbnailExtractor
  488. STDMETHOD (RequestAlphaThumbnail)(void);
  489. STDMETHOD(Init)(IShellFolder *psf, LPCITEMIDLIST pidl);
  490. private:
  491. ~CFolderExtractImage();
  492. LPCTSTR _GetImagePath(UINT cx);
  493. HRESULT _CreateWithMiniPreviews(IShellFolder *psf, const LPCITEMIDLIST *apidlPreviews, BOOL *abTryCached, UINT cpidlPreviews, MINIPREVIEW_LAYOUT uLayout, IShellImageStore *pImageStore, HBITMAP *phBmpThumbnail);
  494. HRESULT _FindMiniPreviews(LPITEMIDLIST apidlPreviews[], BOOL abTryCached[], UINT *cpidlPreviews);
  495. HRESULT _CreateThumbnailFromIconResource(HBITMAP* phBmpThumbnail, int res);
  496. HRESULT _CheckThumbnailCache(HBITMAP *phbmp);
  497. void _CacheThumbnail(HBITMAP hbmp);
  498. IExtractImage *_pExtract;
  499. IRunnableTask *_pRun;
  500. long _cRef;
  501. TCHAR _szFolder[MAX_PATH];
  502. TCHAR _szLogo[MAX_PATH];
  503. TCHAR _szWideLogo[MAX_PATH];
  504. IShellFolder2 *_psf;
  505. SIZE _size;
  506. LPITEMIDLIST _pidl;
  507. IPropertyBag *_ppb;
  508. LONG _lState;
  509. BOOL _fAlpha;
  510. DWORD _dwPriority;
  511. DWORD _dwRecClrDepth;
  512. DWORD _dwExtractFlags;
  513. };
  514. STDAPI CFolderExtractImage_Create(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  515. {
  516. HRESULT hr = E_OUTOFMEMORY;
  517. CFolderExtractImage *pfei = new CFolderExtractImage;
  518. if (pfei)
  519. {
  520. hr = pfei->Init(psf, pidl);
  521. if (SUCCEEDED(hr))
  522. hr = pfei->QueryInterface(riid, ppv);
  523. pfei->Release();
  524. }
  525. return hr;
  526. }
  527. CFolderExtractImage::CFolderExtractImage() : _cRef(1), _lState(IRTIR_TASK_NOT_RUNNING)
  528. {
  529. }
  530. CFolderExtractImage::~CFolderExtractImage()
  531. {
  532. ATOMICRELEASE(_pExtract);
  533. ATOMICRELEASE(_psf);
  534. ILFree(_pidl);
  535. ATOMICRELEASE(_ppb);
  536. }
  537. STDMETHODIMP CFolderExtractImage::QueryInterface(REFIID riid, void **ppv)
  538. {
  539. static const QITAB qit[] = {
  540. QITABENT (CFolderExtractImage, IExtractImage2),
  541. QITABENTMULTI (CFolderExtractImage, IExtractImage, IExtractImage2),
  542. QITABENTMULTI2(CFolderExtractImage, IID_IExtractLogo, IExtractImage2),
  543. QITABENT (CFolderExtractImage, IPersistPropertyBag),
  544. QITABENT (CFolderExtractImage, IRunnableTask),
  545. QITABENT (CFolderExtractImage, IAlphaThumbnailExtractor),
  546. QITABENTMULTI (CFolderExtractImage, IPersist, IPersistPropertyBag),
  547. { 0 },
  548. };
  549. return QISearch(this, qit, riid, ppv);
  550. }
  551. STDMETHODIMP_(ULONG) CFolderExtractImage::AddRef()
  552. {
  553. return InterlockedIncrement(&_cRef);
  554. }
  555. STDMETHODIMP_(ULONG) CFolderExtractImage::Release()
  556. {
  557. if (InterlockedDecrement(&_cRef))
  558. return _cRef;
  559. delete this;
  560. return 0;
  561. }
  562. STDMETHODIMP CFolderExtractImage::GetDateStamp(FILETIME *pftDateStamp)
  563. {
  564. HANDLE h = CreateFile(_szFolder, GENERIC_READ,
  565. FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
  566. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  567. HRESULT hr = (h != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL;
  568. if (SUCCEEDED(hr))
  569. {
  570. hr = GetFileTime(h, NULL, NULL, pftDateStamp) ? S_OK : E_FAIL;
  571. CloseHandle(h);
  572. }
  573. return hr;
  574. }
  575. HRESULT CFolderExtractImage::InitNew()
  576. {
  577. IPropertyBag *ppb;
  578. // load up the default property bag for peruser perfolder
  579. // may have problems down the line with thumbs.db being alluser.
  580. if (SUCCEEDED(SHGetViewStatePropertyBag(_pidl, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
  581. {
  582. IUnknown_Set((IUnknown**)&_ppb, ppb);
  583. ppb->Release();
  584. }
  585. // return success always -- SHGVSPB can fail if _pidl is on a removable drive,
  586. // but we still want to do our thing.
  587. return S_OK;
  588. }
  589. HRESULT CFolderExtractImage::Load(IPropertyBag *ppb, IErrorLog *pErr)
  590. {
  591. IUnknown_Set((IUnknown**)&_ppb, ppb);
  592. return S_OK;
  593. }
  594. LPCTSTR CFolderExtractImage::_GetImagePath(UINT cx)
  595. {
  596. if (!_szLogo[0])
  597. {
  598. if (_ppb && SUCCEEDED(SHPropertyBag_ReadStr(_ppb, TEXT("Logo"), _szLogo, ARRAYSIZE(_szLogo))) && _szLogo[0])
  599. {
  600. if (SUCCEEDED(SHPropertyBag_ReadStr(_ppb, TEXT("WideLogo"), _szWideLogo, ARRAYSIZE(_szWideLogo))) && _szWideLogo[0])
  601. PathCombine(_szWideLogo, _szFolder, _szWideLogo); // relative path support
  602. PathCombine(_szLogo, _szFolder, _szLogo); // relative path support
  603. }
  604. else
  605. {
  606. TCHAR szFind[MAX_PATH];
  607. for (int i = 0; i < ARRAYSIZE(c_szFolderThumbnailPaths); i++)
  608. {
  609. PathCombine(szFind, _szFolder, c_szFolderThumbnailPaths[i]);
  610. if (PathFileExists(szFind))
  611. {
  612. lstrcpyn(_szLogo, szFind, ARRAYSIZE(_szLogo));
  613. break;
  614. }
  615. }
  616. }
  617. }
  618. LPCTSTR psz = ((cx > 120) && _szWideLogo[0]) ? _szWideLogo : _szLogo;
  619. return *psz ? psz : NULL;
  620. }
  621. STDMETHODIMP CFolderExtractImage::RequestAlphaThumbnail()
  622. {
  623. _fAlpha = TRUE;
  624. return S_OK;
  625. }
  626. STDMETHODIMP CFolderExtractImage::GetLocation(LPWSTR pszPath, DWORD cch,
  627. DWORD *pdwPriority, const SIZE *prgSize,
  628. DWORD dwRecClrDepth, DWORD *pdwFlags)
  629. {
  630. lstrcpyn(pszPath, _szFolder, cch);
  631. HRESULT hr = S_OK;
  632. _size = *prgSize;
  633. _dwRecClrDepth = dwRecClrDepth;
  634. _dwExtractFlags = *pdwFlags;
  635. if (pdwFlags)
  636. {
  637. if (*pdwFlags & IEIFLAG_ASYNC)
  638. hr = E_PENDING;
  639. *pdwFlags &= ~IEIFLAG_CACHE; // We handle the caching of this thumbnail inside the folder
  640. *pdwFlags |= IEIFLAG_REFRESH; // We still want to handle the refresh verb
  641. }
  642. if (pdwPriority)
  643. {
  644. _dwPriority = *pdwPriority;
  645. *pdwPriority = 1; // very low
  646. }
  647. return hr;
  648. }
  649. STDMETHODIMP CFolderExtractImage::Extract(HBITMAP *phbm)
  650. {
  651. // Set it to running (only if we're in the not running state).
  652. LONG lResOld = InterlockedCompareExchange(&_lState, IRTIR_TASK_RUNNING, IRTIR_TASK_NOT_RUNNING);
  653. if (lResOld != IRTIR_TASK_NOT_RUNNING)
  654. {
  655. // If we weren't in the not running state, bail.
  656. return E_FAIL;
  657. }
  658. // If we have an extractor, use that.
  659. HRESULT hr = E_FAIL;
  660. hr = _CheckThumbnailCache(phbm);
  661. if (FAILED(hr))
  662. {
  663. LPITEMIDLIST apidlPreviews[MAX_MINIPREVIEWS_COLLECT];
  664. BOOL abTryCached[MAX_MINIPREVIEWS_COLLECT];
  665. UINT cpidlPreviews = 0;
  666. LPCTSTR pszLogo = _GetImagePath(_size.cx);
  667. if (pszLogo)
  668. {
  669. // Don't do the standard mini-previews - we've got a special thumbnail
  670. ATOMICRELEASE(_pExtract);
  671. LPITEMIDLIST pidl;
  672. hr = SHILCreateFromPath(pszLogo, &pidl, NULL);
  673. if (SUCCEEDED(hr))
  674. {
  675. LPCITEMIDLIST pidlChild;
  676. IShellFolder* psfLogo;
  677. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfLogo), &pidlChild);
  678. if (SUCCEEDED(hr))
  679. {
  680. hr = _CreateWithMiniPreviews(psfLogo, &pidlChild, NULL, 1, MINIPREVIEW_LAYOUT_1, NULL, phbm);
  681. psfLogo->Release();
  682. }
  683. ILFree(pidl);
  684. }
  685. }
  686. else
  687. {
  688. const struct
  689. {
  690. int csidl;
  691. int res;
  692. }
  693. thumblist[] =
  694. {
  695. {CSIDL_PERSONAL, IDI_MYDOCS},
  696. {CSIDL_MYMUSIC, IDI_MYMUSIC},
  697. {CSIDL_MYPICTURES, IDI_MYPICS},
  698. {CSIDL_MYVIDEO, IDI_MYVIDEOS},
  699. {CSIDL_COMMON_DOCUMENTS, IDI_MYDOCS},
  700. {CSIDL_COMMON_MUSIC, IDI_MYMUSIC},
  701. {CSIDL_COMMON_PICTURES, IDI_MYPICS},
  702. {CSIDL_COMMON_VIDEO, IDI_MYVIDEOS}
  703. };
  704. BOOL bFound = FALSE;
  705. for (int i=0; i < ARRAYSIZE(thumblist) && !bFound; i++)
  706. {
  707. TCHAR szPath[MAX_PATH];
  708. SHGetFolderPath(NULL, thumblist[i].csidl, NULL, 0, szPath);
  709. if (!lstrcmp(_szFolder, szPath))
  710. {
  711. // We return failure in this case so that the requestor can do
  712. // the default action.
  713. hr = E_FAIL;
  714. bFound = TRUE;
  715. }
  716. }
  717. if (!bFound)
  718. {
  719. // Mini-previews.
  720. IShellImageStore *pDiskCache = NULL;
  721. // It's ok if this fails.
  722. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  723. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE) &&
  724. !(_dwExtractFlags & IEIFLAG_QUALITY))
  725. {
  726. LoadFromFile(CLSID_ShellThumbnailDiskCache, _szFolder, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  727. }
  728. cpidlPreviews = ARRAYSIZE(apidlPreviews);
  729. hr = _FindMiniPreviews(apidlPreviews, abTryCached, &cpidlPreviews);
  730. if (SUCCEEDED(hr))
  731. {
  732. if (cpidlPreviews)
  733. {
  734. hr = _CreateWithMiniPreviews(_psf, apidlPreviews, abTryCached, cpidlPreviews, _GetMiniPreviewLayout(_size), pDiskCache, phbm);
  735. FreeMiniPreviewPidls(apidlPreviews, cpidlPreviews);
  736. }
  737. else
  738. {
  739. // We return failure in this case so that the requestor can do
  740. // the default action
  741. hr = E_FAIL;
  742. }
  743. }
  744. ATOMICRELEASE(pDiskCache);
  745. }
  746. }
  747. if (SUCCEEDED(hr) && *phbm)
  748. {
  749. _CacheThumbnail(*phbm);
  750. }
  751. }
  752. return hr;
  753. }
  754. STDMETHODIMP CFolderExtractImage::GetClassID(CLSID *pClassID)
  755. {
  756. return E_NOTIMPL;
  757. }
  758. STDMETHODIMP CFolderExtractImage::Init(IShellFolder *psf, LPCITEMIDLIST pidl)
  759. {
  760. HRESULT hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szFolder, ARRAYSIZE(_szFolder));
  761. if (SUCCEEDED(hr))
  762. {
  763. LPITEMIDLIST pidlFolder;
  764. hr = SHGetIDListFromUnk(psf, &pidlFolder);
  765. if (SUCCEEDED(hr))
  766. {
  767. hr = SHILCombine(pidlFolder, pidl, &_pidl);
  768. if (SUCCEEDED(hr))
  769. {
  770. // hold the _psf for this guy so we can enum
  771. hr = psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &_psf));
  772. if (SUCCEEDED(hr))
  773. {
  774. hr = InitNew();
  775. }
  776. }
  777. ILFree(pidlFolder);
  778. }
  779. }
  780. return hr;
  781. }
  782. // Not necessary --- IExtractImage::Extract() starts us up.
  783. STDMETHODIMP CFolderExtractImage::Run(void)
  784. {
  785. return E_NOTIMPL;
  786. }
  787. STDMETHODIMP CFolderExtractImage::Kill(BOOL fWait)
  788. {
  789. // Try to kill the current subextraction task that's running, if any.
  790. if (_pRun != NULL)
  791. {
  792. _pRun->Kill(fWait);
  793. // If it didn't work, no big deal, we'll complete this subextraction task,
  794. // and bail before starting the next one.
  795. }
  796. // If we're running, set to pending.
  797. LONG lResOld = InterlockedCompareExchange(&_lState, IRTIR_TASK_PENDING, IRTIR_TASK_RUNNING);
  798. if (lResOld == IRTIR_TASK_RUNNING)
  799. {
  800. // We've now set it to pending - ready to die.
  801. return S_OK;
  802. }
  803. else if (lResOld == IRTIR_TASK_PENDING || lResOld == IRTIR_TASK_FINISHED)
  804. {
  805. // We've already been killed.
  806. return S_FALSE;
  807. }
  808. return E_FAIL;
  809. }
  810. STDMETHODIMP CFolderExtractImage::Suspend(void)
  811. {
  812. return E_NOTIMPL;
  813. }
  814. STDMETHODIMP CFolderExtractImage::Resume(void)
  815. {
  816. return E_NOTIMPL;
  817. }
  818. STDMETHODIMP_(ULONG) CFolderExtractImage::IsRunning(void)
  819. {
  820. return _lState;
  821. }
  822. HRESULT CFolderExtractImage::_CreateWithMiniPreviews(IShellFolder *psf, const LPCITEMIDLIST *apidlPreviews, BOOL *abTryCached, UINT cpidlPreviews, MINIPREVIEW_LAYOUT uLayout, IShellImageStore *pImageStore, HBITMAP *phBmpThumbnail)
  823. {
  824. *phBmpThumbnail = NULL;
  825. HBITMAP hbmpOld;
  826. HDC hdc;
  827. SIZE sizeOriginal; // Size of the source bitmaps that go into the minipreview.
  828. SIZE sizeFolderBmp; // Size of the folder bmp we use for the background.
  829. SIZE sizeMiniPreview; // The size calculated for the minipreviews
  830. POINT aptOrigins[MAX_MINIPREVIEWS];
  831. RGBQUAD* prgb; // the bits of the destination bitmap.
  832. _GetMiniPreviewLocations(uLayout, _size, &sizeFolderBmp,
  833. aptOrigins, &sizeMiniPreview);
  834. // sizeFolderBmp is the size of the folder background bitmap that we're working with,
  835. // not the size of the final thumbnail.
  836. HRESULT hr = _CreateMainRenderingDC(&hdc, phBmpThumbnail, &hbmpOld, sizeFolderBmp.cx, sizeFolderBmp.cy, &prgb);
  837. if (SUCCEEDED(hr))
  838. {
  839. BOOL fIsAlphaBackground;
  840. hr = _DrawMiniPreviewBackground(hdc, sizeFolderBmp, _fAlpha, &fIsAlphaBackground, prgb);
  841. if (SUCCEEDED(hr))
  842. {
  843. ULONG uPreviewLocation = 0;
  844. // Extract the images for the minipreviews
  845. for (ULONG i = 0 ; i < cpidlPreviews && uPreviewLocation < ARRAYSIZE(aptOrigins) ; i++)
  846. {
  847. BOOL bFoundAlphaImage = FALSE;
  848. // If we've been killed, stop the processing the minipreviews:
  849. // PENDING?, we're now FINISHED.
  850. InterlockedCompareExchange(&_lState, IRTIR_TASK_FINISHED, IRTIR_TASK_PENDING);
  851. if (_lState == IRTIR_TASK_FINISHED)
  852. {
  853. // Get out.
  854. hr = E_FAIL;
  855. break;
  856. }
  857. HBITMAP hbmpSubs;
  858. BOOL bFoundImage = FALSE;
  859. // Try the image store first
  860. DWORD dwLock;
  861. HRESULT hr2 = (pImageStore && abTryCached[i]) ? pImageStore->Open(STGM_READ, &dwLock) : E_FAIL;
  862. if (SUCCEEDED(hr2))
  863. {
  864. // Get the fullpidl of this guy.
  865. TCHAR szSubPath[MAX_PATH];
  866. if (SUCCEEDED(DisplayNameOf(psf, apidlPreviews[i], SHGDN_INFOLDER | SHGDN_FORPARSING, szSubPath, MAX_PATH)))
  867. {
  868. if (SUCCEEDED(pImageStore->GetEntry(szSubPath, STGM_READ, &hbmpSubs)))
  869. {
  870. bFoundImage = TRUE;
  871. }
  872. }
  873. pImageStore->ReleaseLock(&dwLock);
  874. }
  875. // Resort to calling extractor if the image was not in the cache.
  876. if (!bFoundImage)
  877. {
  878. IExtractImage *peiSub;
  879. hr2 = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&apidlPreviews[i], IID_X_PPV_ARG(IExtractImage, NULL, &peiSub));
  880. if (SUCCEEDED(hr2))
  881. {
  882. // Now extract the image.
  883. DWORD dwPriority = 0;
  884. DWORD dwFlags = IEIFLAG_ORIGSIZE | IEIFLAG_QUALITY;// ORIGSIZE -> preserve aspect ratio
  885. WCHAR szPathBuffer[MAX_PATH];
  886. hr2 = peiSub->GetLocation(szPathBuffer, ARRAYSIZE(szPathBuffer), &dwPriority, &sizeMiniPreview, 24, &dwFlags);
  887. IAlphaThumbnailExtractor *pati;
  888. if (SUCCEEDED(peiSub->QueryInterface(IID_PPV_ARG(IAlphaThumbnailExtractor, &pati))))
  889. {
  890. if (SUCCEEDED(pati->RequestAlphaThumbnail()))
  891. {
  892. bFoundAlphaImage = TRUE;
  893. }
  894. pati->Release();
  895. }
  896. if (SUCCEEDED(hr2))
  897. {
  898. // After we check for IRTIR_TASK_PENDING, but before
  899. // we call peiSub->Extract, it is possible someone calls
  900. // Kill on us.
  901. // Since _pRun will be NULL, we will not kill
  902. // the subtask, but will instead continue and call extract
  903. // on it, and not bail until we try the next subthumbnail.
  904. // Oh well.
  905. // We could add another check here to reduce the window of
  906. // opportunity in which this could happen.
  907. // Try to get an IRunnableTask so that we can stop execution
  908. // of this subtask if necessary.
  909. peiSub->QueryInterface(IID_PPV_ARG(IRunnableTask, &_pRun));
  910. if (SUCCEEDED(peiSub->Extract(&hbmpSubs)))
  911. {
  912. bFoundImage = TRUE;
  913. }
  914. ATOMICRELEASE(_pRun);
  915. }
  916. peiSub->Release();
  917. }
  918. }
  919. // Add the extracted bitmap to the main one...
  920. if (bFoundImage)
  921. {
  922. // The bitmap will of course need to be resized:
  923. BITMAP rgBitmap;
  924. if (::GetObject((HGDIOBJ)hbmpSubs, sizeof(rgBitmap), &rgBitmap))
  925. {
  926. sizeOriginal.cx = rgBitmap.bmWidth;
  927. sizeOriginal.cy = rgBitmap.bmHeight;
  928. // We need to check if this is really an alpha bitmap. It's possible that the
  929. // extractor said it could generate one, but ended up not being able to.
  930. if (bFoundAlphaImage)
  931. {
  932. RECT rc = {0, 0, rgBitmap.bmWidth, rgBitmap.bmHeight};
  933. bFoundAlphaImage = (rgBitmap.bmBitsPixel == 32) &&
  934. _HasAlpha(rc, rgBitmap.bmWidth, (RGBQUAD*)rgBitmap.bmBits);
  935. }
  936. }
  937. else
  938. {
  939. // Couldn't get the info, oh well, no resize.
  940. // alpha may also be screwed up here, but oh well.
  941. sizeOriginal = sizeMiniPreview;
  942. }
  943. if (SUCCEEDED(_AddBitmap(hdc, hbmpSubs, aptOrigins[uPreviewLocation], sizeMiniPreview, sizeOriginal, bFoundAlphaImage, fIsAlphaBackground, prgb, sizeFolderBmp)))
  944. {
  945. uPreviewLocation++;
  946. }
  947. DeleteObject(hbmpSubs);
  948. }
  949. }
  950. if (!uPreviewLocation)
  951. {
  952. // For whatever reason, we have no mini thumbnails to show, so fail this entire extraction.
  953. hr = E_FAIL;
  954. }
  955. }
  956. if (SUCCEEDED(hr))
  957. {
  958. // Is the requested size one of the sizes of the folder background bitmaps?
  959. // Test against smallest requested dimension, because we're square, and we'll fit into that rectangle
  960. int iSmallestDimension = min(_size.cx, _size.cy);
  961. if ((sizeFolderBmp.cx != iSmallestDimension) || (sizeFolderBmp.cy != iSmallestDimension))
  962. {
  963. // Nope - we need to do some scaling.
  964. // Create another dc and bitmap the size of the requested bitmap
  965. HBITMAP hBmpThumbnailFinal = NULL;
  966. HBITMAP hbmpOld2;
  967. HDC hdcFinal;
  968. RGBQUAD *prgbFinal;
  969. hr = _CreateMainRenderingDC(&hdcFinal, &hBmpThumbnailFinal, &hbmpOld2, iSmallestDimension, iSmallestDimension, &prgbFinal);
  970. if (SUCCEEDED(hr))
  971. {
  972. // Now scale it.
  973. if (fIsAlphaBackground)
  974. {
  975. BLENDFUNCTION bf;
  976. bf.BlendOp = AC_SRC_OVER;
  977. bf.SourceConstantAlpha = 255;
  978. bf.AlphaFormat = AC_SRC_ALPHA;
  979. bf.BlendFlags = 0;
  980. if (AlphaBlend(hdcFinal, 0, 0, iSmallestDimension, iSmallestDimension, hdc, 0 ,0, sizeFolderBmp.cx, sizeFolderBmp.cy, bf))
  981. hr = S_OK;
  982. }
  983. else
  984. {
  985. int iModeSave = SetStretchBltMode(hdcFinal, HALFTONE);
  986. if (StretchBlt(hdcFinal, 0, 0, iSmallestDimension, iSmallestDimension, hdc, 0 ,0, sizeFolderBmp.cx, sizeFolderBmp.cy, SRCCOPY))
  987. hr = S_OK;
  988. SetStretchBltMode(hdcFinal, iModeSave);
  989. }
  990. // Destroy the dc.
  991. _DestroyMainRenderingDC(hdcFinal, hbmpOld2);
  992. // Now do a switcheroo
  993. // Don't need to check for success here. Down below, we'll delete *phBmpThumbnail
  994. // if StretchBlt FAILED - and in that case, *pbBmpThumbnail will be hBmpThumbnailFinal.
  995. DeleteObject(*phBmpThumbnail); // delete this, we don't need it.
  996. *phBmpThumbnail = hBmpThumbnailFinal; // This is the one we want.
  997. }
  998. }
  999. }
  1000. _DestroyMainRenderingDC(hdc, hbmpOld);
  1001. }
  1002. if (FAILED(hr) && *phBmpThumbnail) // Something didn't work? Make sure we delete our bmp
  1003. {
  1004. DeleteObject(*phBmpThumbnail);
  1005. }
  1006. return hr;
  1007. }
  1008. /**
  1009. * In/Out: cpidlPreviews - the number of preview items we should look for. Returns the number found.
  1010. * number of pidls returned is cpidlPreviews.
  1011. * Out: apidlPreviews - array of pidls found. The caller must free them.
  1012. */
  1013. HRESULT CFolderExtractImage::_FindMiniPreviews(LPITEMIDLIST apidlPreviews[], BOOL abTryCached[], UINT *pcpidlPreviews)
  1014. {
  1015. UINT cMaxPreviews = *pcpidlPreviews;
  1016. int uNumPreviewsSoFar = 0;
  1017. BOOL bKilled = FALSE;
  1018. // Make sure our aFileTimes array is the right size...
  1019. ASSERT(MAX_MINIPREVIEWS_COLLECT == cMaxPreviews);
  1020. *pcpidlPreviews = 0; // start with none in case of failure
  1021. IEnumIDList *penum;
  1022. if (S_OK == _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum))
  1023. {
  1024. FILETIME aFileTimes[MAX_MINIPREVIEWS_COLLECT] = {0};
  1025. LPITEMIDLIST pidl;
  1026. BOOL bTryCached;
  1027. while (S_OK == penum->Next(1, &pidl, NULL))
  1028. {
  1029. // _IsMiniPreviewCandidate is a potentially expensive operation, so before
  1030. // doing it, we'll check to see if anyone has killed us.
  1031. // Are we PENDING? Then we're FINISHED.
  1032. InterlockedCompareExchange(&_lState, IRTIR_TASK_FINISHED, IRTIR_TASK_PENDING);
  1033. // Get out?
  1034. bKilled = (_lState == IRTIR_TASK_FINISHED);
  1035. if (!bKilled && _IsMiniPreviewCandidate(_psf, pidl, &bTryCached))
  1036. {
  1037. // Get file time of this guy.
  1038. FILETIME ft;
  1039. if (SUCCEEDED(GetDateProperty(_psf, pidl, &SCID_WRITETIME, &ft)))
  1040. {
  1041. for (int i = 0; i < uNumPreviewsSoFar; i++)
  1042. {
  1043. if (CompareFileTime(&aFileTimes[i], &ft) < 0)
  1044. {
  1045. int j;
  1046. // Put it in this slot. First, move guys down by one.
  1047. // No need to copy last guy:
  1048. if (uNumPreviewsSoFar == (int)cMaxPreviews)
  1049. {
  1050. j = (cMaxPreviews - 2);
  1051. // And we must free the pidl we're nuking.
  1052. ILFree(apidlPreviews[cMaxPreviews - 1]);
  1053. apidlPreviews[cMaxPreviews - 1] = NULL;
  1054. }
  1055. else
  1056. {
  1057. j = uNumPreviewsSoFar - 1;
  1058. uNumPreviewsSoFar++;
  1059. }
  1060. for (; j >= i; j--)
  1061. {
  1062. apidlPreviews[j+1] = apidlPreviews[j];
  1063. abTryCached[j+1] = abTryCached[j];
  1064. aFileTimes[j+1] = aFileTimes[j];
  1065. }
  1066. aFileTimes[i] = ft;
  1067. apidlPreviews[i] = pidl;
  1068. abTryCached[i] = bTryCached;
  1069. pidl = NULL; // don't free
  1070. break; // for loop
  1071. }
  1072. }
  1073. // Did we complete the loop?
  1074. if (i == uNumPreviewsSoFar)
  1075. {
  1076. if (i < (int)cMaxPreviews)
  1077. {
  1078. // We still have room for more previews, so tack this on at the end.
  1079. uNumPreviewsSoFar++;
  1080. aFileTimes[i] = ft;
  1081. apidlPreviews[i] = pidl;
  1082. abTryCached[i] = bTryCached;
  1083. pidl = NULL; // don't free below
  1084. }
  1085. }
  1086. *pcpidlPreviews = uNumPreviewsSoFar;
  1087. }
  1088. }
  1089. ILFree(pidl); // NULL pidl OK
  1090. if (bKilled)
  1091. {
  1092. break;
  1093. }
  1094. }
  1095. penum->Release();
  1096. }
  1097. if (bKilled)
  1098. {
  1099. FreeMiniPreviewPidls(apidlPreviews, *pcpidlPreviews);
  1100. *pcpidlPreviews = 0;
  1101. return E_FAIL;
  1102. }
  1103. else
  1104. {
  1105. return (uNumPreviewsSoFar > 0) ? S_OK : S_FALSE;
  1106. }
  1107. }
  1108. HRESULT CFolderExtractImage::_CreateThumbnailFromIconResource(HBITMAP* phBmpThumbnail, int res)
  1109. {
  1110. *phBmpThumbnail = NULL;
  1111. HBITMAP hbmpOld;
  1112. HDC hdc;
  1113. RGBQUAD* prgb; // the bits of the destination bitmap.
  1114. HRESULT hr = _CreateMainRenderingDC(&hdc, phBmpThumbnail, &hbmpOld, _size.cx, _size.cy, &prgb);
  1115. if (SUCCEEDED(hr))
  1116. {
  1117. HICON hicon = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(res), IMAGE_ICON, _size.cx, _size.cy, 0);
  1118. if (hicon)
  1119. {
  1120. RECT rc = { 0, 0, _size.cx + 1, _size.cy + 1};
  1121. SHFillRectClr(hdc, &rc, GetSysColor(COLOR_WINDOW));
  1122. DrawIconEx(hdc, 0, 0, hicon, _size.cx, _size.cy, 0, NULL, DI_NORMAL);
  1123. DestroyIcon(hicon);
  1124. hr = S_OK;
  1125. }
  1126. _DestroyMainRenderingDC(hdc, hbmpOld);
  1127. }
  1128. if (FAILED(hr) && *phBmpThumbnail)
  1129. {
  1130. DeleteObject(*phBmpThumbnail);
  1131. *phBmpThumbnail = NULL;
  1132. }
  1133. return hr;
  1134. }
  1135. HRESULT CFolderExtractImage::_CheckThumbnailCache(HBITMAP* phbmp)
  1136. {
  1137. HRESULT hr = E_FAIL;
  1138. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  1139. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE) &&
  1140. !(_dwExtractFlags & IEIFLAG_QUALITY))
  1141. {
  1142. IShellImageStore *pDiskCache = NULL;
  1143. hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, _szFolder, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  1144. if (SUCCEEDED(hr))
  1145. {
  1146. DWORD dwLock;
  1147. hr = pDiskCache->Open(STGM_READ, &dwLock);
  1148. if (SUCCEEDED(hr))
  1149. {
  1150. FILETIME ftTimeStamp = {0,0};
  1151. hr = GetDateStamp(&ftTimeStamp);
  1152. if (SUCCEEDED(hr))
  1153. {
  1154. FILETIME ftTimeStampCache = {0,0};
  1155. hr = pDiskCache->IsEntryInStore(FOLDER_GUID, &ftTimeStampCache);
  1156. if (SUCCEEDED(hr))
  1157. {
  1158. if (hr == S_OK && (0 == CompareFileTime(&ftTimeStampCache, &ftTimeStamp)))
  1159. {
  1160. hr = pDiskCache->GetEntry(FOLDER_GUID, STGM_READ, phbmp);
  1161. }
  1162. else
  1163. {
  1164. hr = E_FAIL;
  1165. }
  1166. }
  1167. }
  1168. pDiskCache->ReleaseLock(&dwLock);
  1169. pDiskCache->Close(NULL);
  1170. }
  1171. pDiskCache->Release();
  1172. }
  1173. }
  1174. TraceMsg(TF_DEFVIEW, "CFolderExtractImage::_CheckThumbnailCache (%s, %x)", _szFolder, hr);
  1175. return hr;
  1176. }
  1177. void CFolderExtractImage::_CacheThumbnail(HBITMAP hbmp)
  1178. {
  1179. HRESULT hr = E_UNEXPECTED;
  1180. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  1181. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE))
  1182. {
  1183. SIZE sizeThumbnail;
  1184. SHGetThumbnailSize(&sizeThumbnail); // Don't cache the mini-thumbnail preview
  1185. if (sizeThumbnail.cx == _size.cx && sizeThumbnail.cy == _size.cy)
  1186. {
  1187. IShellImageStore *pDiskCache = NULL;
  1188. hr = LoadFromIDList(CLSID_ShellThumbnailDiskCache, _pidl, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  1189. if (SUCCEEDED(hr))
  1190. {
  1191. DWORD dwLock;
  1192. hr = pDiskCache->Open(STGM_READWRITE, &dwLock);
  1193. if (hr == STG_E_FILENOTFOUND)
  1194. {
  1195. if (!IsCopyEngineRunning())
  1196. {
  1197. hr = pDiskCache->Create(STGM_WRITE, &dwLock);
  1198. }
  1199. }
  1200. if (SUCCEEDED(hr))
  1201. {
  1202. FILETIME ftTimeStamp = {0,0};
  1203. hr = GetDateStamp(&ftTimeStamp);
  1204. if (SUCCEEDED(hr))
  1205. {
  1206. hr = pDiskCache->AddEntry(FOLDER_GUID, &ftTimeStamp, STGM_WRITE, hbmp);
  1207. }
  1208. pDiskCache->ReleaseLock(&dwLock);
  1209. pDiskCache->Close(NULL);
  1210. }
  1211. pDiskCache->Release();
  1212. }
  1213. }
  1214. }
  1215. TraceMsg(TF_DEFVIEW, "CFolderExtractImage::_CacheThumbnail (%s, %x)", _szFolder, hr);
  1216. }