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

1897 lines
54 KiB
C++

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