|
|
#include "shellprv.h"
#include <runtask.h>
#include "defviewp.h"
#include "dvtasks.h"
#include "ids.h"
#include "guids.h"
#include "prop.h" // for SCID_Comment
#include "infotip.h"
// ACL stuff from public/internal/base/inc/seopaque.h
typedef struct _KNOWN_ACE { ACE_HEADER Header; ACCESS_MASK Mask; ULONG SidStart; } KNOWN_ACE, *PKNOWN_ACE;
#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
CDefviewEnumTask::CDefviewEnumTask(CDefView *pdsv) : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv) { }
CDefviewEnumTask::~CDefviewEnumTask() { ATOMICRELEASE(_peunk); DPA_FreeIDArray(_hdpaEnum); // accepts NULL
if (_hdpaPending) DPA_DeleteAllPtrs(_hdpaPending); // the pidl's are owned by defview/listview
}
HRESULT CDefviewEnumTask::FillObjectsToDPA(BOOL fInteractive) { DWORD dwTimeout, dwTime = GetTickCount();
if (_pdsv->_IsDesktop()) dwTimeout = 30000; // 30 seconds
else if (_pdsv->_fs.fFlags & FWF_BESTFITWINDOW) dwTimeout = 3000; // 3 seconds
else dwTimeout = 500; // 1/2 sec
// Make sure _GetEnumFlags calculates the correct bits
_pdsv->_UpdateEnumerationFlags();
HRESULT hr = _pdsv->_pshf->EnumObjects(fInteractive ? _pdsv->_hwndMain : NULL, _pdsv->_GetEnumFlags(), &_peunk); if (S_OK == hr) { IUnknown_SetSite(_peunk, SAFECAST(_pdsv, IOleCommandTarget *)); // give enum a ref to defview
_hdpaEnum = DPA_Create(16); if (_hdpaEnum) { // let callback force background enum
//
// NOTE: If it is desktop, avoid the Background enumeration. Otherwise, it results in
// a lot of flickering when ActiveDesktop is ON. Bug #394940. Fixed by: Sankar.
if ((!_pdsv->_fAllowSearchingWindow && !_pdsv->_IsDesktop()) || S_OK == _pdsv->CallCB(SFVM_BACKGROUNDENUM, 0, 0) || ((GetTickCount() - dwTime) > dwTimeout)) { _fBackground = TRUE; } else { LPITEMIDLIST pidl; ULONG celt; while (S_OK == _peunk->Next(1, &pidl, &celt)) { ASSERT(1==celt); if (DPA_AppendPtr(_hdpaEnum, pidl) == -1) SHFree(pidl);
// Are we taking too long?
if (((GetTickCount() - dwTime) > dwTimeout)) { _fBackground = TRUE; break; } } } } else { hr = E_OUTOFMEMORY; }
IUnknown_SetSite(_peunk, NULL); // Break the site back pointer.
}
_hrRet = hr;
// Let the callback have a chance to "sniff" the items we just enumerated
_pdsv->CallCB(SFVM_ENUMERATEDITEMS, (WPARAM)DPACount(), (LPARAM)DPAArray());
return hr; }
HRESULT CDefviewEnumTask::FillObjectsDPAToDone() { HRESULT hr = S_OK;
if (_fBackground) { ASSERT(S_OK == _hrRet); ASSERT(_peunk);
// let defview do it's background thing
_pdsv->_OnStartBackgroundEnum();
// put ourself on the background scheduler
hr = _pdsv->_AddTask(this, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_BKGRND_FILL, ADDTASK_ATEND); if (FAILED(hr)) { // we can't do background, pretend we're done
hr = _pdsv->_OnStopBackgroundEnum(); } }
if (!_fBackground) { _pdsv->FillDone(); }
return hr; }
HRESULT CDefviewEnumTask::FillObjectsDoneToView() { if (SUCCEEDED(_hrRet)) { HDPA hdpaView = NULL; int cItems = ListView_GetItemCount(_pdsv->_hwndListview); if (cItems) { hdpaView = DPA_Create(16); if (hdpaView) { for (int i = 0; i < cItems; i++) { LPCITEMIDLIST pidl = _pdsv->_GetPIDL(i); ASSERT(IsValidPIDL(pidl)); if (pidl) { DPA_AppendPtr(hdpaView, (void *)pidl); } } } }
// We only need to sort _hdpaView and _hdpaEnum if they both exist
if (hdpaView && _hdpaEnum) { _SortForFilter(hdpaView); if (!_fEnumSorted) _SortForFilter(_hdpaEnum); }
_FilterDPAs(_hdpaEnum, hdpaView);
DPA_Destroy(hdpaView); }
return _hrRet; }
// 99/05/13 vtan: Only use CDefView::_CompareExact if you know that
// IShellFolder2 is implemented. SHCIDS_ALLFIELDS is IShellFolder2
// specific. Use CDefView::_GetCanonicalCompareFunction() to get the function
// to pass to DPA_Sort() if you don't want to make this determination.
// p1 and p2 are pointers to the lv_item's LPARAM, which is currently the pidl
int CALLBACK CDefviewEnumTask::_CompareExactCanonical(void *p1, void *p2, LPARAM lParam) { CDefView *pdv = (CDefView *)lParam; return pdv->_CompareIDsDirection(0 | SHCIDS_ALLFIELDS | SHCIDS_CANONICALONLY, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2); }
PFNDPACOMPARE CDefviewEnumTask::_GetCanonicalCompareFunction(void) { if (_pdsv->_pshf2) return _CompareExactCanonical; else return &(CDefView::_Compare);
}
LPARAM CDefviewEnumTask::_GetCanonicalCompareBits() { if (_pdsv->_pshf2) return 0 | SHCIDS_ALLFIELDS | SHCIDS_CANONICALONLY; else return 0; }
void CDefviewEnumTask::_SortForFilter(HDPA hdpa) { DPA_Sort(hdpa, _GetCanonicalCompareFunction(), (LPARAM)_pdsv); }
// We refreshed the view. Take the old pidls and new pidls and compare
// them, doing a _AddObject for all the new pidls, _RemoveObject
// for the deleted pidls, and _UpdateObject for the inplace modifies.
void CDefviewEnumTask::_FilterDPAs(HDPA hdpaNew, HDPA hdpaOld) { LPARAM lParamSort = _GetCanonicalCompareBits();
for (;;) { LPITEMIDLIST pidlNew, pidlOld;
int iCompare; int cOld = hdpaOld ? DPA_GetPtrCount(hdpaOld) : 0; int cNew = hdpaNew ? DPA_GetPtrCount(hdpaNew) : 0;
if (!cOld && !cNew) break;
if (!cOld) { // only new ones left. Insert all of them.
iCompare = -1; pidlNew = (LPITEMIDLIST)DPA_FastGetPtr(hdpaNew, 0); } else if (!cNew) { // only old ones left. remove them all.
iCompare = 1; pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0); } else { pidlOld = (LPITEMIDLIST)DPA_FastGetPtr(hdpaOld, 0); pidlNew = (LPITEMIDLIST)DPA_FastGetPtr(hdpaNew, 0);
iCompare = _pdsv->_CompareIDsDirection(lParamSort, pidlNew, pidlOld); }
if (iCompare == 0) { // they're the same, remove one of each.
ILFree(pidlNew); DPA_DeletePtr(hdpaNew, 0); DPA_DeletePtr(hdpaOld, 0); } else { // Not identical. See if it's just a modify.
if (cOld && cNew && (lParamSort&SHCIDS_ALLFIELDS)) { iCompare = _pdsv->_CompareIDsDirection((lParamSort&~SHCIDS_ALLFIELDS), pidlNew, pidlOld); } if (iCompare == 0) { _pdsv->_UpdateObject(pidlOld, pidlNew); ILFree(pidlNew); DPA_DeletePtr(hdpaNew, 0); DPA_DeletePtr(hdpaOld, 0); } else if (iCompare < 0) // we have a new item!
{ _pdsv->_AddObject(pidlNew); // takes over pidl ownership.
DPA_DeletePtr(hdpaNew, 0); } else // there's an old item in the view!
{ if (!_DeleteFromPending(pidlOld)) _pdsv->_RemoveObject(pidlOld, TRUE); DPA_DeletePtr(hdpaOld, 0); } } } }
BOOL CDefviewEnumTask::_DeleteFromPending(LPCITEMIDLIST pidl) { if (_hdpaPending) { for (int i = 0; i < DPA_GetPtrCount(_hdpaPending); i++) { LPCITEMIDLIST pidlPending = (LPCITEMIDLIST) DPA_FastGetPtr(_hdpaPending, i);
if (S_OK == _pdsv->_CompareIDsFallback(0, pidl, pidlPending)) { // remove this from the pending list
DPA_DeletePtr(_hdpaPending, i); // the pidl is owned by defview/listview
return TRUE; } } } return FALSE; }
void CDefviewEnumTask::_AddToPending(LPCITEMIDLIST pidl) { if (!_hdpaPending) _hdpaPending = DPA_Create(16);
if (_hdpaPending) DPA_AppendPtr(_hdpaPending, (void *)pidl); }
STDMETHODIMP CDefviewEnumTask::RunInitRT() { return S_OK; }
STDMETHODIMP CDefviewEnumTask::InternalResumeRT() { ULONG celt; LPITEMIDLIST pidl;
IUnknown_SetSite(_peunk, SAFECAST(_pdsv, IOleCommandTarget *)); // give enum a ref to defview
while (S_OK == _peunk->Next(1, &pidl, &celt)) { if (DPA_AppendPtr(_hdpaEnum, pidl) == -1) { SHFree(pidl); }
// we were told to either suspend or quit...
if (WaitForSingleObject(_hDone, 0) == WAIT_OBJECT_0) { return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL; } }
IUnknown_SetSite(_peunk, NULL); // Break the site back pointer.
// Sort on this thread so we do not hang the main thread for as long
DPA_Sort(_hdpaEnum, _GetCanonicalCompareFunction(), (LPARAM)_pdsv); _fEnumSorted = TRUE;
// notify DefView (async) that we're done
PostMessage(_pdsv->_hwndView, WM_DSV_BACKGROUNDENUMDONE, 0, (LPARAM)this); return S_OK; }
class CExtendedColumnTask : public CRunnableTask { public: CExtendedColumnTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn); STDMETHODIMP RunInitRT(void);
private: ~CExtendedColumnTask();
CDefView *_pdsv; LPITEMIDLIST _pidl; const int _fmt; const UINT _uiCol; const UINT _uId; };
CExtendedColumnTask::CExtendedColumnTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn) : CRunnableTask(RTF_DEFAULT), _pdsv(pdsv), _fmt(fmt), _uiCol(uiColumn), _uId(uId) { *phr = SHILClone(pidl, &_pidl); }
CExtendedColumnTask::~CExtendedColumnTask() { ILFree(_pidl); }
STDMETHODIMP CExtendedColumnTask::RunInitRT(void) { DETAILSINFO di;
di.pidl = _pidl; di.fmt = _fmt;
if (SUCCEEDED(_pdsv->_GetDetailsHelper(_uiCol, &di))) { CBackgroundColInfo *pbgci = new CBackgroundColInfo(_pidl, _uId, _uiCol, di.str); if (pbgci) { _pidl = NULL; // give up ownership of this, ILFree checks for null
if (!PostMessage(_pdsv->_hwndView, WM_DSV_UPDATECOLDATA, 0, (LPARAM)pbgci)) delete pbgci; } } return S_OK; }
HRESULT CExtendedColumnTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, UINT uId, int fmt, UINT uiColumn, IRunnableTask **ppTask) { HRESULT hr; CExtendedColumnTask *pECTask = new CExtendedColumnTask(&hr, pdsv, pidl, uId, fmt, uiColumn); if (pECTask) { if (SUCCEEDED(hr)) *ppTask = SAFECAST(pECTask, IRunnableTask*); else pECTask->Release(); } else hr = E_OUTOFMEMORY; return hr; }
class CIconOverlayTask : public CRunnableTask { public: CIconOverlayTask(HRESULT *phr, LPCITEMIDLIST pidl, int iList, CDefView *pdsv);
STDMETHODIMP RunInitRT(void);
private: ~CIconOverlayTask();
CDefView *_pdsv; LPITEMIDLIST _pidl; int _iList; };
CIconOverlayTask::CIconOverlayTask(HRESULT *phr, LPCITEMIDLIST pidl, int iList, CDefView *pdsv) : CRunnableTask(RTF_DEFAULT), _iList(iList), _pdsv(pdsv) { *phr = SHILClone(pidl, &_pidl); }
CIconOverlayTask::~CIconOverlayTask() { ILFree(_pidl); }
STDMETHODIMP CIconOverlayTask::RunInitRT() { int iOverlay = 0;
// get the overlay index for this item.
_pdsv->_psio->GetOverlayIndex(_pidl, &iOverlay);
if (iOverlay > 0) { // now post the result back to the main thread
PostMessage(_pdsv->_hwndView, WM_DSV_UPDATEOVERLAY, (WPARAM)_iList, (LPARAM)iOverlay); }
return S_OK; }
HRESULT CIconOverlayTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, int iList, IRunnableTask **ppTask) { *ppTask = NULL;
HRESULT hr; CIconOverlayTask * pNewTask = new CIconOverlayTask(&hr, pidl, iList, pdsv); if (pNewTask) { if (SUCCEEDED(hr)) *ppTask = SAFECAST(pNewTask, IRunnableTask *); else pNewTask->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
CStatusBarAndInfoTipTask::CStatusBarAndInfoTipTask(HRESULT *phr, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidl, UINT uMsg, int nMsgParam, CBackgroundInfoTip *pbit, HWND hwnd, IShellTaskScheduler2* pScheduler) : CRunnableTask(RTF_DEFAULT), _uMsg(uMsg), _nMsgParam(nMsgParam), _pbit(pbit), _hwnd(hwnd), _pScheduler(pScheduler) { // If we have a pidl, then the number of objects selected must be 1
// This assert applies to the status bar text, but not to the InfoTip text
ASSERT(pbit || !pidl || nMsgParam == 1); *phr = pidl ? SHILClone(pidl, &_pidl) : S_OK;
if (SUCCEEDED(*phr)) { *phr = SHILClone(pidlFolder, &_pidlFolder); if (FAILED(*phr)) { ILFree(_pidl); } }
if (_pbit) _pbit->AddRef(); }
CStatusBarAndInfoTipTask::~CStatusBarAndInfoTipTask() { ILFree(_pidl); ILFree(_pidlFolder); ATOMICRELEASE(_pbit); }
HRESULT CleanTipForSingleLine(LPWSTR pwszTip) { HRESULT hr = E_FAIL; // NULL string, same as failure
if (pwszTip) { // Infotips often contain \t\r\n characters, so
// map control characters to spaces. Also collapse
// consecutive spaces to make us look less badf.
LPWSTR pwszDst, pwszSrc;
// Since we are unicode, we don't have to worry about DBCS.
for (pwszDst = pwszSrc = pwszTip; *pwszSrc; pwszSrc++) { if ((UINT)*pwszSrc <= (UINT)L' ') { if (pwszDst == pwszTip || pwszDst[-1] != L' ') { *pwszDst++ = L' '; } } else { *pwszDst++ = *pwszSrc; } } *pwszDst = 0; // GetInfoTip can return a Null String too.
if (*pwszTip) hr = S_OK; else SHFree(pwszTip); } return hr; }
STDMETHODIMP CStatusBarAndInfoTipTask::RunInitRT() { LPWSTR pwszTip = NULL; HRESULT hr; if (_pidl) { IShellFolder* psf; hr = SHBindToObjectEx(NULL, _pidlFolder, NULL, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { IQueryInfo *pqi; hr = psf->GetUIObjectOf(_hwnd, 1, (LPCITEMIDLIST*)&_pidl, IID_X_PPV_ARG(IQueryInfo, 0, &pqi)); IShellFolder2* psf2; if (FAILED(hr) && SUCCEEDED(hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { hr = CreateInfoTipFromItem(psf2, _pidl, TEXT("prop:Comment"), IID_PPV_ARG(IQueryInfo, &pqi)); psf2->Release(); }
if (SUCCEEDED(hr)) { DWORD dwFlags = _pbit ? QITIPF_USESLOWTIP : 0;
if (_pbit && _pbit->_lvSetInfoTip.pszText[0]) { ICustomizeInfoTip *pcit; if (SUCCEEDED(pqi->QueryInterface(IID_PPV_ARG(ICustomizeInfoTip, &pcit)))) { pcit->SetPrefixText(_pbit->_lvSetInfoTip.pszText); pcit->Release(); } }
hr = pqi->GetInfoTip(dwFlags, &pwszTip);
// Prepare for status bar if we have not requested the InfoTip
if (SUCCEEDED(hr) && !_pbit) hr = CleanTipForSingleLine(pwszTip); pqi->Release(); }
psf->Release(); }
if (FAILED(hr)) { pwszTip = NULL; _uMsg = IDS_FSSTATUSSELECTED; } }
if (_pbit) { // regular info tip case
CoTaskMemFree(_pbit->_lvSetInfoTip.pszText); _pbit->_lvSetInfoTip.pszText = pwszTip;
_pbit->_fReady = TRUE; if (_pScheduler->CountTasks(TOID_DVBackgroundInfoTip) == 1) PostMessage(_hwnd, WM_DSV_DELAYINFOTIP, (WPARAM)_pbit, 0); } else { // status bar case
// Now prepare the text and post it to the view which will set the status bar text
LPWSTR pszStatus = pwszTip; if (pwszTip) { pszStatus = StrDupW(pwszTip); SHFree(pwszTip); } else { WCHAR szTemp[30]; pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(_uMsg), AddCommas(_nMsgParam, szTemp, ARRAYSIZE(szTemp))); }
if (pszStatus && _pScheduler->CountTasks(TOID_DVBackgroundStatusBar) != 1 || !PostMessage(_hwnd, WM_DSV_DELAYSTATUSBARUPDATE, 0, (LPARAM)pszStatus)) { LocalFree((void *)pszStatus); } }
return S_OK; }
HRESULT CStatusBarAndInfoTipTask_CreateInstance(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidl, UINT uMsg, int nMsgParam, CBackgroundInfoTip *pbit, HWND hwnd, IShellTaskScheduler2* pScheduler, CStatusBarAndInfoTipTask **ppTask) { *ppTask = NULL;
HRESULT hr; CStatusBarAndInfoTipTask * pNewTask = new CStatusBarAndInfoTipTask(&hr, pidlFolder, pidl, uMsg, nMsgParam, pbit, hwnd, pScheduler); if (pNewTask) { if (SUCCEEDED(hr)) *ppTask = pNewTask; else pNewTask->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
HRESULT CDUIInfotipTask_CreateInstance(CDefView *pDefView, HWND hwndContaining, UINT uToolID, LPCITEMIDLIST pidl, CDUIInfotipTask **ppTask) { HRESULT hr;
CDUIInfotipTask* pTask = new CDUIInfotipTask(); if (pTask) { hr = pTask->Initialize(pDefView, hwndContaining, uToolID, pidl); if (SUCCEEDED(hr)) *ppTask = pTask; else pTask->Release(); } else hr = E_OUTOFMEMORY;
return hr; }
CDUIInfotipTask::~CDUIInfotipTask() { if (_pDefView) _pDefView->Release();
if (_pidl) ILFree(_pidl); }
HRESULT CDUIInfotipTask::Initialize(CDefView *pDefView, HWND hwndContaining, UINT uToolID, LPCITEMIDLIST pidl) { HRESULT hr;
if (pDefView && hwndContaining && pidl) { ASSERT(!_pDefView && !_hwndContaining && !_uToolID && !_pidl);
_hwndContaining = hwndContaining; // DUI task's containing hwnd
_uToolID = uToolID; // DUI task's identifier
hr = SHILClone(pidl, &_pidl); // DUI task's destination pidl
if (SUCCEEDED(hr)) { _pDefView = pDefView; pDefView->AddRef(); } } else { hr = E_INVALIDARG; }
return hr; }
STDMETHODIMP CDUIInfotipTask::RunInitRT() { HRESULT hr;
ASSERT(_pDefView); ASSERT(_hwndContaining); ASSERT(_pidl);
// Retrieve an IQueryInfo for the _pidl.
IQueryInfo *pqi; hr = SHGetUIObjectFromFullPIDL(_pidl, _hwndContaining, IID_PPV_ARG(IQueryInfo, &pqi)); if (SUCCEEDED(hr)) { // Retrieve infotip text from IQueryInfo.
LPWSTR pwszInfotip; hr = pqi->GetInfoTip(QITIPF_USESLOWTIP, &pwszInfotip); if (SUCCEEDED(hr)) { // Create infotip.
hr = _pDefView->PostCreateInfotip(_hwndContaining, _uToolID, pwszInfotip, 0); CoTaskMemFree(pwszInfotip); }
pqi->Release(); }
return hr; }
STDMETHODIMP CTestCacheTask::RunInitRT() { HRESULT hr = E_FAIL;
if (!_fForce) { // make sure the disk cache is open for reading.
DWORD dwLock = 0; hr = _pView->_pDiskCache ? _pView->_pDiskCache->Open(STGM_READ, &dwLock) : E_FAIL; if (SUCCEEDED(hr)) { // start the timer, once every two seconds....
SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL);
// is it in the cache....
FILETIME ftCacheTimeStamp; hr = _pView->_pDiskCache->IsEntryInStore(_szPath, &ftCacheTimeStamp);
// 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
// and write its image back to the cache to update it anyway.....
if ((hr == S_OK) && ((0 == CompareFileTime(&ftCacheTimeStamp, &_ftDateStamp)) || IsNullTime(&_ftDateStamp))) { DWORD dwPriority = _dwPriority - PRIORITY_DELTA_DISKCACHE;
if ((!_pView->_fDestroying) && (S_OK != _pView->_pScheduler->MoveTask(TOID_DiskCacheTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT))) { // try it in the background...
IRunnableTask *pTask; hr = CDiskCacheTask_Create(_dwTaskID, _pView, dwPriority, _iItem, _pidl, _szPath, _ftDateStamp, _pExtract, _dwFlags, &pTask); if (SUCCEEDED(hr)) { // add the task to the scheduler...
TraceMsg(TF_DEFVIEW, "CTestCacheTask *ADDING* CDiskCacheTask (path=%s, priority=%x)", _szPath, dwPriority); hr = _pView->_pScheduler->AddTask2(pTask, TOID_DiskCacheTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT); if (SUCCEEDED(hr)) hr = S_FALSE; pTask->Release(); } } else { hr = S_FALSE; } } else { TraceMsg(TF_DEFVIEW, "CTestCacheTask *MISS* (hr:%x)", hr); hr = E_FAIL; } _pView->_pDiskCache->ReleaseLock(&dwLock); } else { TraceMsg(TF_DEFVIEW, "CTestCacheTask *WARNING* Could not open thumbnail cache"); } } if (FAILED(hr)) { // Extract It....
// does it not support Async, or were we told to run it forground ?
if (!_fAsync || !_fBackground) { IRunnableTask *pTask; if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask))) { if (!_fBackground) { // make sure there is no extract task already underway as we
// are not adding this to the queue...
_pView->_pScheduler->RemoveTasks(TOID_ExtractImageTask, _dwTaskID, TRUE); } hr = pTask->Run();
pTask->Release(); } } else { DWORD dwPriority = _dwPriority - PRIORITY_DELTA_EXTRACT; if (S_OK != _pView->_pScheduler->MoveTask(TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT)) { IRunnableTask *pTask; if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask))) { // add the task to the scheduler...
TraceMsg(TF_DEFVIEW, "CTestCacheTask *ADDING* CExtractImageTask (path=%s, priority=%x)", _szPath, dwPriority); hr = _pView->_pScheduler->AddTask2(pTask, TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT); pTask->Release(); } } // signify we want a default icon for now....
hr = S_FALSE; } } return hr; }
CTestCacheTask::CTestCacheTask(DWORD dwTaskID, CDefView* pView, IExtractImage *pExtract, LPCWSTR pszPath, FILETIME ftDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority, BOOL fAsync, BOOL fBackground, BOOL fForce) : CRunnableTask(RTF_DEFAULT), _iItem(iItem), _dwTaskID(dwTaskID), _dwFlags(dwFlags), _dwPriority(dwPriority), _fAsync(fAsync), _fBackground(fBackground), _fForce(fForce), _pExtract(pExtract), _pView(pView), _ftDateStamp(ftDateStamp) { StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath));
_pExtract->AddRef(); }
CTestCacheTask::~CTestCacheTask() { ILFree(_pidl);
_pExtract->Release(); }
HRESULT CTestCacheTask::Init(LPCITEMIDLIST pidl) { return SHILClone(pidl, &_pidl); }
HRESULT CTestCacheTask_Create(DWORD dwTaskID, CDefView* pView, IExtractImage *pExtract, LPCWSTR pszPath, FILETIME ftDateStamp, LPCITEMIDLIST pidl, int iItem, DWORD dwFlags, DWORD dwPriority, BOOL fAsync, BOOL fBackground, BOOL fForce, CTestCacheTask **ppTask) { *ppTask = NULL;
HRESULT hr; CTestCacheTask * pNew = new CTestCacheTask(dwTaskID, pView, pExtract, pszPath, ftDateStamp, iItem, dwFlags, dwPriority, fAsync, fBackground, fForce); if (pNew) { hr = pNew->Init(pidl); if (SUCCEEDED(hr)) { *ppTask = pNew; hr = S_OK; } else pNew->Release(); } else hr = E_OUTOFMEMORY; return hr; }
class CDiskCacheTask : public CRunnableTask { public: STDMETHODIMP RunInitRT(void);
CDiskCacheTask(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags); HRESULT Init(LPCITEMIDLIST pidl);
private: ~CDiskCacheTask();
int _iItem; LPITEMIDLIST _pidl; CDefView* _pView; WCHAR _szPath[MAX_PATH]; FILETIME _ftDateStamp; DWORD _dwTaskID; DWORD _dwPriority; IExtractImage *_pExtract; DWORD _dwFlags; };
CDiskCacheTask::CDiskCacheTask(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags) : CRunnableTask(RTF_DEFAULT), _pView(pView), _dwTaskID(dwTaskID), _dwPriority(dwPriority), _iItem(iItem), _ftDateStamp(ftDateStamp), _pExtract(pExtract), _dwFlags(dwFlags) { StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath)); _pExtract->AddRef(); }
CDiskCacheTask::~CDiskCacheTask() { ILFree(_pidl); _pExtract->Release(); }
HRESULT CDiskCacheTask::Init(LPCITEMIDLIST pidl) { return SHILClone(pidl, &_pidl); }
STDMETHODIMP CDiskCacheTask::RunInitRT() { DWORD dwLock;
HRESULT hr = E_FAIL;
if (_dwFlags & IEIFLAG_CACHE) { hr = _pView->_pDiskCache->Open(STGM_READ, &dwLock); if (SUCCEEDED(hr)) { HBITMAP hBmp; hr = _pView->_pDiskCache->GetEntry(_szPath, STGM_READ, &hBmp); if (SUCCEEDED(hr)) { TraceMsg(TF_DEFVIEW, "CDiskCacheTask *CACHE* (path=%s, priority=%x)", _szPath, _dwPriority); hr = _pView->UpdateImageForItem(_dwTaskID, hBmp, _iItem, _pidl, _szPath, _ftDateStamp, FALSE, _dwPriority); if (hr != S_FALSE) DeleteObject(hBmp); } // set the tick count so we know when we last accessed the disk cache
SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL); _pView->_pDiskCache->ReleaseLock(&dwLock); } }
if (FAILED(hr)) // We couldn't pull it out of the disk cache, try an extract
{ DWORD dwPriority = _dwPriority - PRIORITY_DELTA_EXTRACT; if (S_OK != _pView->_pScheduler->MoveTask(TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT)) { IRunnableTask *pTask; if (SUCCEEDED(hr = CExtractImageTask_Create(_dwTaskID, _pView, _pExtract, _szPath, _pidl, _ftDateStamp, _iItem, _dwFlags, _dwPriority, &pTask))) { // add the task to the scheduler...
TraceMsg(TF_DEFVIEW, "CDiskCacheTask *ADDING* CExtractImageTask (path=%s, priority=%x)", _szPath, dwPriority); hr = _pView->_pScheduler->AddTask2(pTask, TOID_ExtractImageTask, _dwTaskID, dwPriority, ITSSFLAG_TASK_PLACEINFRONT); pTask->Release(); } } } return hr; }
HRESULT CDiskCacheTask_Create(DWORD dwTaskID, CDefView *pView, DWORD dwPriority, int iItem, LPCITEMIDLIST pidl, LPCWSTR pszPath, FILETIME ftDateStamp, IExtractImage *pExtract, DWORD dwFlags, IRunnableTask **ppTask) { HRESULT hr; CDiskCacheTask *pTask = new CDiskCacheTask(dwTaskID, pView, dwPriority, iItem, pszPath, ftDateStamp, pExtract, dwFlags); if (pTask) { hr = pTask->Init(pidl); if (SUCCEEDED(hr)) hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask)); pTask->Release(); } else hr = E_OUTOFMEMORY; return hr; }
class CWriteCacheTask : public CRunnableTask { public: STDMETHOD (RunInitRT)();
CWriteCacheTask(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp, HBITMAP hImage);
private: ~CWriteCacheTask();
LONG _lState; CDefView* _pView; WCHAR _szPath[MAX_PATH]; FILETIME _ftDateStamp; HBITMAP _hImage; DWORD _dwTaskID; };
CWriteCacheTask::CWriteCacheTask(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp, HBITMAP hImage) : CRunnableTask(RTF_DEFAULT), _dwTaskID(dwTaskID), _hImage(hImage), _pView(pView), _ftDateStamp(ftDateStamp) { StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath)); }
CWriteCacheTask::~CWriteCacheTask() { DeleteObject(_hImage); }
HRESULT CWriteCacheTask_Create(DWORD dwTaskID, CDefView *pView, LPCWSTR pszPath, FILETIME ftDateStamp, HBITMAP hImage, IRunnableTask **ppTask) { *ppTask = NULL;
CWriteCacheTask * pNew = new CWriteCacheTask(dwTaskID, pView, pszPath, ftDateStamp, hImage); if (!pNew) return E_OUTOFMEMORY;
*ppTask = SAFECAST(pNew, IRunnableTask *); return S_OK; }
STDMETHODIMP CWriteCacheTask::RunInitRT() { DWORD dwLock;
HRESULT hr = _pView->_pDiskCache->Open(STGM_WRITE, &dwLock); if (hr == STG_E_FILENOTFOUND) { hr = _pView->_pDiskCache->Create(STGM_WRITE, &dwLock); }
if (SUCCEEDED(hr)) { hr = _pView->_pDiskCache->AddEntry(_szPath, IsNullTime(&_ftDateStamp) ? NULL : &_ftDateStamp, STGM_WRITE, _hImage); // set the tick count so that when the timer goes off, we can know when we
// last used it...
SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL); hr = _pView->_pDiskCache->ReleaseLock(&dwLock); }
return hr; }
class CReadAheadTask : public IRunnableTask { public: CReadAheadTask(CDefView *pView); HRESULT Init();
// IUnknown
STDMETHOD (QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// IRunnableTask
STDMETHOD (Run)(void); STDMETHOD (Kill)(BOOL fWait); STDMETHOD (Suspend)(); STDMETHOD (Resume)(); STDMETHOD_(ULONG, IsRunning)(void);
private: ~CReadAheadTask(); HRESULT InternalResume();
LONG _cRef; LONG _lState; CDefView *_pView; HANDLE _hEvent;
ULONG _ulCntPerPage; ULONG _ulCntTotal; ULONG _ulCnt; };
CReadAheadTask::~CReadAheadTask() { if (_hEvent) CloseHandle(_hEvent); }
CReadAheadTask::CReadAheadTask(CDefView *pView) : _cRef(1), _pView(pView) { _ulCntPerPage = pView->_ApproxItemsPerView(); _ulCntTotal = ListView_GetItemCount(pView->_hwndListview); #ifndef DEBUG
// Because we define a small cache in debug we need to only do this
// in retail. Otherwise we would not be able to debug readahead.
_ulCntTotal = min(_ulCntTotal, (ULONG)pView->_iMaxCacheSize); #endif
_ulCnt = _ulCntPerPage; }
HRESULT CReadAheadTask::Init() { _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); return _hEvent ? S_OK : E_OUTOFMEMORY; }
STDMETHODIMP CReadAheadTask::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CReadAheadTask, IRunnableTask), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CReadAheadTask::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CReadAheadTask::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CReadAheadTask_Create(CDefView *pView, IRunnableTask **ppTask) { HRESULT hr; CReadAheadTask *pTask = new CReadAheadTask(pView); if (pTask) { hr = pTask->Init(); if (SUCCEEDED(hr)) hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask)); pTask->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
STDMETHODIMP CReadAheadTask::Run() { if (_lState == IRTIR_TASK_RUNNING) { return S_FALSE; }
if (_lState == IRTIR_TASK_PENDING) { // it is about to die, so fail
return E_FAIL; }
LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING); if (lRes == IRTIR_TASK_PENDING) { _lState = IRTIR_TASK_FINISHED; return S_OK; }
// otherwise, run the task ....
HRESULT hr = InternalResume(); if (hr != E_PENDING) _lState = IRTIR_TASK_FINISHED;
return hr; }
STDMETHODIMP CReadAheadTask::Suspend() { if (_lState != IRTIR_TASK_RUNNING) { return E_FAIL; }
// suspend ourselves
LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED); if (lRes == IRTIR_TASK_FINISHED) { _lState = lRes; return S_OK; }
// if it is running, then there is an Event Handle, if we have passed where
// we are using it, then we are close to finish, so it will ignore the suspend
// request
ASSERT(_hEvent); SetEvent(_hEvent);
return S_OK; }
STDMETHODIMP CReadAheadTask::Resume() { if (_lState != IRTIR_TASK_SUSPENDED) { return E_FAIL; }
ResetEvent(_hEvent); _lState = IRTIR_TASK_RUNNING;
HRESULT hr = InternalResume(); if (hr != E_PENDING) { _lState= IRTIR_TASK_FINISHED; } return hr; }
STDMETHODIMP CReadAheadTask::Kill(BOOL fWait) { if (_lState == IRTIR_TASK_RUNNING) { LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING); if (lRes == IRTIR_TASK_FINISHED) { _lState = lRes; } else if (_hEvent) { // signal the event it is likely to be waiting on
SetEvent(_hEvent); }
return S_OK; } else if (_lState == IRTIR_TASK_PENDING || _lState == IRTIR_TASK_FINISHED) { return S_FALSE; }
return E_FAIL; }
STDMETHODIMP_(ULONG) CReadAheadTask::IsRunning() { return _lState; }
HRESULT CReadAheadTask::InternalResume() { HRESULT hr = S_OK;
// pfortier: this algorithm of determining which guys are off the page or not, seems kind of broken.
// For example, grouping will screw it up. Also, the Z-order of the items, is not necessarily
// the same as the item order, and we're going by item order.
// Also, _ulCnt is calculated before dui view is present, so the value is off.
TraceMsg(TF_DEFVIEW, "ReadAhead: Start");
for (; _ulCnt < _ulCntTotal; ++_ulCnt) { // See if we need to suspend
if (WaitForSingleObject(_hEvent, 0) == WAIT_OBJECT_0) { // why were we signalled ...
if (_lState == IRTIR_TASK_SUSPENDED) { hr = E_PENDING; break; } else { hr = E_FAIL; break; } }
LV_ITEMW rgItem; rgItem.iItem = (int)_ulCnt; rgItem.mask = LVIF_IMAGE; rgItem.iSubItem = 0;
TraceMsg(TF_DEFVIEW, "Thumbnail readahead for item %d", _ulCnt); // This will force the extraction of the image if necessary. We will extract it at the right
// priority, by determining if the item is visible during GetDisplayInfo.
int iItem = ListView_GetItem(_pView->_hwndListview, &rgItem); }
TraceMsg(TF_DEFVIEW, "ReadAhead: Done (hr:%x)", hr);
return hr; }
class CFileTypePropertiesTask : public CRunnableTask { public: CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId); STDMETHODIMP RunInitRT(); STDMETHODIMP InternalResumeRT();
private: ~CFileTypePropertiesTask();
CDefView *_pdsv; LPITEMIDLIST _pidl; UINT _uMaxPropertiesToShow; UINT _uId; };
CFileTypePropertiesTask::CFileTypePropertiesTask(HRESULT *phr, CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId) : CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pdsv(pdsv), _uMaxPropertiesToShow(uMaxPropertiesToShow), _uId(uId) { *phr = SHILClone(pidl, &_pidl); }
CFileTypePropertiesTask::~CFileTypePropertiesTask() { ILFree(_pidl); } STDMETHODIMP CFileTypePropertiesTask::RunInitRT() { return S_OK; }
STDMETHODIMP CFileTypePropertiesTask::InternalResumeRT(void) { // If Columns are not loaded yet, this means this window is just starting up
// so we want to give it some time to finish the startup (let it paint and such)
// before we proceed here because the first call to GetImportantColumns will
// causes all column handlers to be loaded, a slow process.
if (!_pdsv->_bLoadedColumns) { if (WaitForSingleObject(_hDone, 750) == WAIT_OBJECT_0) { return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL; } }
UINT rgColumns[8]; // currently _uMaxPropertiesToShow is 2, this is big enough if that grows
UINT cColumns = min(_uMaxPropertiesToShow, ARRAYSIZE(rgColumns));
if (SUCCEEDED(_pdsv->_GetImportantColumns(_pidl, rgColumns, &cColumns))) { CBackgroundTileInfo *pbgTileInfo = new CBackgroundTileInfo(_pidl, _uId, rgColumns, cColumns); if (pbgTileInfo) { _pidl = NULL; // give up ownership of this, ILFree checks for null
if (!PostMessage(_pdsv->_hwndView, WM_DSV_SETIMPORTANTCOLUMNS, 0, (LPARAM)pbgTileInfo)) delete pbgTileInfo; } } return S_OK; }
HRESULT CFileTypePropertiesTask_CreateInstance(CDefView *pdsv, LPCITEMIDLIST pidl, UINT uMaxPropertiesToShow, UINT uId, IRunnableTask **ppTask) { HRESULT hr; CFileTypePropertiesTask *pFTPTask = new CFileTypePropertiesTask(&hr, pdsv, pidl, uMaxPropertiesToShow, uId); if (pFTPTask) { if (SUCCEEDED(hr)) *ppTask = SAFECAST(pFTPTask, IRunnableTask*); else pFTPTask->Release(); } else hr = E_OUTOFMEMORY; return hr; }
class CExtractImageTask : public IRunnableTask { public: CExtractImageTask(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract, LPCWSTR pszPath, LPCITEMIDLIST pidl, FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority); HRESULT Init(LPCITEMIDLIST pidl);
// IUnknown
STDMETHOD (QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// IRunnableTask
STDMETHOD (Run)(void); STDMETHOD (Kill)(BOOL fWait); STDMETHOD (Suspend)(); STDMETHOD (Resume)(); STDMETHOD_(ULONG, IsRunning)(void);
private: ~CExtractImageTask(); HRESULT InternalResume();
LONG _cRef; LONG _lState; IExtractImage *_pExtract; #if 0
IRunnableTask *_pTask; #endif
WCHAR _szPath[MAX_PATH]; LPITEMIDLIST _pidl; CDefView* _pView; DWORD _dwMask; DWORD _dwFlags; int _iItem; HBITMAP _hBmp; FILETIME _ftDateStamp; DWORD _dwTaskID; DWORD _dwPriority; };
CExtractImageTask::CExtractImageTask(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract, LPCWSTR pszPath, LPCITEMIDLIST pidl, FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority) : _cRef(1), _lState(IRTIR_TASK_NOT_RUNNING), _dwTaskID(dwTaskID), _ftDateStamp(ftNewDateStamp), _dwFlags(dwFlags), _pExtract(pExtract), _pView(pView), _dwPriority(dwPriority) { _pExtract->AddRef();
StrCpyNW(_szPath, pszPath, ARRAYSIZE(_szPath)); _iItem = iItem == -1 ? _pView->_FindItem(pidl, NULL, FALSE) : iItem; _dwMask = pView->_GetOverlayMask(pidl); }
CExtractImageTask::~CExtractImageTask() { _pExtract->Release(); #if 0
if (_pTask) _pTask->Release(); #endif
ILFree(_pidl);
if (_hBmp) DeleteObject(_hBmp); }
STDMETHODIMP CExtractImageTask::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CExtractImageTask, IRunnableTask), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CExtractImageTask::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CExtractImageTask::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CExtractImageTask::Init(LPCITEMIDLIST pidl) { return SHILClone(pidl, &_pidl); }
HRESULT CExtractImageTask_Create(DWORD dwTaskID, CDefView*pView, IExtractImage *pExtract, LPCWSTR pszPath, LPCITEMIDLIST pidl, FILETIME ftNewDateStamp, int iItem, DWORD dwFlags, DWORD dwPriority, IRunnableTask **ppTask) { HRESULT hr; CExtractImageTask *pTask = new CExtractImageTask(dwTaskID, pView, pExtract, pszPath, pidl, ftNewDateStamp, iItem, dwFlags, dwPriority); if (pTask) { hr = pTask->Init(pidl); if (SUCCEEDED(hr)) hr = pTask->QueryInterface(IID_PPV_ARG(IRunnableTask, ppTask)); pTask->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
STDMETHODIMP CExtractImageTask::Run(void) { HRESULT hr = E_FAIL; if (_lState == IRTIR_TASK_RUNNING) { hr = S_FALSE; } else if (_lState == IRTIR_TASK_PENDING) { hr = E_FAIL; } else if (_lState == IRTIR_TASK_NOT_RUNNING) { LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING); if (lRes == IRTIR_TASK_PENDING) { _lState = IRTIR_TASK_FINISHED; return S_OK; }
// extractor may support IRunnableTask so they can support being
// canceled in the middle of the extract call. CHtmlThumb & CImgCtxThumb
// extractors use this. CImgCtxThumb has been replaced that with our GDI+ extractor
#if 0
if (!_pTask) { _pExtract->QueryInterface(IID_PPV_ARG(IRunnableTask, &_pTask)); } #endif
if (_lState == IRTIR_TASK_RUNNING) { TraceMsg(TF_DEFVIEW, "CExtractImageTask *START* (path=%s, priority=%x)", _szPath, _dwPriority); // start the extractor....
// extractor can return S_FALSE and set _hBmp to NULL. We will use _hBmp to recognize this situation
ASSERT(_hBmp == NULL); if (FAILED(_pExtract->Extract(&_hBmp))) { _hBmp = NULL; } }
if (_hBmp && _lState == IRTIR_TASK_RUNNING) { TraceMsg(TF_DEFVIEW, "CExtractImageTask *EXTRACT* (path=%s, priority=%x)", _szPath, _dwPriority); hr = InternalResume(); }
if (_lState != IRTIR_TASK_SUSPENDED || hr != E_PENDING) { _lState = IRTIR_TASK_FINISHED; } }
return hr; }
STDMETHODIMP CExtractImageTask::Kill(BOOL fWait) { // This is broken: by not setting the fSuspended flag on the task,
// the scheduler doesn't know to call Resume back. Instead, it calls
// Run. This causes the task to never finish.
#if 0
if (_lState != IRTIR_TASK_RUNNING) { return S_FALSE; }
LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING); if (lRes == IRTIR_TASK_FINISHED) { _lState = lRes; return S_OK; }
// does it support IRunnableTask ? Can we kill it ?
if (_pExtract) { IRunnableTask *pTask; if (SUCCEEDED(_pExtract->QueryInterface(IID_PPV_ARG(IRunnableTask, &pTask)))) { pTask->Kill(FALSE); pTask->Release(); } } return S_OK; #else // 0
return E_NOTIMPL;
#endif // 0
}
STDMETHODIMP CExtractImageTask::Suspend(void) { // This is broken: by not setting the fSuspended flag on the task,
// the scheduler doesn't know to call Resume back. Instead, it calls
// Run. This causes the task to never finish.
#if 0
if (!_pTask) return E_NOTIMPL;
if (_lState != IRTIR_TASK_RUNNING) return E_FAIL;
LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED); HRESULT hr = _pTask->Suspend(); if (SUCCEEDED(hr)) { lRes = (LONG) _pTask->IsRunning(); if (lRes == IRTIR_TASK_SUSPENDED) { if (lRes != IRTIR_TASK_RUNNING) { _lState = lRes; } } } else { _lState = lRes; }
return hr; #else // 0
return E_NOTIMPL;
#endif // 0
}
STDMETHODIMP CExtractImageTask::Resume(void) { #if 0
if (!_pTask) return E_NOTIMPL;
if (_lState != IRTIR_TASK_SUSPENDED) { return E_FAIL; }
_lState = IRTIR_TASK_RUNNING;
HRESULT hr = _pTask->Resume(); if (SUCCEEDED(hr)) { hr = InternalResume(); }
return hr; #else // 0
return E_NOTIMPL;
#endif // 0
}
HRESULT CExtractImageTask::InternalResume() { ASSERT(_hBmp != NULL); BOOL bCache = (_dwFlags & IEIFLAG_CACHE);
if (bCache) { IShellFolder* psf = NULL;
if (SUCCEEDED(_pView->GetShellFolder(&psf))) { TCHAR szPath[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(psf, _pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath)))) { // Make sure we don't request to cache an item that is encrypted in a folder that is not
if (SFGAO_ENCRYPTED == SHGetAttributes(psf, _pidl, SFGAO_ENCRYPTED)) { bCache = FALSE; LPITEMIDLIST pidlParent = _pView->_GetViewPidl(); if (pidlParent) { if (SFGAO_ENCRYPTED == SHGetAttributes(NULL, pidlParent, SFGAO_ENCRYPTED)) { bCache = TRUE; } #ifdef DEBUG
else { TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s is encrypted in unencrypted folder)", szPath); } #endif
ILFree(pidlParent); } }
// Make sure we don't request to cache an item that has differing ACLs applied
if (bCache) { PACL pdacl; PSECURITY_DESCRIPTOR psd;
bCache = FALSE; if (ERROR_SUCCESS == GetNamedSecurityInfo(szPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pdacl, NULL, &psd)) { SECURITY_DESCRIPTOR_CONTROL sdc; DWORD dwRevision; if (GetSecurityDescriptorControl(psd, &sdc, &dwRevision) && !(sdc & SE_DACL_PROTECTED)) { if (pdacl) { PKNOWN_ACE pACE = (PKNOWN_ACE) FirstAce(pdacl); if ((pACE->Header.AceType != ACCESS_DENIED_ACE_TYPE) || (pACE->Header.AceFlags & INHERITED_ACE)) { bCache = TRUE; } #ifdef DEBUG
else { TraceMsg(TF_DEFVIEW, "CExtractImageTask (%s has a non-inherited deny acl)", szPath); } #endif
} else { bCache = TRUE; // NULL dacl == everyone all access
} } #ifdef DEBUG
else { TraceMsg(TF_DEFVIEW,"CExtractImageTask (%s has a protected dacl)", szPath); } #endif
LocalFree(psd); } } } psf->Release(); } if (!bCache && _pView->_pDiskCache) // If we were asked to cache and are not for security reasons
{ DWORD dwLock; if (SUCCEEDED(_pView->_pDiskCache->Open(STGM_WRITE, &dwLock))) { _pView->_pDiskCache->DeleteEntry(_szPath); _pView->_pDiskCache->ReleaseLock(&dwLock); SetTimer(_pView->_hwndView, DV_IDTIMER_DISKCACHE, 2000, NULL); // Keep open for 2 seconds, just in case
} } }
HRESULT hr = _pView->UpdateImageForItem(_dwTaskID, _hBmp, _iItem, _pidl, _szPath, _ftDateStamp, bCache, _dwPriority);
// UpdateImageForItem returns S_FALSE if it assumes ownership of bitmap
if (hr == S_FALSE) { _hBmp = NULL; }
_lState = IRTIR_TASK_FINISHED;
return hr; }
STDMETHODIMP_(ULONG) CExtractImageTask::IsRunning(void) { return _lState; }
class CCategoryTask : public CRunnableTask { public: STDMETHOD (RunInitRT)();
CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl);
private: ~CCategoryTask();
CDefView* _pView; LPITEMIDLIST _pidl; ICategorizer* _pcat; UINT _uId; };
CCategoryTask::CCategoryTask(CDefView *pView, UINT uId, LPCITEMIDLIST pidl) : CRunnableTask(RTF_DEFAULT), _uId(uId), _pView(pView), _pcat(pView->_pcat) { _pcat->AddRef(); _pidl = ILClone(pidl); }
CCategoryTask::~CCategoryTask() { ATOMICRELEASE(_pcat); ILFree(_pidl); PostMessage(_pView->_hwndView, WM_DSV_GROUPINGDONE, 0, 0); }
HRESULT CCategoryTask_Create(CDefView *pView, LPCITEMIDLIST pidl, UINT uId, IRunnableTask **ppTask) { *ppTask = NULL; CCategoryTask * pNew = new CCategoryTask(pView, uId, pidl); if (!pNew) return E_OUTOFMEMORY;
*ppTask = SAFECAST(pNew, IRunnableTask *); return S_OK; }
STDMETHODIMP CCategoryTask::RunInitRT() { if (_pidl) { DWORD dwGroup = -1; _pcat->GetCategory(1, (LPCITEMIDLIST*)&_pidl, &dwGroup);
CBackgroundGroupInfo* pbggi = new CBackgroundGroupInfo(_pidl, _uId, dwGroup); if (pbggi) { _pidl = NULL; // Transferred ownership to BackgroundInfo
if (!PostMessage(_pView->_hwndView, WM_DSV_SETITEMGROUP, NULL, (LPARAM)pbggi)) delete pbggi; } }
return S_OK; }
class CGetCommandStateTask : public CRunnableTask { public: STDMETHODIMP RunInitRT(); STDMETHODIMP InternalResumeRT();
CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray);
private: ~CGetCommandStateTask();
CDefView *_pView; IUICommand *_puiCommand; IShellItemArray *_psiItemArray; };
HRESULT CGetCommandStateTask_Create(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray, IRunnableTask **ppTask) { *ppTask = NULL; CGetCommandStateTask *pNew = new CGetCommandStateTask(pView, puiCommand, psiItemArray); if (!pNew) return E_OUTOFMEMORY;
*ppTask = SAFECAST(pNew, IRunnableTask *); return S_OK; }
CGetCommandStateTask::CGetCommandStateTask(CDefView *pView, IUICommand *puiCommand,IShellItemArray *psiItemArray) : CRunnableTask(RTF_SUPPORTKILLSUSPEND) { _pView = pView; _puiCommand = puiCommand; _puiCommand->AddRef(); _psiItemArray = psiItemArray; if (_psiItemArray) _psiItemArray->AddRef(); } CGetCommandStateTask::~CGetCommandStateTask() { ATOMICRELEASE(_puiCommand); ATOMICRELEASE(_psiItemArray); }
STDMETHODIMP CGetCommandStateTask::RunInitRT() { return S_OK; }
STDMETHODIMP CGetCommandStateTask::InternalResumeRT() { // Don't want to interfere with the explorer view starting up, so give it a head start.
// we were told to either suspend or quit...
if (WaitForSingleObject(_hDone, 1000) == WAIT_OBJECT_0) { return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL; } UISTATE uis; HRESULT hr = _puiCommand->get_State(_psiItemArray, TRUE, &uis); if (SUCCEEDED(hr) && (uis==UIS_ENABLED)) { _pView->_PostSelectionChangedMessage(LVIS_SELECTED); } return S_OK; }
|