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.

1384 lines
50 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 _CheckThumbnailCache(HBITMAP *phbmp);
  496. void _CacheThumbnail(HBITMAP hbmp);
  497. IExtractImage *_pExtract;
  498. IRunnableTask *_pRun;
  499. long _cRef;
  500. TCHAR _szFolder[MAX_PATH];
  501. TCHAR _szLogo[MAX_PATH];
  502. TCHAR _szWideLogo[MAX_PATH];
  503. IShellFolder2 *_psf;
  504. SIZE _size;
  505. LPITEMIDLIST _pidl;
  506. IPropertyBag *_ppb;
  507. LONG _lState;
  508. BOOL _fAlpha;
  509. DWORD _dwPriority;
  510. DWORD _dwRecClrDepth;
  511. DWORD _dwExtractFlags;
  512. };
  513. STDAPI CFolderExtractImage_Create(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  514. {
  515. HRESULT hr = E_OUTOFMEMORY;
  516. CFolderExtractImage *pfei = new CFolderExtractImage;
  517. if (pfei)
  518. {
  519. hr = pfei->Init(psf, pidl);
  520. if (SUCCEEDED(hr))
  521. hr = pfei->QueryInterface(riid, ppv);
  522. pfei->Release();
  523. }
  524. return hr;
  525. }
  526. CFolderExtractImage::CFolderExtractImage() : _cRef(1), _lState(IRTIR_TASK_NOT_RUNNING)
  527. {
  528. }
  529. CFolderExtractImage::~CFolderExtractImage()
  530. {
  531. ATOMICRELEASE(_pExtract);
  532. ATOMICRELEASE(_psf);
  533. ILFree(_pidl);
  534. ATOMICRELEASE(_ppb);
  535. }
  536. STDMETHODIMP CFolderExtractImage::QueryInterface(REFIID riid, void **ppv)
  537. {
  538. static const QITAB qit[] = {
  539. QITABENT (CFolderExtractImage, IExtractImage2),
  540. QITABENTMULTI (CFolderExtractImage, IExtractImage, IExtractImage2),
  541. QITABENTMULTI2(CFolderExtractImage, IID_IExtractLogo, IExtractImage2),
  542. QITABENT (CFolderExtractImage, IPersistPropertyBag),
  543. QITABENT (CFolderExtractImage, IRunnableTask),
  544. QITABENT (CFolderExtractImage, IAlphaThumbnailExtractor),
  545. QITABENTMULTI (CFolderExtractImage, IPersist, IPersistPropertyBag),
  546. { 0 },
  547. };
  548. return QISearch(this, qit, riid, ppv);
  549. }
  550. STDMETHODIMP_(ULONG) CFolderExtractImage::AddRef()
  551. {
  552. return InterlockedIncrement(&_cRef);
  553. }
  554. STDMETHODIMP_(ULONG) CFolderExtractImage::Release()
  555. {
  556. ASSERT( 0 != _cRef );
  557. ULONG cRef = InterlockedDecrement(&_cRef);
  558. if ( 0 == cRef )
  559. {
  560. delete this;
  561. }
  562. return cRef;
  563. }
  564. STDMETHODIMP CFolderExtractImage::GetDateStamp(FILETIME *pftDateStamp)
  565. {
  566. HANDLE h = CreateFile(_szFolder, GENERIC_READ,
  567. FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
  568. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  569. HRESULT hr = (h != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL;
  570. if (SUCCEEDED(hr))
  571. {
  572. hr = GetFileTime(h, NULL, NULL, pftDateStamp) ? S_OK : E_FAIL;
  573. CloseHandle(h);
  574. }
  575. return hr;
  576. }
  577. HRESULT CFolderExtractImage::InitNew()
  578. {
  579. IPropertyBag *ppb;
  580. // load up the default property bag for peruser perfolder
  581. // may have problems down the line with thumbs.db being alluser.
  582. if (SUCCEEDED(SHGetViewStatePropertyBag(_pidl, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
  583. {
  584. IUnknown_Set((IUnknown**)&_ppb, ppb);
  585. ppb->Release();
  586. }
  587. // return success always -- SHGVSPB can fail if _pidl is on a removable drive,
  588. // but we still want to do our thing.
  589. return S_OK;
  590. }
  591. HRESULT CFolderExtractImage::Load(IPropertyBag *ppb, IErrorLog *pErr)
  592. {
  593. IUnknown_Set((IUnknown**)&_ppb, ppb);
  594. return S_OK;
  595. }
  596. LPCTSTR CFolderExtractImage::_GetImagePath(UINT cx)
  597. {
  598. if (!_szLogo[0])
  599. {
  600. if (_ppb && SUCCEEDED(SHPropertyBag_ReadStr(_ppb, TEXT("Logo"), _szLogo, ARRAYSIZE(_szLogo))) && _szLogo[0])
  601. {
  602. if (SUCCEEDED(SHPropertyBag_ReadStr(_ppb, TEXT("WideLogo"), _szWideLogo, ARRAYSIZE(_szWideLogo))) && _szWideLogo[0])
  603. if (!PathCombine(_szWideLogo, _szFolder, _szWideLogo)) // relative path support
  604. ZeroMemory(_szWideLogo, sizeof(_szWideLogo));
  605. if (!PathCombine(_szLogo, _szFolder, _szLogo)) // relative path support
  606. ZeroMemory(_szLogo, sizeof(_szLogo));
  607. }
  608. else
  609. {
  610. TCHAR szFind[MAX_PATH];
  611. for (int i = 0; i < ARRAYSIZE(c_szFolderThumbnailPaths); i++)
  612. {
  613. if (PathCombine(szFind, _szFolder, c_szFolderThumbnailPaths[i]))
  614. {
  615. if (PathFileExists(szFind))
  616. {
  617. if (SUCCEEDED(StringCchCopy(_szLogo, ARRAYSIZE(_szLogo), szFind)))
  618. {
  619. break;
  620. }
  621. else
  622. {
  623. ZeroMemory(_szLogo,(sizeof(_szLogo)));
  624. }
  625. }
  626. }
  627. }
  628. }
  629. }
  630. LPCTSTR psz = ((cx > 120) && _szWideLogo[0]) ? _szWideLogo : _szLogo;
  631. return *psz ? psz : NULL;
  632. }
  633. STDMETHODIMP CFolderExtractImage::RequestAlphaThumbnail()
  634. {
  635. _fAlpha = TRUE;
  636. return S_OK;
  637. }
  638. STDMETHODIMP CFolderExtractImage::GetLocation(LPWSTR pszPath, DWORD cch,
  639. DWORD *pdwPriority, const SIZE *prgSize,
  640. DWORD dwRecClrDepth, DWORD *pdwFlags)
  641. {
  642. HRESULT hr;
  643. hr = StringCchCopy(pszPath, cch, _szFolder);
  644. if (SUCCEEDED(hr))
  645. {
  646. _size = *prgSize;
  647. _dwRecClrDepth = dwRecClrDepth;
  648. _dwExtractFlags = *pdwFlags;
  649. if (pdwFlags)
  650. {
  651. if (*pdwFlags & IEIFLAG_ASYNC)
  652. hr = E_PENDING;
  653. *pdwFlags &= ~IEIFLAG_CACHE; // We handle the caching of this thumbnail inside the folder
  654. *pdwFlags |= IEIFLAG_REFRESH; // We still want to handle the refresh verb
  655. }
  656. if (pdwPriority)
  657. {
  658. _dwPriority = *pdwPriority;
  659. *pdwPriority = 1; // very low
  660. }
  661. }
  662. return hr;
  663. }
  664. STDMETHODIMP CFolderExtractImage::Extract(HBITMAP *phbm)
  665. {
  666. // Set it to running (only if we're in the not running state).
  667. LONG lResOld = InterlockedCompareExchange(&_lState, IRTIR_TASK_RUNNING, IRTIR_TASK_NOT_RUNNING);
  668. if (lResOld != IRTIR_TASK_NOT_RUNNING)
  669. {
  670. // If we weren't in the not running state, bail.
  671. return E_FAIL;
  672. }
  673. // If we have an extractor, use that.
  674. HRESULT hr = E_FAIL;
  675. hr = _CheckThumbnailCache(phbm);
  676. if (FAILED(hr))
  677. {
  678. LPITEMIDLIST apidlPreviews[MAX_MINIPREVIEWS_COLLECT];
  679. BOOL abTryCached[MAX_MINIPREVIEWS_COLLECT];
  680. UINT cpidlPreviews = 0;
  681. LPCTSTR pszLogo = _GetImagePath(_size.cx);
  682. if (pszLogo)
  683. {
  684. // Don't do the standard mini-previews - we've got a special thumbnail
  685. ATOMICRELEASE(_pExtract);
  686. LPITEMIDLIST pidl;
  687. hr = SHILCreateFromPath(pszLogo, &pidl, NULL);
  688. if (SUCCEEDED(hr))
  689. {
  690. LPCITEMIDLIST pidlChild;
  691. IShellFolder* psfLogo;
  692. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfLogo), &pidlChild);
  693. if (SUCCEEDED(hr))
  694. {
  695. hr = _CreateWithMiniPreviews(psfLogo, &pidlChild, NULL, 1, MINIPREVIEW_LAYOUT_1, NULL, phbm);
  696. psfLogo->Release();
  697. }
  698. ILFree(pidl);
  699. }
  700. }
  701. else
  702. {
  703. const struct
  704. {
  705. int csidl;
  706. int res;
  707. }
  708. thumblist[] =
  709. {
  710. {CSIDL_PERSONAL, IDI_MYDOCS},
  711. {CSIDL_MYMUSIC, IDI_MYMUSIC},
  712. {CSIDL_MYPICTURES, IDI_MYPICS},
  713. {CSIDL_MYVIDEO, IDI_MYVIDEOS},
  714. {CSIDL_COMMON_DOCUMENTS, IDI_MYDOCS},
  715. {CSIDL_COMMON_MUSIC, IDI_MYMUSIC},
  716. {CSIDL_COMMON_PICTURES, IDI_MYPICS},
  717. {CSIDL_COMMON_VIDEO, IDI_MYVIDEOS}
  718. };
  719. BOOL bFound = FALSE;
  720. for (int i=0; i < ARRAYSIZE(thumblist) && !bFound; i++)
  721. {
  722. TCHAR szPath[MAX_PATH];
  723. SHGetFolderPath(NULL, thumblist[i].csidl, NULL, 0, szPath);
  724. if (!lstrcmp(_szFolder, szPath))
  725. {
  726. // We return failure in this case so that the requestor can do
  727. // the default action.
  728. hr = E_FAIL;
  729. bFound = TRUE;
  730. }
  731. }
  732. if (!bFound)
  733. {
  734. // Mini-previews.
  735. IShellImageStore *pDiskCache = NULL;
  736. // It's ok if this fails.
  737. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  738. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE) &&
  739. !(_dwExtractFlags & IEIFLAG_QUALITY))
  740. {
  741. LoadFromFile(CLSID_ShellThumbnailDiskCache, _szFolder, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  742. }
  743. cpidlPreviews = ARRAYSIZE(apidlPreviews);
  744. hr = _FindMiniPreviews(apidlPreviews, abTryCached, &cpidlPreviews);
  745. if (SUCCEEDED(hr))
  746. {
  747. if (cpidlPreviews)
  748. {
  749. hr = _CreateWithMiniPreviews(_psf, apidlPreviews, abTryCached, cpidlPreviews, _GetMiniPreviewLayout(_size), pDiskCache, phbm);
  750. FreeMiniPreviewPidls(apidlPreviews, cpidlPreviews);
  751. }
  752. else
  753. {
  754. // We return failure in this case so that the requestor can do
  755. // the default action
  756. hr = E_FAIL;
  757. }
  758. }
  759. ATOMICRELEASE(pDiskCache);
  760. }
  761. }
  762. if (SUCCEEDED(hr) && *phbm)
  763. {
  764. _CacheThumbnail(*phbm);
  765. }
  766. }
  767. return hr;
  768. }
  769. STDMETHODIMP CFolderExtractImage::GetClassID(CLSID *pClassID)
  770. {
  771. return E_NOTIMPL;
  772. }
  773. STDMETHODIMP CFolderExtractImage::Init(IShellFolder *psf, LPCITEMIDLIST pidl)
  774. {
  775. HRESULT hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szFolder, ARRAYSIZE(_szFolder));
  776. if (SUCCEEDED(hr))
  777. {
  778. LPITEMIDLIST pidlFolder;
  779. hr = SHGetIDListFromUnk(psf, &pidlFolder);
  780. if (SUCCEEDED(hr))
  781. {
  782. hr = SHILCombine(pidlFolder, pidl, &_pidl);
  783. if (SUCCEEDED(hr))
  784. {
  785. // hold the _psf for this guy so we can enum
  786. hr = psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &_psf));
  787. if (SUCCEEDED(hr))
  788. {
  789. hr = InitNew();
  790. }
  791. }
  792. ILFree(pidlFolder);
  793. }
  794. }
  795. return hr;
  796. }
  797. // Not necessary --- IExtractImage::Extract() starts us up.
  798. STDMETHODIMP CFolderExtractImage::Run(void)
  799. {
  800. return E_NOTIMPL;
  801. }
  802. STDMETHODIMP CFolderExtractImage::Kill(BOOL fWait)
  803. {
  804. // Try to kill the current subextraction task that's running, if any.
  805. if (_pRun != NULL)
  806. {
  807. _pRun->Kill(fWait);
  808. // If it didn't work, no big deal, we'll complete this subextraction task,
  809. // and bail before starting the next one.
  810. }
  811. // If we're running, set to pending.
  812. LONG lResOld = InterlockedCompareExchange(&_lState, IRTIR_TASK_PENDING, IRTIR_TASK_RUNNING);
  813. if (lResOld == IRTIR_TASK_RUNNING)
  814. {
  815. // We've now set it to pending - ready to die.
  816. return S_OK;
  817. }
  818. else if (lResOld == IRTIR_TASK_PENDING || lResOld == IRTIR_TASK_FINISHED)
  819. {
  820. // We've already been killed.
  821. return S_FALSE;
  822. }
  823. return E_FAIL;
  824. }
  825. STDMETHODIMP CFolderExtractImage::Suspend(void)
  826. {
  827. return E_NOTIMPL;
  828. }
  829. STDMETHODIMP CFolderExtractImage::Resume(void)
  830. {
  831. return E_NOTIMPL;
  832. }
  833. STDMETHODIMP_(ULONG) CFolderExtractImage::IsRunning(void)
  834. {
  835. return _lState;
  836. }
  837. HRESULT CFolderExtractImage::_CreateWithMiniPreviews(IShellFolder *psf, const LPCITEMIDLIST *apidlPreviews, BOOL *abTryCached, UINT cpidlPreviews, MINIPREVIEW_LAYOUT uLayout, IShellImageStore *pImageStore, HBITMAP *phBmpThumbnail)
  838. {
  839. *phBmpThumbnail = NULL;
  840. HBITMAP hbmpOld;
  841. HDC hdc;
  842. SIZE sizeOriginal; // Size of the source bitmaps that go into the minipreview.
  843. SIZE sizeFolderBmp; // Size of the folder bmp we use for the background.
  844. SIZE sizeMiniPreview; // The size calculated for the minipreviews
  845. POINT aptOrigins[MAX_MINIPREVIEWS];
  846. RGBQUAD* prgb; // the bits of the destination bitmap.
  847. _GetMiniPreviewLocations(uLayout, _size, &sizeFolderBmp,
  848. aptOrigins, &sizeMiniPreview);
  849. // sizeFolderBmp is the size of the folder background bitmap that we're working with,
  850. // not the size of the final thumbnail.
  851. HRESULT hr = _CreateMainRenderingDC(&hdc, phBmpThumbnail, &hbmpOld, sizeFolderBmp.cx, sizeFolderBmp.cy, &prgb);
  852. if (SUCCEEDED(hr))
  853. {
  854. BOOL fIsAlphaBackground;
  855. hr = _DrawMiniPreviewBackground(hdc, sizeFolderBmp, _fAlpha, &fIsAlphaBackground, prgb);
  856. if (SUCCEEDED(hr))
  857. {
  858. ULONG uPreviewLocation = 0;
  859. // Extract the images for the minipreviews
  860. for (ULONG i = 0 ; i < cpidlPreviews && uPreviewLocation < ARRAYSIZE(aptOrigins) ; i++)
  861. {
  862. BOOL bFoundAlphaImage = FALSE;
  863. // If we've been killed, stop the processing the minipreviews:
  864. // PENDING?, we're now FINISHED.
  865. InterlockedCompareExchange(&_lState, IRTIR_TASK_FINISHED, IRTIR_TASK_PENDING);
  866. if (_lState == IRTIR_TASK_FINISHED)
  867. {
  868. // Get out.
  869. hr = E_FAIL;
  870. break;
  871. }
  872. HBITMAP hbmpSubs;
  873. BOOL bFoundImage = FALSE;
  874. // Try the image store first
  875. DWORD dwLock;
  876. HRESULT hr2 = (pImageStore && abTryCached[i]) ? pImageStore->Open(STGM_READ, &dwLock) : E_FAIL;
  877. if (SUCCEEDED(hr2))
  878. {
  879. // Get the fullpidl of this guy.
  880. TCHAR szSubPath[MAX_PATH];
  881. if (SUCCEEDED(DisplayNameOf(psf, apidlPreviews[i], SHGDN_INFOLDER | SHGDN_FORPARSING, szSubPath, MAX_PATH)))
  882. {
  883. if (SUCCEEDED(pImageStore->GetEntry(szSubPath, STGM_READ, &hbmpSubs)))
  884. {
  885. bFoundImage = TRUE;
  886. }
  887. }
  888. pImageStore->ReleaseLock(&dwLock);
  889. }
  890. // Resort to calling extractor if the image was not in the cache.
  891. if (!bFoundImage)
  892. {
  893. IExtractImage *peiSub;
  894. hr2 = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&apidlPreviews[i], IID_X_PPV_ARG(IExtractImage, NULL, &peiSub));
  895. if (SUCCEEDED(hr2))
  896. {
  897. // Now extract the image.
  898. DWORD dwPriority = 0;
  899. DWORD dwFlags = IEIFLAG_ORIGSIZE | IEIFLAG_QUALITY;// ORIGSIZE -> preserve aspect ratio
  900. WCHAR szPathBuffer[MAX_PATH];
  901. hr2 = peiSub->GetLocation(szPathBuffer, ARRAYSIZE(szPathBuffer), &dwPriority, &sizeMiniPreview, 24, &dwFlags);
  902. if (SUCCEEDED(hr2) && StrCmpIW(szPathBuffer, _szFolder)) // Avoid simple recursion
  903. {
  904. IAlphaThumbnailExtractor *pati;
  905. if (SUCCEEDED(peiSub->QueryInterface(IID_PPV_ARG(IAlphaThumbnailExtractor, &pati))))
  906. {
  907. if (SUCCEEDED(pati->RequestAlphaThumbnail()))
  908. {
  909. bFoundAlphaImage = TRUE;
  910. }
  911. pati->Release();
  912. }
  913. // After we check for IRTIR_TASK_PENDING, but before
  914. // we call peiSub->Extract, it is possible someone calls
  915. // Kill on us.
  916. // Since _pRun will be NULL, we will not kill
  917. // the subtask, but will instead continue and call extract
  918. // on it, and not bail until we try the next subthumbnail.
  919. // Oh well.
  920. // We could add another check here to reduce the window of
  921. // opportunity in which this could happen.
  922. // Try to get an IRunnableTask so that we can stop execution
  923. // of this subtask if necessary.
  924. peiSub->QueryInterface(IID_PPV_ARG(IRunnableTask, &_pRun));
  925. if (SUCCEEDED(peiSub->Extract(&hbmpSubs)))
  926. {
  927. bFoundImage = TRUE;
  928. }
  929. ATOMICRELEASE(_pRun);
  930. }
  931. peiSub->Release();
  932. }
  933. }
  934. // Add the extracted bitmap to the main one...
  935. if (bFoundImage)
  936. {
  937. // The bitmap will of course need to be resized:
  938. BITMAP rgBitmap;
  939. if (::GetObject((HGDIOBJ)hbmpSubs, sizeof(rgBitmap), &rgBitmap))
  940. {
  941. sizeOriginal.cx = rgBitmap.bmWidth;
  942. sizeOriginal.cy = rgBitmap.bmHeight;
  943. // We need to check if this is really an alpha bitmap. It's possible that the
  944. // extractor said it could generate one, but ended up not being able to.
  945. if (bFoundAlphaImage)
  946. {
  947. RECT rc = {0, 0, rgBitmap.bmWidth, rgBitmap.bmHeight};
  948. bFoundAlphaImage = (rgBitmap.bmBitsPixel == 32) &&
  949. _HasAlpha(rc, rgBitmap.bmWidth, (RGBQUAD*)rgBitmap.bmBits);
  950. }
  951. }
  952. else
  953. {
  954. // Couldn't get the info, oh well, no resize.
  955. // alpha may also be screwed up here, but oh well.
  956. sizeOriginal = sizeMiniPreview;
  957. }
  958. if (SUCCEEDED(_AddBitmap(hdc, hbmpSubs, aptOrigins[uPreviewLocation], sizeMiniPreview, sizeOriginal, bFoundAlphaImage, fIsAlphaBackground, prgb, sizeFolderBmp)))
  959. {
  960. uPreviewLocation++;
  961. }
  962. DeleteObject(hbmpSubs);
  963. }
  964. }
  965. if (!uPreviewLocation)
  966. {
  967. // For whatever reason, we have no mini thumbnails to show, so fail this entire extraction.
  968. hr = E_FAIL;
  969. }
  970. }
  971. if (SUCCEEDED(hr))
  972. {
  973. // Is the requested size one of the sizes of the folder background bitmaps?
  974. // Test against smallest requested dimension, because we're square, and we'll fit into that rectangle
  975. int iSmallestDimension = min(_size.cx, _size.cy);
  976. if ((sizeFolderBmp.cx != iSmallestDimension) || (sizeFolderBmp.cy != iSmallestDimension))
  977. {
  978. // Nope - we need to do some scaling.
  979. // Create another dc and bitmap the size of the requested bitmap
  980. HBITMAP hBmpThumbnailFinal = NULL;
  981. HBITMAP hbmpOld2;
  982. HDC hdcFinal;
  983. RGBQUAD *prgbFinal;
  984. hr = _CreateMainRenderingDC(&hdcFinal, &hBmpThumbnailFinal, &hbmpOld2, iSmallestDimension, iSmallestDimension, &prgbFinal);
  985. if (SUCCEEDED(hr))
  986. {
  987. // Now scale it.
  988. if (fIsAlphaBackground)
  989. {
  990. BLENDFUNCTION bf;
  991. bf.BlendOp = AC_SRC_OVER;
  992. bf.SourceConstantAlpha = 255;
  993. bf.AlphaFormat = AC_SRC_ALPHA;
  994. bf.BlendFlags = 0;
  995. if (AlphaBlend(hdcFinal, 0, 0, iSmallestDimension, iSmallestDimension, hdc, 0 ,0, sizeFolderBmp.cx, sizeFolderBmp.cy, bf))
  996. hr = S_OK;
  997. }
  998. else
  999. {
  1000. int iModeSave = SetStretchBltMode(hdcFinal, HALFTONE);
  1001. if (StretchBlt(hdcFinal, 0, 0, iSmallestDimension, iSmallestDimension, hdc, 0 ,0, sizeFolderBmp.cx, sizeFolderBmp.cy, SRCCOPY))
  1002. hr = S_OK;
  1003. SetStretchBltMode(hdcFinal, iModeSave);
  1004. }
  1005. // Destroy the dc.
  1006. _DestroyMainRenderingDC(hdcFinal, hbmpOld2);
  1007. // Now do a switcheroo
  1008. // Don't need to check for success here. Down below, we'll delete *phBmpThumbnail
  1009. // if StretchBlt FAILED - and in that case, *pbBmpThumbnail will be hBmpThumbnailFinal.
  1010. DeleteObject(*phBmpThumbnail); // delete this, we don't need it.
  1011. *phBmpThumbnail = hBmpThumbnailFinal; // This is the one we want.
  1012. }
  1013. }
  1014. }
  1015. _DestroyMainRenderingDC(hdc, hbmpOld);
  1016. }
  1017. if (FAILED(hr) && *phBmpThumbnail) // Something didn't work? Make sure we delete our bmp
  1018. {
  1019. DeleteObject(*phBmpThumbnail);
  1020. }
  1021. return hr;
  1022. }
  1023. /**
  1024. * In/Out: cpidlPreviews - the number of preview items we should look for. Returns the number found.
  1025. * number of pidls returned is cpidlPreviews.
  1026. * Out: apidlPreviews - array of pidls found. The caller must free them.
  1027. */
  1028. HRESULT CFolderExtractImage::_FindMiniPreviews(LPITEMIDLIST apidlPreviews[], BOOL abTryCached[], UINT *pcpidlPreviews)
  1029. {
  1030. UINT cMaxPreviews = *pcpidlPreviews;
  1031. int uNumPreviewsSoFar = 0;
  1032. BOOL bKilled = FALSE;
  1033. // Make sure our aFileTimes array is the right size...
  1034. ASSERT(MAX_MINIPREVIEWS_COLLECT == cMaxPreviews);
  1035. *pcpidlPreviews = 0; // start with none in case of failure
  1036. IEnumIDList *penum;
  1037. if (S_OK == _psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum))
  1038. {
  1039. FILETIME aFileTimes[MAX_MINIPREVIEWS_COLLECT] = {0};
  1040. LPITEMIDLIST pidl;
  1041. BOOL bTryCached;
  1042. while (S_OK == penum->Next(1, &pidl, NULL))
  1043. {
  1044. // _IsMiniPreviewCandidate is a potentially expensive operation, so before
  1045. // doing it, we'll check to see if anyone has killed us.
  1046. // Are we PENDING? Then we're FINISHED.
  1047. InterlockedCompareExchange(&_lState, IRTIR_TASK_FINISHED, IRTIR_TASK_PENDING);
  1048. // Get out?
  1049. bKilled = (_lState == IRTIR_TASK_FINISHED);
  1050. if (!bKilled && _IsMiniPreviewCandidate(_psf, pidl, &bTryCached))
  1051. {
  1052. // Get file time of this guy.
  1053. FILETIME ft;
  1054. if (SUCCEEDED(GetDateProperty(_psf, pidl, &SCID_WRITETIME, &ft)))
  1055. {
  1056. for (int i = 0; i < uNumPreviewsSoFar; i++)
  1057. {
  1058. if (CompareFileTime(&aFileTimes[i], &ft) < 0)
  1059. {
  1060. int j;
  1061. // Put it in this slot. First, move guys down by one.
  1062. // No need to copy last guy:
  1063. if (uNumPreviewsSoFar == (int)cMaxPreviews)
  1064. {
  1065. j = (cMaxPreviews - 2);
  1066. // And we must free the pidl we're nuking.
  1067. ILFree(apidlPreviews[cMaxPreviews - 1]);
  1068. apidlPreviews[cMaxPreviews - 1] = NULL;
  1069. }
  1070. else
  1071. {
  1072. j = uNumPreviewsSoFar - 1;
  1073. uNumPreviewsSoFar++;
  1074. }
  1075. for (; j >= i; j--)
  1076. {
  1077. apidlPreviews[j+1] = apidlPreviews[j];
  1078. abTryCached[j+1] = abTryCached[j];
  1079. aFileTimes[j+1] = aFileTimes[j];
  1080. }
  1081. aFileTimes[i] = ft;
  1082. apidlPreviews[i] = pidl;
  1083. abTryCached[i] = bTryCached;
  1084. pidl = NULL; // don't free
  1085. break; // for loop
  1086. }
  1087. }
  1088. // Did we complete the loop?
  1089. if (i == uNumPreviewsSoFar)
  1090. {
  1091. if (i < (int)cMaxPreviews)
  1092. {
  1093. // We still have room for more previews, so tack this on at the end.
  1094. uNumPreviewsSoFar++;
  1095. aFileTimes[i] = ft;
  1096. apidlPreviews[i] = pidl;
  1097. abTryCached[i] = bTryCached;
  1098. pidl = NULL; // don't free below
  1099. }
  1100. }
  1101. *pcpidlPreviews = uNumPreviewsSoFar;
  1102. }
  1103. }
  1104. ILFree(pidl); // NULL pidl OK
  1105. if (bKilled)
  1106. {
  1107. break;
  1108. }
  1109. }
  1110. penum->Release();
  1111. }
  1112. if (bKilled)
  1113. {
  1114. FreeMiniPreviewPidls(apidlPreviews, *pcpidlPreviews);
  1115. *pcpidlPreviews = 0;
  1116. return E_FAIL;
  1117. }
  1118. else
  1119. {
  1120. return (uNumPreviewsSoFar > 0) ? S_OK : S_FALSE;
  1121. }
  1122. }
  1123. HRESULT CFolderExtractImage::_CheckThumbnailCache(HBITMAP* phbmp)
  1124. {
  1125. HRESULT hr = E_FAIL;
  1126. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  1127. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE) &&
  1128. !(_dwExtractFlags & IEIFLAG_QUALITY))
  1129. {
  1130. IShellImageStore *pDiskCache = NULL;
  1131. hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, _szFolder, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  1132. if (SUCCEEDED(hr))
  1133. {
  1134. DWORD dwLock;
  1135. hr = pDiskCache->Open(STGM_READ, &dwLock);
  1136. if (SUCCEEDED(hr))
  1137. {
  1138. FILETIME ftTimeStamp = {0,0};
  1139. hr = GetDateStamp(&ftTimeStamp);
  1140. if (SUCCEEDED(hr))
  1141. {
  1142. FILETIME ftTimeStampCache = {0,0};
  1143. hr = pDiskCache->IsEntryInStore(FOLDER_GUID, &ftTimeStampCache);
  1144. if (SUCCEEDED(hr))
  1145. {
  1146. if (hr == S_OK && (0 == CompareFileTime(&ftTimeStampCache, &ftTimeStamp)))
  1147. {
  1148. hr = pDiskCache->GetEntry(FOLDER_GUID, STGM_READ, phbmp);
  1149. }
  1150. else
  1151. {
  1152. hr = E_FAIL;
  1153. }
  1154. }
  1155. }
  1156. pDiskCache->ReleaseLock(&dwLock);
  1157. pDiskCache->Close(NULL);
  1158. }
  1159. pDiskCache->Release();
  1160. }
  1161. }
  1162. TraceMsg(TF_DEFVIEW, "CFolderExtractImage::_CheckThumbnailCache (%s, %x)", _szFolder, hr);
  1163. return hr;
  1164. }
  1165. void CFolderExtractImage::_CacheThumbnail(HBITMAP hbmp)
  1166. {
  1167. HRESULT hr = E_UNEXPECTED;
  1168. if (!SHRestricted(REST_NOTHUMBNAILCACHE) &&
  1169. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE))
  1170. {
  1171. SIZE sizeThumbnail;
  1172. SHGetThumbnailSize(&sizeThumbnail); // Don't cache the mini-thumbnail preview
  1173. if (sizeThumbnail.cx == _size.cx && sizeThumbnail.cy == _size.cy)
  1174. {
  1175. IShellImageStore *pDiskCache = NULL;
  1176. hr = LoadFromIDList(CLSID_ShellThumbnailDiskCache, _pidl, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  1177. if (SUCCEEDED(hr))
  1178. {
  1179. DWORD dwLock;
  1180. hr = pDiskCache->Open(STGM_READWRITE, &dwLock);
  1181. if (hr == STG_E_FILENOTFOUND)
  1182. {
  1183. if (!IsCopyEngineRunning())
  1184. {
  1185. hr = pDiskCache->Create(STGM_WRITE, &dwLock);
  1186. }
  1187. }
  1188. if (SUCCEEDED(hr))
  1189. {
  1190. FILETIME ftTimeStamp = {0,0};
  1191. hr = GetDateStamp(&ftTimeStamp);
  1192. if (SUCCEEDED(hr))
  1193. {
  1194. hr = pDiskCache->AddEntry(FOLDER_GUID, &ftTimeStamp, STGM_WRITE, hbmp);
  1195. }
  1196. pDiskCache->ReleaseLock(&dwLock);
  1197. pDiskCache->Close(NULL);
  1198. }
  1199. pDiskCache->Release();
  1200. }
  1201. }
  1202. }
  1203. TraceMsg(TF_DEFVIEW, "CFolderExtractImage::_CacheThumbnail (%s, %x)", _szFolder, hr);
  1204. }