|
|
#include "priv.h"
#include "sccls.h"
#include "runtask.h"
#include "legacy.h"
#include <ntquery.h> // defines some values used for fmtid and pid
#define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
DEFINE_SCID(SCID_WRITETIME, PSGUID_STORAGE, PID_STG_WRITETIME);
class CThumbnail : public IThumbnail2, public CLogoBase { public: CThumbnail(void);
// IUnknown
STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
// IThumbnail
STDMETHODIMP Init(HWND hwnd, UINT uMsg); STDMETHODIMP GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight);
// IThumbnail2
STDMETHODIMP GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight);
private: ~CThumbnail(void);
LONG _cRef; HWND _hwnd; UINT _uMsg; IShellImageStore *_pImageStore;
virtual IShellFolder * GetSF() {ASSERT(0);return NULL;}; virtual HWND GetHWND() {ASSERT(0); return _hwnd;};
HRESULT UpdateLogoCallback(DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache); REFTASKOWNERID GetTOID();
BOOL _InCache(LPCWSTR pszItemPath, LPCWSTR pszGLocation, const FILETIME * pftDateStamp); HRESULT _BitmapFromIDList(LPCITEMIDLIST pidl, LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight); HRESULT _InitTaskCancelItems(); };
static const GUID TOID_Thumbnail = { 0xadec3450, 0xe907, 0x11d0, {0xa5, 0x7b, 0x00, 0xc0, 0x4f, 0xc2, 0xf7, 0x6a} };
HRESULT CDiskCacheTask_Create(CLogoBase * pView, IShellImageStore *pImageStore, LPCWSTR pszItem, LPCWSTR pszGLocation, DWORD dwItem, IRunnableTask ** ppTask); class CDiskCacheTask : public CRunnableTask { public: CDiskCacheTask();
STDMETHODIMP RunInitRT(void);
friend HRESULT CDiskCacheTask_Create(CLogoBase * pView, IShellImageStore *pImageStore, LPCWSTR pszItem, LPCWSTR pszGLocation, DWORD dwItem, const SIZE * prgSize, IRunnableTask ** ppTask);
private: ~CDiskCacheTask(); HRESULT PrepImage(HBITMAP * phBmp); IShellImageStore *_pImageStore; WCHAR _szItem[MAX_PATH]; WCHAR _szGLocation[MAX_PATH]; CLogoBase * _pView; DWORD _dwItem; SIZE m_rgSize; };
// CreateInstance
HRESULT CThumbnail_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { *ppunk = NULL;
CThumbnail *pthumbnail = new CThumbnail(); if (pthumbnail) { *ppunk = SAFECAST(pthumbnail, IThumbnail*); return S_OK; }
return E_OUTOFMEMORY; }
// Constructor / Destructor
CThumbnail::CThumbnail(void) : _cRef(1) { DllAddRef(); }
CThumbnail::~CThumbnail(void) { if (_pTaskScheduler) { _pTaskScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE); _pTaskScheduler->Release(); _pTaskScheduler = NULL; }
DllRelease(); }
HRESULT CThumbnail::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CThumbnail, IThumbnail2), QITABENTMULTI(CThumbnail, IThumbnail, IThumbnail2), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
ULONG CThumbnail::AddRef(void) { return InterlockedIncrement(&_cRef); }
ULONG CThumbnail::Release(void) { if (InterlockedDecrement(&_cRef) > 0) return _cRef;
delete this; return 0; }
// IThumbnail
HRESULT CThumbnail::Init(HWND hwnd, UINT uMsg) { _hwnd = hwnd; _uMsg = uMsg; ASSERT(NULL == _pTaskScheduler);
return S_OK; }
HRESULT CThumbnail::_InitTaskCancelItems() { if (!_pTaskScheduler) { if (SUCCEEDED(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTaskScheduler, &_pTaskScheduler)))) { // make sure RemoveTasks() actually kills old tasks even if they're not done yet
_pTaskScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, ITSS_THREAD_TIMEOUT_NO_CHANGE); } }
if (_pTaskScheduler) { // Kill any old tasks in the scheduler.
_pTaskScheduler->RemoveTasks(TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, FALSE); } return _pTaskScheduler ? S_OK : E_FAIL; }
HRESULT CThumbnail::_BitmapFromIDList(LPCITEMIDLIST pidl, LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight) { LPCITEMIDLIST pidlLast; IShellFolder *psf; HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { IExtractImage *pei; hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractImage, &pei)); if (SUCCEEDED(hr)) { DWORD dwPriority; DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_SCREEN | IEIFLAG_OFFLINE; SIZEL rgSize = {lWidth, lHeight};
WCHAR szBufferW[MAX_PATH]; hr = pei->GetLocation(szBufferW, ARRAYSIZE(szBufferW), &dwPriority, &rgSize, SHGetCurColorRes(), &dwFlags); if (SUCCEEDED(hr)) { if (S_OK == hr) { HBITMAP hBitmap; hr = pei->Extract(&hBitmap); if (SUCCEEDED(hr)) { hr = UpdateLogoCallback(dwItem, 0, hBitmap, NULL, TRUE); } } else hr = E_FAIL; } else if (E_PENDING == hr) { WCHAR szPath[MAX_PATH];
if (NULL == pszFile) { DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath)); pszFile = szPath; }
// now get the date stamp and check the disk cache....
FILETIME ftImageTimeStamp; BOOL fNoDateStamp = TRUE; // try it in the background...
// od they support date stamps....
IExtractImage2 *pei2; if (SUCCEEDED(pei->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2)))) { if (SUCCEEDED(pei2->GetDateStamp(&ftImageTimeStamp))) { fNoDateStamp = FALSE; // we have a date stamp..
} pei2->Release(); } else { IShellFolder2 *psf2; if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { if (SUCCEEDED(GetDateProperty(psf2, pidlLast, &SCID_WRITETIME, &ftImageTimeStamp))) { fNoDateStamp = FALSE; // we have a date stamp..
} psf2->Release(); } }
// if it is in the cache, and it is an uptodate image, then fetch from disk....
// if the timestamps are wrong, then the extract code further down will then try
// we only test the cache on NT5 because the templates are old on other platforms and
// thus the image will be the wrong size...
IRunnableTask *prt; if (IsOS(OS_WIN2000ORGREATER) && _InCache(pszFile, szBufferW, (fNoDateStamp ? NULL : &ftImageTimeStamp))) { hr = CDiskCacheTask_Create(this, _pImageStore, pszFile, szBufferW, dwItem, &rgSize, &prt); if (SUCCEEDED(hr)) { // let go of the image store, so the task has the only ref and the lock..
ATOMICRELEASE(_pImageStore); } } else { // Cannot hold the prt which is returned in a member variable since that
// would be a circular reference
hr = CExtractImageTask_Create(this, pei, L"", dwItem, -1, EITF_SAVEBITMAP | EITF_ALWAYSCALL, &prt); } if (SUCCEEDED(hr)) { // Add the task to the scheduler.
hr = _pTaskScheduler->AddTask(prt, TOID_Thumbnail, ITSAT_DEFAULT_LPARAM, dwPriority); prt->Release(); } } pei->Release(); } psf->Release(); } ATOMICRELEASE(_pImageStore); return hr; }
STDMETHODIMP CThumbnail::GetBitmap(LPCWSTR pszFile, DWORD dwItem, LONG lWidth, LONG lHeight) { HRESULT hr = _InitTaskCancelItems();
if (pszFile) { LPITEMIDLIST pidl = ILCreateFromPathW(pszFile); if (pidl) { hr = _BitmapFromIDList(pidl, pszFile, dwItem, lWidth, lHeight); ILFree(pidl); } else hr = E_FAIL; } return hr; }
// IThumbnail2
STDMETHODIMP CThumbnail::GetBitmapFromIDList(LPCITEMIDLIST pidl, DWORD dwItem, LONG lWidth, LONG lHeight) { HRESULT hr = _InitTaskCancelItems(); if (pidl) { hr = _BitmapFromIDList(pidl, NULL, dwItem, lWidth, lHeight); } return hr; }
// private stuff
HRESULT CThumbnail::UpdateLogoCallback(DWORD dwItem, int iIcon, HBITMAP hImage, LPCWSTR pszCache, BOOL fCache) { if (!PostMessage(_hwnd, _uMsg, dwItem, (LPARAM)hImage)) { DeleteObject(hImage); }
return S_OK; }
REFTASKOWNERID CThumbnail::GetTOID() { return TOID_Thumbnail; }
BOOL CThumbnail::_InCache(LPCWSTR pszItemPath, LPCWSTR pszGLocation, const FILETIME * pftDateStamp) { BOOL fRes = FALSE;
HRESULT hr; if (_pImageStore) hr = S_OK; else { // init the cache only once, assume all items from same folder!
WCHAR szName[MAX_PATH]; StrCpyNW(szName, pszItemPath, ARRAYSIZE(szName)); PathRemoveFileSpecW(szName); hr = LoadFromFile(CLSID_ShellThumbnailDiskCache, szName, IID_PPV_ARG(IShellImageStore, &_pImageStore)); }
if (SUCCEEDED(hr)) { DWORD dwStoreLock; hr = _pImageStore->Open(STGM_READ, &dwStoreLock); if (SUCCEEDED(hr)) { FILETIME ftCacheDateStamp; hr = _pImageStore->IsEntryInStore(pszGLocation, &ftCacheDateStamp); if ((hr == S_OK) && (!pftDateStamp || (pftDateStamp->dwLowDateTime == ftCacheDateStamp.dwLowDateTime && pftDateStamp->dwHighDateTime == ftCacheDateStamp.dwHighDateTime))) { fRes = TRUE; } _pImageStore->Close(&dwStoreLock); } } return fRes; }
HRESULT CDiskCacheTask_Create(CLogoBase * pView, IShellImageStore *pImageStore, LPCWSTR pszItem, LPCWSTR pszGLocation, DWORD dwItem, const SIZE * prgSize, IRunnableTask ** ppTask) { CDiskCacheTask *pTask = new CDiskCacheTask; if (pTask == NULL) { return E_OUTOFMEMORY; }
StrCpyW(pTask->_szItem, pszItem); StrCpyW(pTask->_szGLocation, pszGLocation); pTask->_pView = pView; pTask->_pImageStore = pImageStore; pImageStore->AddRef(); pTask->_dwItem = dwItem;
pTask->m_rgSize = * prgSize; *ppTask = SAFECAST(pTask, IRunnableTask *);
return S_OK; }
STDMETHODIMP CDiskCacheTask::RunInitRT() { // otherwise, run the task ....
HBITMAP hBmp = NULL; DWORD dwLock;
HRESULT hr = _pImageStore->Open(STGM_READ, &dwLock); if (SUCCEEDED(hr)) { // at this point, we assume that it IS in the cache, and we already have a read lock on the cache...
hr = _pImageStore->GetEntry(_szGLocation, STGM_READ, &hBmp); // release the lock, we don't need it...
_pImageStore->Close(&dwLock); } ATOMICRELEASE(_pImageStore);
if (hBmp) { PrepImage(&hBmp); _pView->UpdateLogoCallback(_dwItem, 0, hBmp, _szItem, TRUE); }
// ensure we don't return the "we've suspended" value...
if (hr == E_PENDING) hr = E_FAIL; return hr; }
CDiskCacheTask::CDiskCacheTask() : CRunnableTask(RTF_DEFAULT) { }
CDiskCacheTask::~CDiskCacheTask() { ATOMICRELEASE(_pImageStore); }
HRESULT CDiskCacheTask::PrepImage(HBITMAP * phBmp) { ASSERT(phBmp && *phBmp);
DIBSECTION rgDIB;
if (!GetObject(*phBmp, sizeof(rgDIB), &rgDIB)) { return E_FAIL; }
// the disk cache only supports 32 Bpp DIBS now, so we can ignore the palette issue...
ASSERT(rgDIB.dsBm.bmBitsPixel == 32); HBITMAP hBmpNew = NULL; HPALETTE hPal = NULL; if (SHGetCurColorRes() == 8) { hPal = SHCreateShellPalette(NULL); } IScaleAndSharpenImage2 * pScale; HRESULT hr = CoCreateInstance(CLSID_ThumbnailScaler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IScaleAndSharpenImage2, &pScale)); if (SUCCEEDED(hr)) { hr = pScale->ScaleSharpen2((BITMAPINFO *) &rgDIB.dsBmih, rgDIB.dsBm.bmBits, &hBmpNew, &m_rgSize, SHGetCurColorRes(), hPal, 0, FALSE); pScale->Release(); }
if (hPal) DeletePalette(hPal); if (SUCCEEDED(hr) && hBmpNew) { DeleteObject(*phBmp); *phBmp = hBmpNew; }
return S_OK; }
|