windows-nt/Source/XPSP1/NT/shell/browseui/legacy/htmlbm.cpp
2020-09-26 16:20:57 +08:00

474 lines
14 KiB
C++

#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;
}