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.

1470 lines
37 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "debug.h"
  4. #include "stgutil.h"
  5. #include "ids.h"
  6. #include "tngen\ctngen.h"
  7. #include "tlist.h"
  8. #include "thumbutil.h"
  9. void SHGetThumbnailSizeForThumbsDB(SIZE *psize);
  10. STDAPI CThumbStore_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv);
  11. class CThumbStore : public IShellImageStore,
  12. public IPersistFolder,
  13. public IPersistFile,
  14. public CComObjectRootEx<CComMultiThreadModel>,
  15. public CComCoClass< CThumbStore,&CLSID_ShellThumbnailDiskCache >
  16. {
  17. struct CATALOG_ENTRY
  18. {
  19. DWORD cbSize;
  20. DWORD dwIndex;
  21. FILETIME ftTimeStamp;
  22. WCHAR szName[1];
  23. };
  24. struct CATALOG_HEADER
  25. {
  26. WORD cbSize;
  27. WORD wVersion;
  28. DWORD dwEntryCount;
  29. SIZE szThumbnailExtent;
  30. };
  31. public:
  32. BEGIN_COM_MAP(CThumbStore)
  33. COM_INTERFACE_ENTRY_IID(IID_IShellImageStore,IShellImageStore)
  34. COM_INTERFACE_ENTRY(IPersistFolder)
  35. COM_INTERFACE_ENTRY(IPersistFile)
  36. END_COM_MAP()
  37. DECLARE_NOT_AGGREGATABLE(CThumbStore)
  38. CThumbStore();
  39. ~CThumbStore();
  40. // IPersist
  41. STDMETHOD(GetClassID)(CLSID *pClassID);
  42. // IPersistFolder
  43. STDMETHOD(Initialize)(LPCITEMIDLIST pidl);
  44. // IPersistFile
  45. STDMETHOD (IsDirty)(void);
  46. STDMETHOD (Load)(LPCWSTR pszFileName, DWORD dwMode);
  47. STDMETHOD (Save)(LPCWSTR pszFileName, BOOL fRemember);
  48. STDMETHOD (SaveCompleted)(LPCWSTR pszFileName);
  49. STDMETHOD (GetCurFile)(LPWSTR *ppszFileName);
  50. // IImageCache
  51. STDMETHOD (Open)(DWORD dwMode, DWORD *pdwLock);
  52. STDMETHOD (Create)(DWORD dwMode, DWORD *pdwLock);
  53. STDMETHOD (Close)(DWORD const *pdwLock);
  54. STDMETHOD (Commit)(DWORD const *pdwLock);
  55. STDMETHOD (ReleaseLock)(DWORD const *pdwLock);
  56. STDMETHOD (IsLocked)(THIS);
  57. STDMETHOD (GetMode)(DWORD *pdwMode);
  58. STDMETHOD (GetCapabilities)(DWORD *pdwCapMask);
  59. STDMETHOD (AddEntry)(LPCWSTR pszName, const FILETIME *pftTimeStamp, DWORD dwMode, HBITMAP hImage);
  60. STDMETHOD (GetEntry)(LPCWSTR pszName, DWORD dwMode, HBITMAP *phImage);
  61. STDMETHOD (DeleteEntry)(LPCWSTR pszName);
  62. STDMETHOD (IsEntryInStore)(LPCWSTR pszName, FILETIME *pftTimeStamp);
  63. STDMETHOD (Enum)(IEnumShellImageStore ** ppEnum);
  64. protected:
  65. friend class CEnumThumbStore;
  66. HRESULT LoadCatalog(void);
  67. HRESULT SaveCatalog(void);
  68. HRESULT FindStreamID(LPCWSTR pszName, DWORD *pdwStream, CATALOG_ENTRY **ppEntry);
  69. HRESULT GetEntryStream(DWORD dwStream, DWORD dwMode, IStream **ppStream);
  70. DWORD GetAccessMode(DWORD dwMode, BOOL fStream);
  71. DWORD AcquireLock(void);
  72. void ReleaseLock(DWORD dwLock);
  73. BOOL InitCodec(void);
  74. HRESULT PrepImage(HBITMAP *phBmp, SIZE * prgSize, void ** ppBits);
  75. BOOL DecompressImage(void * pvInBuffer, ULONG ulBufferSize, HBITMAP *phBmp);
  76. BOOL CompressImage(HBITMAP hBmp, void ** ppvOutBuffer, ULONG * plBufSize);
  77. HRESULT WriteImage(IStream *pStream, HBITMAP hBmp);
  78. HRESULT ReadImage(IStream *pStream, HBITMAP *phBmp);
  79. BOOL _MatchNodeName(CATALOG_ENTRY *pNode, LPCWSTR pszName);
  80. HRESULT _InitFromPath(LPCTSTR pszPath, DWORD dwMode);
  81. void _SetAttribs(BOOL bForce);
  82. CATALOG_HEADER m_rgHeader;
  83. CList<CATALOG_ENTRY> m_rgCatalog;
  84. IStorage *_pStorageThumb;
  85. DWORD _dwModeStorage;
  86. DWORD m_dwModeAllow;
  87. WCHAR m_szPath[MAX_PATH];
  88. DWORD m_dwMaxIndex;
  89. DWORD m_dwCatalogChange;
  90. // Crit section used to protect the internals
  91. CRITICAL_SECTION m_csInternals;
  92. // needed for this object to be free-threaded... so that
  93. // we can query the catalog from the main thread whilst icons are
  94. // being read and written from the main thread.
  95. CRITICAL_SECTION m_csLock;
  96. DWORD m_dwLock;
  97. int m_fLocked;
  98. CThumbnailFCNContainer * m_pJPEGCodec;
  99. };
  100. HRESULT CEnumThumbStore_Create(CThumbStore * pThis, IEnumShellImageStore ** ppEnum);
  101. class CEnumThumbStore : public IEnumShellImageStore,
  102. public CComObjectRoot
  103. {
  104. public:
  105. BEGIN_COM_MAP(CEnumThumbStore)
  106. COM_INTERFACE_ENTRY_IID(IID_IEnumShellImageStore,IEnumShellImageStore)
  107. END_COM_MAP()
  108. CEnumThumbStore();
  109. ~CEnumThumbStore();
  110. STDMETHOD (Reset)(void);
  111. STDMETHOD (Next)(ULONG celt, PENUMSHELLIMAGESTOREDATA *prgElt, ULONG *pceltFetched);
  112. STDMETHOD (Skip)(ULONG celt);
  113. STDMETHOD (Clone)(IEnumShellImageStore ** pEnum);
  114. protected:
  115. friend HRESULT CEnumThumbStore_Create(CThumbStore *pThis, IEnumShellImageStore **ppEnum);
  116. CThumbStore * m_pStore;
  117. CLISTPOS m_pPos;
  118. DWORD m_dwCatalogChange;
  119. };
  120. #define THUMB_FILENAME L"Thumbs.db"
  121. #define CATALOG_STREAM L"Catalog"
  122. #define CATALOG_VERSION 0x0006
  123. #define CATALOG_VERSION_XPGOLD 0x0005
  124. #define STREAMFLAGS_JPEG 0x0001
  125. #define STREAMFLAGS_DIB 0x0002
  126. struct STREAM_HEADER
  127. {
  128. DWORD cbSize;
  129. DWORD dwFlags;
  130. ULONG ulSize;
  131. };
  132. void GenerateStreamName(LPWSTR pszBuffer, DWORD cbSize, DWORD dwNumber);
  133. HRESULT ReadImage(IStream *pStream, HBITMAP *phImage);
  134. HRESULT WriteImage(IStream *pStream, HBITMAP hImage);
  135. BITMAPINFO *BitmapToDIB(HBITMAP hBmp);
  136. CThumbStore::CThumbStore()
  137. {
  138. m_szPath[0] = 0;
  139. m_rgHeader.dwEntryCount = 0;
  140. m_rgHeader.wVersion = CATALOG_VERSION;
  141. m_rgHeader.cbSize = sizeof(m_rgHeader);
  142. SHGetThumbnailSizeForThumbsDB(&m_rgHeader.szThumbnailExtent);
  143. m_dwMaxIndex = 0;
  144. m_dwModeAllow = STGM_READWRITE;
  145. // this counter is inc'd everytime the catalog changes so that we know when it
  146. // must be committed and so enumerators can detect the list has changed...
  147. m_dwCatalogChange = 0;
  148. m_fLocked = 0;
  149. InitializeCriticalSection(&m_csLock);
  150. InitializeCriticalSection(&m_csInternals);
  151. }
  152. CThumbStore::~CThumbStore()
  153. {
  154. CLISTPOS pCur = m_rgCatalog.GetHeadPosition();
  155. while (pCur != NULL)
  156. {
  157. CATALOG_ENTRY *pNode = m_rgCatalog.GetNext(pCur);
  158. ASSERT(pNode != NULL);
  159. LocalFree((void *) pNode);
  160. }
  161. m_rgCatalog.RemoveAll();
  162. if (_pStorageThumb)
  163. {
  164. _pStorageThumb->Release();
  165. }
  166. if (m_pJPEGCodec)
  167. {
  168. delete m_pJPEGCodec;
  169. }
  170. // assume these are free, we are at ref count zero, no one should still be calling us...
  171. DeleteCriticalSection(&m_csLock);
  172. DeleteCriticalSection(&m_csInternals);
  173. }
  174. STDAPI CThumbStore_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  175. {
  176. return CComCreator< CComObject< CThumbStore > >::CreateInstance((void *)punkOuter, riid, (void **)ppv);
  177. }
  178. DWORD CThumbStore::AcquireLock(void)
  179. {
  180. EnterCriticalSection(&m_csLock);
  181. // inc the lock (we use a counter because we may reenter this on the same thread)
  182. m_fLocked++;
  183. // Never return a lock signature of zero, because that means "not locked".
  184. if (++m_dwLock == 0)
  185. ++m_dwLock;
  186. return m_dwLock;
  187. }
  188. void CThumbStore::ReleaseLock(DWORD dwLock)
  189. {
  190. if (dwLock)
  191. {
  192. ASSERT(m_fLocked);
  193. m_fLocked--;
  194. LeaveCriticalSection(&m_csLock);
  195. }
  196. }
  197. // the structure of the catalog is simple, it is a just a header stream
  198. HRESULT CThumbStore::LoadCatalog()
  199. {
  200. HRESULT hr;
  201. if (_pStorageThumb == NULL)
  202. {
  203. hr = E_UNEXPECTED;
  204. }
  205. else if (m_rgHeader.dwEntryCount != 0)
  206. {
  207. // it is already loaded....
  208. hr = S_OK;
  209. }
  210. else
  211. {
  212. // open the catalog stream...
  213. IStream *pCatalog;
  214. hr = _pStorageThumb->OpenStream(CATALOG_STREAM, NULL, GetAccessMode(STGM_READ, TRUE), NULL, &pCatalog);
  215. if (SUCCEEDED(hr))
  216. {
  217. EnterCriticalSection(&m_csInternals);
  218. // now read in the catalog from the stream ...
  219. hr = IStream_Read(pCatalog, &m_rgHeader, sizeof(m_rgHeader));
  220. if (SUCCEEDED(hr))
  221. {
  222. SIZE szCurrentSize;
  223. SHGetThumbnailSizeForThumbsDB(&szCurrentSize);
  224. if ((m_rgHeader.cbSize != sizeof(m_rgHeader)) || (m_rgHeader.wVersion != CATALOG_VERSION) ||
  225. (m_rgHeader.szThumbnailExtent.cx != szCurrentSize.cx) || (m_rgHeader.szThumbnailExtent.cy != szCurrentSize.cy))
  226. {
  227. if (m_rgHeader.wVersion == CATALOG_VERSION_XPGOLD)
  228. {
  229. hr = STG_E_DOCFILECORRUPT; // SECURITY: Many issues encrypting XPGOLD thumbnail databases, just delete it
  230. _pStorageThumb->Release();
  231. _pStorageThumb = NULL;
  232. }
  233. else
  234. {
  235. _SetAttribs(TRUE); // SECURITY: Old formats can't be encrypted
  236. hr = STG_E_OLDFORMAT;
  237. }
  238. }
  239. else
  240. {
  241. for (UINT iEntry = 0; (iEntry < m_rgHeader.dwEntryCount) && SUCCEEDED(hr); iEntry++)
  242. {
  243. DWORD cbSize;
  244. hr = IStream_Read(pCatalog, &cbSize, sizeof(cbSize));
  245. if (SUCCEEDED(hr))
  246. {
  247. ASSERT(cbSize <= sizeof(CATALOG_ENTRY) + sizeof(WCHAR) * MAX_PATH);
  248. CATALOG_ENTRY *pEntry = (CATALOG_ENTRY *)LocalAlloc(LPTR, cbSize);
  249. if (pEntry)
  250. {
  251. pEntry->cbSize = cbSize;
  252. // read the rest with out the size on the front...
  253. hr = IStream_Read(pCatalog, ((BYTE *)pEntry + sizeof(cbSize)), cbSize - sizeof(cbSize));
  254. if (SUCCEEDED(hr))
  255. {
  256. CLISTPOS pCur = m_rgCatalog.AddTail(pEntry);
  257. if (pCur)
  258. {
  259. if (m_dwMaxIndex < pEntry->dwIndex)
  260. {
  261. m_dwMaxIndex = pEntry->dwIndex;
  262. }
  263. }
  264. else
  265. {
  266. hr = E_OUTOFMEMORY;
  267. }
  268. }
  269. }
  270. else
  271. {
  272. hr = E_OUTOFMEMORY;
  273. }
  274. }
  275. }
  276. }
  277. }
  278. if (FAILED(hr))
  279. {
  280. // reset the catalog header...
  281. m_rgHeader.wVersion = CATALOG_VERSION;
  282. m_rgHeader.cbSize = sizeof(m_rgHeader);
  283. SHGetThumbnailSizeForThumbsDB(&m_rgHeader.szThumbnailExtent);
  284. m_rgHeader.dwEntryCount = 0;
  285. }
  286. m_dwCatalogChange = 0;
  287. LeaveCriticalSection(&m_csInternals);
  288. pCatalog->Release();
  289. }
  290. }
  291. return hr;
  292. }
  293. HRESULT CThumbStore::SaveCatalog()
  294. {
  295. _pStorageThumb->DestroyElement(CATALOG_STREAM);
  296. IStream *pCatalog;
  297. HRESULT hr = _pStorageThumb->CreateStream(CATALOG_STREAM, GetAccessMode(STGM_WRITE, TRUE), NULL, NULL, &pCatalog);
  298. if (SUCCEEDED(hr))
  299. {
  300. EnterCriticalSection(&m_csInternals);
  301. // now write the catalog to the stream ...
  302. hr = IStream_Write(pCatalog, &m_rgHeader, sizeof(m_rgHeader));
  303. if (SUCCEEDED(hr))
  304. {
  305. CLISTPOS pCur = m_rgCatalog.GetHeadPosition();
  306. while (pCur && SUCCEEDED(hr))
  307. {
  308. CATALOG_ENTRY *pEntry = m_rgCatalog.GetNext(pCur);
  309. if (pEntry)
  310. {
  311. hr = IStream_Write(pCatalog, pEntry, pEntry->cbSize);
  312. }
  313. }
  314. }
  315. if (SUCCEEDED(hr))
  316. m_dwCatalogChange = 0;
  317. LeaveCriticalSection(&m_csInternals);
  318. pCatalog->Release();
  319. }
  320. return hr;
  321. }
  322. void GenerateStreamName(LPWSTR pszBuffer, DWORD cbSize, DWORD dwNumber)
  323. {
  324. UINT cPos = 0;
  325. while (dwNumber > 0)
  326. {
  327. DWORD dwRem = dwNumber % 10;
  328. // based the fact that UNICODE chars 0-9 are the same as the ANSI chars 0 - 9
  329. pszBuffer[cPos++] = (WCHAR)(dwRem + '0');
  330. dwNumber /= 10;
  331. }
  332. pszBuffer[cPos] = 0;
  333. }
  334. // IPersist methods
  335. STDMETHODIMP CThumbStore::GetClassID(CLSID *pClsid)
  336. {
  337. *pClsid = CLSID_ShellThumbnailDiskCache;
  338. return S_OK;
  339. }
  340. // IPersistFolder
  341. STDMETHODIMP CThumbStore::Initialize(LPCITEMIDLIST pidl)
  342. {
  343. WCHAR szPath[MAX_PATH];
  344. HRESULT hr = SHGetPathFromIDList(pidl, szPath) ? S_OK : E_FAIL;
  345. if (SUCCEEDED(hr))
  346. {
  347. if (PathAppend(szPath, THUMB_FILENAME))
  348. hr = _InitFromPath(szPath, STGM_READWRITE);
  349. else
  350. hr = E_INVALIDARG;
  351. }
  352. return hr;
  353. }
  354. // IPersistFile
  355. STDMETHODIMP CThumbStore::IsDirty(void)
  356. {
  357. return m_dwCatalogChange ? S_OK : S_FALSE;
  358. }
  359. HRESULT CThumbStore::_InitFromPath(LPCTSTR pszPath, DWORD dwMode)
  360. {
  361. if (PathIsRemovable(pszPath))
  362. dwMode = STGM_READ;
  363. m_dwModeAllow = dwMode;
  364. StrCpyN(m_szPath, pszPath, ARRAYSIZE(m_szPath));
  365. return S_OK;
  366. }
  367. STDMETHODIMP CThumbStore::Load(LPCWSTR pszFileName, DWORD dwMode)
  368. {
  369. TCHAR szPath[MAX_PATH];
  370. PathCombine(szPath, pszFileName, THUMB_FILENAME);
  371. return _InitFromPath(szPath, dwMode);
  372. }
  373. STDMETHODIMP CThumbStore::Save(LPCWSTR pszFileName, BOOL fRemember)
  374. {
  375. return E_NOTIMPL;
  376. }
  377. STDMETHODIMP CThumbStore::SaveCompleted(LPCWSTR pszFileName)
  378. {
  379. return E_NOTIMPL;
  380. }
  381. STDMETHODIMP CThumbStore::GetCurFile(LPWSTR *ppszFileName)
  382. {
  383. return SHStrDupW(m_szPath, ppszFileName);
  384. }
  385. // IShellImageStore methods
  386. void CThumbStore::_SetAttribs(BOOL bForce)
  387. {
  388. // reduce spurious changenotifies by checking file attribs first
  389. DWORD dwAttrib = GetFileAttributes(m_szPath);
  390. if (bForce ||
  391. ((dwAttrib != 0xFFFFFFFF) &&
  392. (dwAttrib & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)))
  393. {
  394. SetFileAttributes(m_szPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  395. WCHAR szStream[MAX_PATH + 13];
  396. StrCpyNW(szStream, m_szPath, ARRAYSIZE(szStream));
  397. StrCatW(szStream, TEXT(":encryptable"));
  398. HANDLE hStream = CreateFile(szStream, GENERIC_WRITE, NULL, NULL, CREATE_NEW, NULL, NULL);
  399. if (hStream != INVALID_HANDLE_VALUE)
  400. {
  401. CloseHandle(hStream);
  402. }
  403. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, m_szPath, NULL); // suppress the update dir
  404. }
  405. }
  406. STDMETHODIMP CThumbStore::Open(DWORD dwMode, DWORD *pdwLock)
  407. {
  408. if (m_szPath[0] == 0)
  409. {
  410. return E_UNEXPECTED;
  411. }
  412. if ((m_dwModeAllow == STGM_READ) && (dwMode != STGM_READ))
  413. return STG_E_ACCESSDENIED;
  414. // at this point we have the lock if we need it, so we can close and reopen if we
  415. // don't have it open with the right permissions...
  416. if (_pStorageThumb)
  417. {
  418. if (_dwModeStorage == dwMode)
  419. {
  420. // we already have it open in this mode...
  421. *pdwLock = AcquireLock();
  422. return S_FALSE;
  423. }
  424. else
  425. {
  426. // we are open and the mode is different, so close it. Note, no lock is passed, we already
  427. // have it
  428. HRESULT hr = Close(NULL);
  429. if (FAILED(hr))
  430. {
  431. return hr;
  432. }
  433. }
  434. }
  435. DWORD dwLock = AcquireLock();
  436. DWORD dwFlags = GetAccessMode(dwMode, FALSE);
  437. // now open the DocFile
  438. HRESULT hr = StgOpenStorage(m_szPath, NULL, dwFlags, NULL, NULL, &_pStorageThumb);
  439. if (SUCCEEDED(hr))
  440. {
  441. _dwModeStorage = dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
  442. _SetAttribs(FALSE);
  443. hr = LoadCatalog();
  444. *pdwLock = dwLock;
  445. }
  446. if (STG_E_DOCFILECORRUPT == hr)
  447. {
  448. DeleteFile(m_szPath);
  449. }
  450. if (FAILED(hr))
  451. {
  452. ReleaseLock(dwLock);
  453. }
  454. return hr;
  455. }
  456. STDMETHODIMP CThumbStore::Create(DWORD dwMode, DWORD *pdwLock)
  457. {
  458. if (m_szPath[0] == 0)
  459. {
  460. return E_UNEXPECTED;
  461. }
  462. if (_pStorageThumb)
  463. {
  464. // we already have it open, so we can't create it ...
  465. return STG_E_ACCESSDENIED;
  466. }
  467. if ((m_dwModeAllow == STGM_READ) && (dwMode != STGM_READ))
  468. return STG_E_ACCESSDENIED;
  469. DWORD dwLock = AcquireLock();
  470. DWORD dwFlags = GetAccessMode(dwMode, FALSE);
  471. HRESULT hr = StgCreateDocfile(m_szPath, dwFlags, NULL, &_pStorageThumb);
  472. if (SUCCEEDED(hr))
  473. {
  474. _dwModeStorage = dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
  475. _SetAttribs(FALSE);
  476. *pdwLock = dwLock;
  477. }
  478. if (FAILED(hr))
  479. {
  480. ReleaseLock(dwLock);
  481. }
  482. return hr;
  483. }
  484. STDMETHODIMP CThumbStore::ReleaseLock(DWORD const *pdwLock)
  485. {
  486. ReleaseLock(*pdwLock);
  487. return S_OK;
  488. }
  489. STDMETHODIMP CThumbStore::IsLocked()
  490. {
  491. return (m_fLocked > 0 ? S_OK : S_FALSE);
  492. }
  493. // pdwLock can be NULL indicating close the last opened lock
  494. STDMETHODIMP CThumbStore::Close(DWORD const *pdwLock)
  495. {
  496. DWORD dwLock;
  497. DWORD const *pdwRel = pdwLock;
  498. if (!pdwLock)
  499. {
  500. dwLock = AcquireLock();
  501. pdwRel = &dwLock;
  502. }
  503. HRESULT hr = S_FALSE;
  504. if (_pStorageThumb)
  505. {
  506. if (_dwModeStorage != STGM_READ)
  507. {
  508. // write out the new catalog...
  509. hr = Commit(pdwLock);
  510. _pStorageThumb->Commit(0);
  511. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, m_szPath, NULL); // suppress the update dir
  512. }
  513. _pStorageThumb->Release();
  514. _pStorageThumb = NULL;
  515. }
  516. ReleaseLock(*pdwRel);
  517. return hr;
  518. }
  519. // pdwLock can be NULL meaning use the current lock
  520. STDMETHODIMP CThumbStore::Commit(DWORD const *pdwLock)
  521. {
  522. DWORD dwLock;
  523. if (!pdwLock)
  524. {
  525. dwLock = AcquireLock();
  526. pdwLock = &dwLock;
  527. }
  528. HRESULT hr = S_FALSE;
  529. if (_pStorageThumb && _dwModeStorage != STGM_READ)
  530. {
  531. if (m_dwCatalogChange)
  532. {
  533. SaveCatalog();
  534. }
  535. hr = S_OK;
  536. }
  537. ReleaseLock(*pdwLock);
  538. return hr;
  539. }
  540. STDMETHODIMP CThumbStore::GetMode(DWORD *pdwMode)
  541. {
  542. if (!pdwMode)
  543. {
  544. return E_INVALIDARG;
  545. }
  546. if (_pStorageThumb)
  547. {
  548. *pdwMode = _dwModeStorage;
  549. return S_OK;
  550. }
  551. *pdwMode = 0;
  552. return S_FALSE;
  553. }
  554. STDMETHODIMP CThumbStore::GetCapabilities(DWORD *pdwMode)
  555. {
  556. ASSERT(pdwMode);
  557. // right now, both are needed/supported for thumbs.db
  558. *pdwMode = SHIMSTCAPFLAG_LOCKABLE | SHIMSTCAPFLAG_PURGEABLE;
  559. return S_OK;
  560. }
  561. STDMETHODIMP CThumbStore::AddEntry(LPCWSTR pszName, const FILETIME *pftTimeStamp, DWORD dwMode, HBITMAP hImage)
  562. {
  563. ASSERT(pszName);
  564. if (!_pStorageThumb)
  565. {
  566. return E_UNEXPECTED;
  567. }
  568. if (_dwModeStorage == STGM_READ)
  569. {
  570. // can't modify in this mode...
  571. return E_ACCESSDENIED;
  572. }
  573. // this will block unless we already have the lock on this thread...
  574. DWORD dwLock = AcquireLock();
  575. DWORD dwStream = 0;
  576. CLISTPOS pCur = NULL;
  577. CATALOG_ENTRY *pNode = NULL;
  578. EnterCriticalSection(&m_csInternals);
  579. if (FindStreamID(pszName, &dwStream, &pNode) != S_OK)
  580. {
  581. // needs adding to the catalog...
  582. UINT cbSize = sizeof(*pNode) + lstrlenW(pszName) * sizeof(WCHAR);
  583. pNode = (CATALOG_ENTRY *)LocalAlloc(LPTR, cbSize);
  584. if (pNode == NULL)
  585. {
  586. LeaveCriticalSection(&m_csInternals);
  587. ReleaseLock(dwLock);
  588. return E_OUTOFMEMORY;
  589. }
  590. pNode->cbSize = cbSize;
  591. if (pftTimeStamp)
  592. {
  593. pNode->ftTimeStamp = *pftTimeStamp;
  594. }
  595. dwStream = pNode->dwIndex = ++m_dwMaxIndex;
  596. StrCpyW(pNode->szName, pszName);
  597. pCur = m_rgCatalog.AddTail(pNode);
  598. if (pCur == NULL)
  599. {
  600. LocalFree(pNode);
  601. LeaveCriticalSection(&m_csInternals);
  602. ReleaseLock(dwLock);
  603. return E_OUTOFMEMORY;
  604. }
  605. m_rgHeader.dwEntryCount++;
  606. }
  607. else if (pftTimeStamp)
  608. {
  609. // update the timestamp .....
  610. pNode->ftTimeStamp = *pftTimeStamp;
  611. }
  612. LeaveCriticalSection(&m_csInternals);
  613. IStream *pStream = NULL;
  614. HRESULT hr = THR(GetEntryStream(dwStream, dwMode, &pStream));
  615. if (SUCCEEDED(hr))
  616. {
  617. hr = THR(WriteImage(pStream, hImage));
  618. pStream->Release();
  619. }
  620. if (FAILED(hr) && pCur)
  621. {
  622. // take it back out of the list if we added it...
  623. EnterCriticalSection(&m_csInternals);
  624. m_rgCatalog.RemoveAt(pCur);
  625. m_rgHeader.dwEntryCount--;
  626. LeaveCriticalSection(&m_csInternals);
  627. LocalFree(pNode);
  628. }
  629. if (SUCCEEDED(hr))
  630. {
  631. // catalog change....
  632. m_dwCatalogChange++;
  633. }
  634. ReleaseLock(dwLock);
  635. return hr;
  636. }
  637. STDMETHODIMP CThumbStore::GetEntry(LPCWSTR pszName, DWORD dwMode, HBITMAP *phImage)
  638. {
  639. if (!_pStorageThumb)
  640. {
  641. return E_UNEXPECTED;
  642. }
  643. HRESULT hr;
  644. DWORD dwStream;
  645. if (FindStreamID(pszName, &dwStream, NULL) != S_OK)
  646. {
  647. hr = E_FAIL;
  648. }
  649. else
  650. {
  651. IStream *pStream;
  652. hr = GetEntryStream(dwStream, dwMode, &pStream);
  653. if (SUCCEEDED(hr))
  654. {
  655. hr = ReadImage(pStream, phImage);
  656. pStream->Release();
  657. }
  658. }
  659. return hr;
  660. }
  661. BOOL CThumbStore::_MatchNodeName(CATALOG_ENTRY *pNode, LPCWSTR pszName)
  662. {
  663. return (StrCmpIW(pNode->szName, pszName) == 0) ||
  664. (StrCmpIW(PathFindFileName(pNode->szName), pszName) == 0); // match old thumbs.db files
  665. }
  666. STDMETHODIMP CThumbStore::DeleteEntry(LPCWSTR pszName)
  667. {
  668. if (!_pStorageThumb)
  669. {
  670. return E_UNEXPECTED;
  671. }
  672. if (_dwModeStorage == STGM_READ)
  673. {
  674. // can't modify in this mode...
  675. return E_ACCESSDENIED;
  676. }
  677. DWORD dwLock = AcquireLock();
  678. EnterCriticalSection(&m_csInternals);
  679. // check to see if it already exists.....
  680. CATALOG_ENTRY *pNode = NULL;
  681. CLISTPOS pCur = m_rgCatalog.GetHeadPosition();
  682. while (pCur != NULL)
  683. {
  684. CLISTPOS pDel = pCur;
  685. pNode = m_rgCatalog.GetNext(pCur);
  686. ASSERT(pNode != NULL);
  687. if (_MatchNodeName(pNode, pszName))
  688. {
  689. m_rgCatalog.RemoveAt(pDel);
  690. m_rgHeader.dwEntryCount--;
  691. m_dwCatalogChange++;
  692. if (pNode->dwIndex == m_dwMaxIndex)
  693. {
  694. m_dwMaxIndex--;
  695. }
  696. LeaveCriticalSection(&m_csInternals);
  697. WCHAR szStream[30];
  698. GenerateStreamName(szStream, ARRAYSIZE(szStream), pNode->dwIndex);
  699. _pStorageThumb->DestroyElement(szStream);
  700. LocalFree(pNode);
  701. ReleaseLock(dwLock);
  702. return S_OK;
  703. }
  704. }
  705. LeaveCriticalSection(&m_csInternals);
  706. ReleaseLock(dwLock);
  707. return E_INVALIDARG;
  708. }
  709. STDMETHODIMP CThumbStore::IsEntryInStore(LPCWSTR pszName, FILETIME *pftTimeStamp)
  710. {
  711. if (!_pStorageThumb)
  712. {
  713. return E_UNEXPECTED;
  714. }
  715. DWORD dwStream = 0;
  716. CATALOG_ENTRY *pNode = NULL;
  717. EnterCriticalSection(&m_csInternals);
  718. HRESULT hr = FindStreamID(pszName, &dwStream, &pNode);
  719. if (pftTimeStamp && SUCCEEDED(hr))
  720. {
  721. ASSERT(pNode);
  722. *pftTimeStamp = pNode->ftTimeStamp;
  723. }
  724. LeaveCriticalSection(&m_csInternals);
  725. return (hr == S_OK) ? S_OK : S_FALSE;
  726. }
  727. STDMETHODIMP CThumbStore::Enum(IEnumShellImageStore **ppEnum)
  728. {
  729. return CEnumThumbStore_Create(this, ppEnum);
  730. }
  731. HRESULT CThumbStore::FindStreamID(LPCWSTR pszName, DWORD *pdwStream, CATALOG_ENTRY ** ppNode)
  732. {
  733. // check to see if it already exists in the catalog.....
  734. CATALOG_ENTRY *pNode = NULL;
  735. CLISTPOS pCur = m_rgCatalog.GetHeadPosition();
  736. while (pCur != NULL)
  737. {
  738. pNode = m_rgCatalog.GetNext(pCur);
  739. ASSERT(pNode != NULL);
  740. if (_MatchNodeName(pNode, pszName))
  741. {
  742. *pdwStream = pNode->dwIndex;
  743. if (ppNode != NULL)
  744. {
  745. *ppNode = pNode;
  746. }
  747. return S_OK;
  748. }
  749. }
  750. return E_FAIL;
  751. }
  752. CEnumThumbStore::CEnumThumbStore()
  753. {
  754. m_pStore = NULL;
  755. m_pPos = 0;
  756. m_dwCatalogChange = 0;
  757. }
  758. CEnumThumbStore::~CEnumThumbStore()
  759. {
  760. if (m_pStore)
  761. {
  762. SAFECAST(m_pStore, IPersistFile *)->Release();
  763. }
  764. }
  765. STDMETHODIMP CEnumThumbStore::Reset(void)
  766. {
  767. m_pPos = m_pStore->m_rgCatalog.GetHeadPosition();
  768. m_dwCatalogChange = m_pStore->m_dwCatalogChange;
  769. return S_OK;
  770. }
  771. STDMETHODIMP CEnumThumbStore::Next(ULONG celt, PENUMSHELLIMAGESTOREDATA * prgElt, ULONG * pceltFetched)
  772. {
  773. if ((celt > 1 && !pceltFetched) || !celt)
  774. {
  775. return E_INVALIDARG;
  776. }
  777. if (m_dwCatalogChange != m_pStore->m_dwCatalogChange)
  778. {
  779. return E_UNEXPECTED;
  780. }
  781. ULONG celtFetched = 0;
  782. while (celtFetched < celt &&m_pPos)
  783. {
  784. CThumbStore::CATALOG_ENTRY *pNode = m_pStore->m_rgCatalog.GetNext(m_pPos);
  785. ASSERT(pNode);
  786. PENUMSHELLIMAGESTOREDATA pElt = (PENUMSHELLIMAGESTOREDATA) CoTaskMemAlloc(sizeof(ENUMSHELLIMAGESTOREDATA));
  787. if (!pElt)
  788. {
  789. // cleanup others...
  790. for (ULONG celtCleanup = 0; celtCleanup < celtFetched; celtCleanup++)
  791. {
  792. CoTaskMemFree(prgElt[celtCleanup]);
  793. prgElt[celtCleanup] = NULL;
  794. }
  795. return E_OUTOFMEMORY;
  796. }
  797. StrCpyN(pElt->szPath, pNode->szName, MAX_PATH);
  798. pElt->ftTimeStamp = pNode->ftTimeStamp;
  799. ASSERT(!IsBadWritePtr((void *) (prgElt + celtFetched), sizeof(PENUMSHELLIMAGESTOREDATA)));
  800. prgElt[celtFetched] = pElt;
  801. celtFetched++;
  802. }
  803. if (pceltFetched)
  804. {
  805. *pceltFetched = celtFetched;
  806. }
  807. if (!celtFetched)
  808. {
  809. return E_FAIL;
  810. }
  811. return (celtFetched < celt) ? S_FALSE : S_OK;
  812. }
  813. STDMETHODIMP CEnumThumbStore::Skip(ULONG celt)
  814. {
  815. if (!celt)
  816. {
  817. return E_INVALIDARG;
  818. }
  819. if (m_dwCatalogChange != m_pStore->m_dwCatalogChange)
  820. {
  821. return E_UNEXPECTED;
  822. }
  823. ULONG celtSkipped = 0;
  824. while (celtSkipped < celt &&m_pPos)
  825. {
  826. m_pStore->m_rgCatalog.GetNext(m_pPos);
  827. }
  828. if (!celtSkipped)
  829. {
  830. return E_FAIL;
  831. }
  832. return (celtSkipped < celt) ? S_FALSE : S_OK;
  833. }
  834. STDMETHODIMP CEnumThumbStore::Clone(IEnumShellImageStore ** ppEnum)
  835. {
  836. CEnumThumbStore * pEnum = new CComObject<CEnumThumbStore>;
  837. if (!pEnum)
  838. {
  839. return E_OUTOFMEMORY;
  840. }
  841. ((IPersistFile *)m_pStore)->AddRef();
  842. pEnum->m_pStore = m_pStore;
  843. pEnum->m_dwCatalogChange = m_dwCatalogChange;
  844. // created with zero ref count....
  845. pEnum->AddRef();
  846. *ppEnum = SAFECAST(pEnum, IEnumShellImageStore *);
  847. return S_OK;
  848. }
  849. HRESULT CEnumThumbStore_Create(CThumbStore * pThis, IEnumShellImageStore ** ppEnum)
  850. {
  851. CEnumThumbStore * pEnum = new CComObject<CEnumThumbStore>;
  852. if (!pEnum)
  853. {
  854. return E_OUTOFMEMORY;
  855. }
  856. ((IPersistFile *)pThis)->AddRef();
  857. pEnum->m_pStore = pThis;
  858. // created with zero ref count....
  859. pEnum->AddRef();
  860. *ppEnum = SAFECAST(pEnum, IEnumShellImageStore *);
  861. return S_OK;
  862. }
  863. HRESULT Version1ReadImage(IStream *pStream, DWORD cbSize, HBITMAP *phImage)
  864. {
  865. *phImage = NULL;
  866. HRESULT hr = E_OUTOFMEMORY;
  867. BITMAPINFO *pbi = (BITMAPINFO *) LocalAlloc(LPTR, cbSize);
  868. if (pbi)
  869. {
  870. hr = IStream_Read(pStream, pbi, cbSize);
  871. if (SUCCEEDED(hr))
  872. {
  873. HDC hdc = GetDC(NULL);
  874. if (hdc)
  875. {
  876. *phImage = CreateDIBitmap(hdc, &(pbi->bmiHeader), CBM_INIT, CalcBitsOffsetInDIB(pbi), pbi, DIB_RGB_COLORS);
  877. ReleaseDC(NULL, hdc);
  878. hr = S_OK;
  879. }
  880. }
  881. LocalFree(pbi);
  882. }
  883. return hr;
  884. }
  885. HRESULT CThumbStore::ReadImage(IStream *pStream, HBITMAP *phImage)
  886. {
  887. STREAM_HEADER rgHead;
  888. HRESULT hr = IStream_Read(pStream, &rgHead, sizeof(rgHead));
  889. if (SUCCEEDED(hr))
  890. {
  891. if (rgHead.cbSize == sizeof(rgHead))
  892. {
  893. if (rgHead.dwFlags == STREAMFLAGS_DIB)
  894. {
  895. hr = Version1ReadImage(pStream, rgHead.ulSize, phImage);
  896. }
  897. else if (rgHead.dwFlags == STREAMFLAGS_JPEG)
  898. {
  899. void *pBits = LocalAlloc(LPTR, rgHead.ulSize);
  900. if (pBits)
  901. {
  902. hr = IStream_Read(pStream, pBits, rgHead.ulSize);
  903. if (SUCCEEDED(hr))
  904. {
  905. if (!DecompressImage(pBits, rgHead.ulSize, phImage))
  906. {
  907. hr = E_UNEXPECTED;
  908. }
  909. }
  910. LocalFree(pBits);
  911. }
  912. else
  913. {
  914. hr = E_OUTOFMEMORY;
  915. }
  916. }
  917. else
  918. {
  919. return E_FAIL;
  920. }
  921. }
  922. else
  923. {
  924. hr = E_FAIL;
  925. }
  926. }
  927. return hr;
  928. }
  929. HRESULT Version1WriteImage(IStream *pStream, HBITMAP hImage)
  930. {
  931. HRESULT hr;
  932. BITMAPINFO *pBitmap = BitmapToDIB(hImage);
  933. if (pBitmap)
  934. {
  935. int ncolors = pBitmap->bmiHeader.biClrUsed;
  936. if (ncolors == 0 && pBitmap->bmiHeader.biBitCount <= 8)
  937. ncolors = 1 << pBitmap->bmiHeader.biBitCount;
  938. if (pBitmap->bmiHeader.biBitCount == 16 ||
  939. pBitmap->bmiHeader.biBitCount == 32)
  940. {
  941. if (pBitmap->bmiHeader.biCompression == BI_BITFIELDS)
  942. {
  943. ncolors = 3;
  944. }
  945. }
  946. int iOffset = ncolors * sizeof(RGBQUAD);
  947. STREAM_HEADER rgHead;
  948. rgHead.cbSize = sizeof(rgHead);
  949. rgHead.dwFlags = STREAMFLAGS_DIB;
  950. rgHead.ulSize = pBitmap->bmiHeader.biSize + iOffset + pBitmap->bmiHeader.biSizeImage;
  951. hr = IStream_Write(pStream, &rgHead, sizeof(rgHead));
  952. if (SUCCEEDED(hr))
  953. {
  954. hr = IStream_Write(pStream, pBitmap, rgHead.ulSize);
  955. }
  956. LocalFree(pBitmap);
  957. }
  958. else
  959. {
  960. hr = E_UNEXPECTED;
  961. }
  962. return hr;
  963. }
  964. HRESULT CThumbStore::WriteImage(IStream *pStream, HBITMAP hImage)
  965. {
  966. void *pBits;
  967. ULONG ulBuffer;
  968. HRESULT hr;
  969. if (CompressImage(hImage, &pBits, &ulBuffer))
  970. {
  971. STREAM_HEADER rgHead;
  972. rgHead.cbSize = sizeof(rgHead);
  973. rgHead.dwFlags = STREAMFLAGS_JPEG;
  974. rgHead.ulSize = ulBuffer;
  975. hr = IStream_Write(pStream, &rgHead, sizeof(rgHead));
  976. if (SUCCEEDED(hr))
  977. {
  978. hr = IStream_Write(pStream, pBits, ulBuffer);
  979. }
  980. CoTaskMemFree(pBits);
  981. }
  982. else
  983. hr = E_FAIL;
  984. return hr;
  985. }
  986. HRESULT CThumbStore::GetEntryStream(DWORD dwStream, DWORD dwMode, IStream **ppStream)
  987. {
  988. WCHAR szStream[30];
  989. GenerateStreamName(szStream, ARRAYSIZE(szStream), dwStream);
  990. // leave only the STG_READ | STGM_READWRITE | STGM_WRITE modes
  991. dwMode &= STGM_READ | STGM_WRITE | STGM_READWRITE;
  992. if (!_pStorageThumb)
  993. {
  994. return E_UNEXPECTED;
  995. }
  996. if (_dwModeStorage != STGM_READWRITE && dwMode != _dwModeStorage)
  997. {
  998. return E_ACCESSDENIED;
  999. }
  1000. DWORD dwFlags = GetAccessMode(dwMode, TRUE);
  1001. if (dwFlags & STGM_WRITE)
  1002. {
  1003. _pStorageThumb->DestroyElement(szStream);
  1004. return _pStorageThumb->CreateStream(szStream, dwFlags, NULL, NULL, ppStream);
  1005. }
  1006. else
  1007. {
  1008. return _pStorageThumb->OpenStream(szStream, NULL, dwFlags, NULL, ppStream);
  1009. }
  1010. }
  1011. DWORD CThumbStore::GetAccessMode(DWORD dwMode, BOOL fStream)
  1012. {
  1013. dwMode &= STGM_READ | STGM_WRITE | STGM_READWRITE;
  1014. DWORD dwFlags = dwMode;
  1015. // the root only needs Deny_Write, streams need exclusive....
  1016. if (dwMode == STGM_READ && !fStream)
  1017. {
  1018. dwFlags |= STGM_SHARE_DENY_WRITE;
  1019. }
  1020. else
  1021. {
  1022. dwFlags |= STGM_SHARE_EXCLUSIVE;
  1023. }
  1024. return dwFlags;
  1025. }
  1026. BITMAPINFO *BitmapToDIB(HBITMAP hBmp)
  1027. {
  1028. HDC hdcWnd = GetDC(NULL);
  1029. HDC hMemDC = CreateCompatibleDC(hdcWnd);
  1030. BITMAPINFO bi;
  1031. BITMAP Bitmap;
  1032. GetObject(hBmp, sizeof(Bitmap), (LPSTR)&Bitmap);
  1033. bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  1034. bi.bmiHeader.biBitCount = 0;
  1035. int iVal = GetDIBits(hMemDC, hBmp, 0, Bitmap.bmHeight, NULL, &bi, DIB_RGB_COLORS);
  1036. int ncolors = bi.bmiHeader.biClrUsed;
  1037. if (ncolors == 0 && bi.bmiHeader.biBitCount <= 8)
  1038. ncolors = 1 << bi.bmiHeader.biBitCount;
  1039. if (bi.bmiHeader.biBitCount == 16 ||
  1040. bi.bmiHeader.biBitCount == 32)
  1041. {
  1042. if (bi.bmiHeader.biCompression == BI_BITFIELDS)
  1043. {
  1044. ncolors = 3;
  1045. }
  1046. }
  1047. int iOffset = ncolors * sizeof(RGBQUAD);
  1048. void *pBuffer = LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + iOffset + bi.bmiHeader.biSizeImage);
  1049. if (pBuffer)
  1050. {
  1051. BITMAPINFO * pbi = (BITMAPINFO *)pBuffer;
  1052. void *lpBits = (BYTE *)pBuffer + iOffset + sizeof(BITMAPINFOHEADER);
  1053. // copy members of what was returned in last GetDIBits call.
  1054. CopyMemory(&pbi->bmiHeader, &bi.bmiHeader, sizeof(BITMAPINFOHEADER));
  1055. iVal = GetDIBits(hMemDC, hBmp, 0, Bitmap.bmHeight, lpBits,
  1056. pbi, DIB_RGB_COLORS);
  1057. }
  1058. DeleteDC(hMemDC);
  1059. ReleaseDC(NULL, hdcWnd);
  1060. return (BITMAPINFO *)pBuffer;
  1061. }
  1062. BOOL CThumbStore::InitCodec(void)
  1063. {
  1064. if (NULL == m_pJPEGCodec)
  1065. {
  1066. m_pJPEGCodec = new CThumbnailFCNContainer();
  1067. }
  1068. return (NULL != m_pJPEGCodec);
  1069. }
  1070. BOOL CThumbStore::CompressImage(HBITMAP hBmp, void **ppvOutBuffer, ULONG *plBufSize)
  1071. {
  1072. // given an HBITMAP, get its data....
  1073. HBITMAP hBmpOut = hBmp;
  1074. void *pBits;
  1075. SIZE rgBmpSize;
  1076. *ppvOutBuffer = NULL;
  1077. if (!InitCodec())
  1078. {
  1079. return FALSE;
  1080. }
  1081. HRESULT hr = PrepImage(&hBmpOut, &rgBmpSize, &pBits);
  1082. if (SUCCEEDED(hr))
  1083. {
  1084. ASSERT(pBits);
  1085. hr = E_FAIL;
  1086. if (pBits)
  1087. {
  1088. hr = m_pJPEGCodec->EncodeThumbnail(pBits, rgBmpSize.cx, rgBmpSize.cy,
  1089. ppvOutBuffer, plBufSize);
  1090. }
  1091. if (hBmpOut != hBmp)
  1092. {
  1093. // free the DIBSECTION we were passed back...
  1094. DeleteObject(hBmpOut);
  1095. }
  1096. }
  1097. return SUCCEEDED(hr);
  1098. }
  1099. BOOL CThumbStore::DecompressImage(void *pvInBuffer, ULONG ulBufferSize, HBITMAP *phBmp)
  1100. {
  1101. if (!InitCodec())
  1102. {
  1103. return FALSE;
  1104. }
  1105. ULONG ulWidth;
  1106. ULONG ulHeight;
  1107. HRESULT hr = m_pJPEGCodec->DecodeThumbnail(phBmp, &ulWidth, &ulHeight, pvInBuffer, ulBufferSize);
  1108. return SUCCEEDED(hr);
  1109. }
  1110. HRESULT CThumbStore::PrepImage(HBITMAP *phBmp, SIZE * prgSize, void ** ppBits)
  1111. {
  1112. ASSERT(phBmp && &phBmp);
  1113. ASSERT(prgSize);
  1114. ASSERT(ppBits);
  1115. DIBSECTION rgDIB;
  1116. *ppBits = NULL;
  1117. HRESULT hr = E_FAIL;
  1118. // is the image the wrong colour depth or not a DIBSECTION
  1119. if (!GetObject(*phBmp, sizeof(rgDIB), &rgDIB) || (rgDIB.dsBmih.biBitCount != 32))
  1120. {
  1121. HBITMAP hBmp;
  1122. BITMAP rgBmp;
  1123. GetObject(*phBmp, sizeof(rgBmp), &rgBmp);
  1124. prgSize->cx = rgBmp.bmWidth;
  1125. prgSize->cy = rgBmp.bmHeight;
  1126. // generate a 32 bit DIB of the right size
  1127. if (!CreateSizedDIBSECTION(prgSize, 32, NULL, NULL, &hBmp, NULL, ppBits))
  1128. {
  1129. return E_OUTOFMEMORY;
  1130. }
  1131. HDC hDCMem1 = CreateCompatibleDC(NULL);
  1132. if (hDCMem1)
  1133. {
  1134. HDC hDCMem2 = CreateCompatibleDC(NULL);
  1135. if (hDCMem2)
  1136. {
  1137. HBITMAP hBmpOld1 = (HBITMAP) SelectObject(hDCMem1, *phBmp);
  1138. HBITMAP hBmpOld2 = (HBITMAP) SelectObject(hDCMem2, hBmp);
  1139. // copy the image accross to generate the right sized DIB
  1140. BitBlt(hDCMem2, 0, 0, prgSize->cx, prgSize->cy, hDCMem1, 0, 0, SRCCOPY);
  1141. SelectObject(hDCMem1, hBmpOld1);
  1142. SelectObject(hDCMem2, hBmpOld2);
  1143. DeleteDC(hDCMem2);
  1144. }
  1145. DeleteDC(hDCMem1);
  1146. }
  1147. // pass back the BMP so it can be destroyed later...
  1148. *phBmp = hBmp;
  1149. return S_OK;
  1150. }
  1151. else
  1152. {
  1153. // HOUSTON, we have a DIBSECTION, this is quicker.....
  1154. *ppBits = rgDIB.dsBm.bmBits;
  1155. prgSize->cx = rgDIB.dsBm.bmWidth;
  1156. prgSize->cy = rgDIB.dsBm.bmHeight;
  1157. return S_OK;
  1158. }
  1159. }
  1160. HRESULT DeleteFileThumbnail(LPCWSTR szFilePath)
  1161. {
  1162. WCHAR szFolder[MAX_PATH];
  1163. WCHAR *szFile;
  1164. HRESULT hr = S_OK;
  1165. StrCpyNW(szFolder, szFilePath, ARRAYSIZE(szFolder));
  1166. if (SUCCEEDED(hr))
  1167. {
  1168. szFile = PathFindFileName(szFolder);
  1169. if (szFile != szFolder)
  1170. {
  1171. *(szFile - 1) = 0; // NULL terminates folder
  1172. IShellImageStore *pDiskCache = NULL;
  1173. hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, szFolder, IID_PPV_ARG(IShellImageStore, &pDiskCache));
  1174. if (SUCCEEDED(hr))
  1175. {
  1176. IPersistFile *pPersist = NULL;
  1177. hr = pDiskCache->QueryInterface(IID_PPV_ARG(IPersistFile, &pPersist));
  1178. if (SUCCEEDED(hr))
  1179. {
  1180. hr = pPersist->Load(szFolder, STGM_READWRITE);
  1181. if (SUCCEEDED(hr))
  1182. {
  1183. DWORD dwLock;
  1184. hr = pDiskCache->Open(STGM_READWRITE, &dwLock);
  1185. if (SUCCEEDED(hr))
  1186. {
  1187. hr = pDiskCache->DeleteEntry(szFile);
  1188. pDiskCache->ReleaseLock(&dwLock);
  1189. pDiskCache->Close(NULL);
  1190. }
  1191. }
  1192. pPersist->Release();
  1193. }
  1194. pDiskCache->Release();
  1195. }
  1196. }
  1197. else
  1198. {
  1199. hr = E_FAIL;
  1200. }
  1201. }
  1202. return hr;
  1203. }