1897 lines
54 KiB
C++
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;
|
|
}
|