WindowsXP-SP1/shell/shdocvw/nsctask.cpp

449 lines
14 KiB
C++

#include "priv.h"
#include "nsc.h"
#include "nsctask.h"
#define PRIORITY_ENUM ITSAT_DEFAULT_PRIORITY
#define PRIORITY_ICON PRIORITY_ENUM + 1 //needs to be slightly higher priority
#define PRIORITY_OVERLAY ITSAT_DEFAULT_PRIORITY
/////////////////////////////////////////////////////////////////////////
// COPY AND PASTE ALERT
// this code is mostly copied and pasted from browseui/icotask.cpp
// see lamadio and/or davemi for why these aren't combined or shared
/////////////////////////////////////////////////////////////////////////
// {7DB7F689-BBDB-483f-A8A9-C6E963E8D274}
EXTERN_C const GUID TASKID_BackgroundEnum = { 0x7db7f689, 0xbbdb, 0x483f, { 0xa8, 0xa9, 0xc6, 0xe9, 0x63, 0xe8, 0xd2, 0x74 } };
// {EB30900C-1AC4-11d2-8383-00C04FD918D0}
EXTERN_C const GUID TASKID_IconExtraction = { 0xeb30900c, 0x1ac4, 0x11d2, { 0x83, 0x83, 0x0, 0xc0, 0x4f, 0xd9, 0x18, 0xd0 } };
// {EB30900D-1AC4-11d2-8383-00C04FD918D0}
EXTERN_C const GUID TASKID_OverlayExtraction = { 0xeb30900d, 0x1ac4, 0x11d2, { 0x83, 0x83, 0x0, 0xc0, 0x4f, 0xd9, 0x18, 0xd0 } };
class CNscIconTask : public CRunnableTask
{
public:
CNscIconTask(LPITEMIDLIST pidl, PFNNSCICONTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId);
// IRunnableTask
STDMETHODIMP RunInitRT(void);
protected:
virtual ~CNscIconTask();
virtual void _Extract(IShellFolder *psf, LPCITEMIDLIST pidlItem);
BOOL _bOverlayTask;
LPITEMIDLIST _pidl;
PFNNSCICONTASKBALLBACK _pfnIcon;
CNscTree *_pns;
UINT_PTR _uId;
UINT _uSynchId;
};
CNscIconTask::CNscIconTask(LPITEMIDLIST pidl, PFNNSCICONTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId):
_pidl(pidl), _pfnIcon(pfn), _uId(uId), _uSynchId(uSynchId), CRunnableTask(RTF_DEFAULT)
{
_pns = pns;
if (_pns)
_pns->AddRef();
}
CNscIconTask::~CNscIconTask()
{
if (_pns)
_pns->Release();
ILFree(_pidl);
}
// IRunnableTask methods (override)
STDMETHODIMP CNscIconTask::RunInitRT(void)
{
IShellFolder* psf;
LPCITEMIDLIST pidlItem;
// We need to rebind because shell folders may not be thread safe.
if (SUCCEEDED(IEBindToParentFolder(_pidl, &psf, &pidlItem)))
{
_Extract(psf, pidlItem);
psf->Release();
}
return S_OK;
}
void CNscIconTask::_Extract(IShellFolder *psf, LPCITEMIDLIST pidlItem)
{
int iIcon = -1, iIconOpen = -1;
#ifdef IE5_36825
HRESULT hr = E_FAIL;
IShellIcon *psi;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellIcon, &psi))))
{
hr = psi->GetIconOf(pidlItem, 0, &iIcon);
if (hr == S_OK)
{
ULONG ulAttrs = SFGAO_FOLDER;
psf->GetAttributesOf(1, &pidlItem, &ulAttrs);
if (!(ulAttrs & SFGAO_FOLDER) || FAILED(psi->GetIconOf(pidlItem, GIL_OPENICON, &iIconOpen)))
iIconOpen = iIcon;
}
psi->Release();
}
if (hr != S_OK)
{
#endif
// slow way...
iIcon = IEMapPIDLToSystemImageListIndex(psf, pidlItem, &iIconOpen);
#ifdef IE5_36825
}
#endif
// REARCHITECT : This is no good. We are attempted to see if the content is offline. That should
// be done by using IQueryInfo::xxx(). This should go in the InternetShortcut object.
// IShellFolder2::GetItemData or can also be used.
//
// See if it is a link. If it is not, then it can't be in the wininet cache and can't
// be pinned (sticky cache entry) or greyed (unavailable when offline)
DWORD dwFlags = 0;
BOOL fAvailable;
BOOL fSticky;
// GetLinkInfo() will fail if the SFGAO_FOLDER or SFGAO_BROWSER bits aren't set.
if (pidlItem && SUCCEEDED(GetLinkInfo(psf, pidlItem, &fAvailable, &fSticky)))
{
if (!fAvailable)
{
dwFlags |= NSCICON_GREYED;
}
if (fSticky)
{
dwFlags |= NSCICON_PINNED;
}
}
else
{
//item is not a link
dwFlags |= NSCICON_DONTREFETCH;
}
_pfnIcon(_pns, _uId, iIcon, iIconOpen, dwFlags, _uSynchId);
}
// takes ownership of pidl
HRESULT AddNscIconTask(IShellTaskScheduler* pts, LPITEMIDLIST pidl, PFNNSCICONTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId)
{
HRESULT hr;
CNscIconTask* pTask = new CNscIconTask(pidl, pfn, pns, uId, uSynchId);
if (pTask)
{
hr = pts->AddTask(SAFECAST(pTask, IRunnableTask*), TASKID_IconExtraction,
ITSAT_DEFAULT_LPARAM, PRIORITY_ICON);
pTask->Release();
}
else
{
hr = E_OUTOFMEMORY;
ILFree(pidl);
}
return hr;
}
class CNscOverlayTask : public CNscIconTask
{
public:
CNscOverlayTask(LPITEMIDLIST pidl, PFNNSCOVERLAYTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId);
protected:
virtual void _Extract(IShellFolder *psf, LPCITEMIDLIST pidlItem);
PFNNSCOVERLAYTASKBALLBACK _pfnOverlay;
};
CNscOverlayTask::CNscOverlayTask(LPITEMIDLIST pidl, PFNNSCOVERLAYTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId) :
CNscIconTask(pidl, NULL, pns, uId, uSynchId), _pfnOverlay(pfn)
{
}
void CNscOverlayTask::_Extract(IShellFolder *psf, LPCITEMIDLIST pidlItem)
{
IShellIconOverlay *psio;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &psio))))
{
int iOverlayIndex = 0;
if (psio->GetOverlayIndex(pidlItem, &iOverlayIndex) == S_OK && iOverlayIndex > 0)
{
_pfnOverlay(_pns, _uId, iOverlayIndex, _uSynchId);
}
psio->Release();
}
}
// takes ownership of pidl
HRESULT AddNscOverlayTask(IShellTaskScheduler* pts, LPITEMIDLIST pidl, PFNNSCOVERLAYTASKBALLBACK pfn, CNscTree *pns, UINT_PTR uId, UINT uSynchId)
{
HRESULT hr;
CNscOverlayTask *pTask = new CNscOverlayTask(pidl, pfn, pns, uId, uSynchId);
if (pTask)
{
hr = pts->AddTask(SAFECAST(pTask, IRunnableTask*), TASKID_OverlayExtraction,
ITSAT_DEFAULT_LPARAM, PRIORITY_OVERLAY);
pTask->Release();
}
else
{
hr = E_OUTOFMEMORY;
ILFree(pidl); // we own it, clean up here
}
return hr;
}
class CNscEnumTask : public CRunnableTask
{
public:
CNscEnumTask(PFNNSCENUMTASKBALLBACK pfn,
CNscTree *pns, UINT_PTR uId, DWORD dwSig, DWORD grfFlags, HDPA hdpaOrder,
DWORD dwOrderSig, BOOL fForceExpand, UINT uDepth, BOOL fUpdate, BOOL fUpdatePidls);
HRESULT Init(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExpandingTo);
// IRunnableTask
STDMETHODIMP RunInitRT(void);
STDMETHODIMP InternalResumeRT(void);
private:
virtual ~CNscEnumTask();
LPITEMIDLIST _pidl;
PFNNSCENUMTASKBALLBACK _pfn;
CNscTree * _pns;
UINT_PTR _uId;
DWORD _dwSig;
DWORD _grfFlags;
HDPA _hdpaOrder;
LPITEMIDLIST _pidlExpandingTo;
DWORD _dwOrderSig;
BOOL _fForceExpand;
BOOL _fUpdate;
BOOL _fUpdatePidls;
UINT _uDepth;
HDPA _hdpa;
IShellFolder * _psf;
IEnumIDList * _penum;
static DWORD s_dwMaxItems;
};
DWORD CNscEnumTask::s_dwMaxItems = 0;
CNscEnumTask::CNscEnumTask(PFNNSCENUMTASKBALLBACK pfn, CNscTree *pns,
UINT_PTR uId, DWORD dwSig, DWORD grfFlags, HDPA hdpaOrder,
DWORD dwOrderSig, BOOL fForceExpand, UINT uDepth,
BOOL fUpdate, BOOL fUpdatePidls) :
CRunnableTask(RTF_SUPPORTKILLSUSPEND), _pfn(pfn), _uId(uId), _dwSig(dwSig), _grfFlags(grfFlags),
_hdpaOrder(hdpaOrder), _dwOrderSig(dwOrderSig), _fForceExpand(fForceExpand), _uDepth(uDepth),
_fUpdate(fUpdate), _fUpdatePidls(fUpdatePidls)
{
_pns = pns;
if (_pns)
_pns->AddRef();
}
HRESULT CNscEnumTask::Init(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExpandingTo)
{
if (pidlExpandingTo)
SHILClone(pidlExpandingTo, &_pidlExpandingTo); // failure OK
return SHILClone(pidl, &_pidl);
}
CNscEnumTask::~CNscEnumTask()
{
if (_pns)
_pns->Release();
ILFree(_pidl);
ILFree(_pidlExpandingTo);
OrderList_Destroy(&_hdpaOrder, TRUE); // calls DPA_Destroy(_hdpaOrder)
ATOMICRELEASE(_psf);
ATOMICRELEASE(_penum);
if (_hdpa)
OrderList_Destroy(&_hdpa, TRUE); // calls DPA_Destroy(hdpa)
}
BOOL OrderList_AppendCustom(HDPA hdpa, LPITEMIDLIST pidl, int nOrder, DWORD dwAttrib)
{
PORDERITEM poi = OrderItem_Create(pidl, nOrder);
if (poi)
{
poi->lParam = dwAttrib;
if (-1 != DPA_AppendPtr(hdpa, poi))
return TRUE;
OrderItem_Free(poi, FALSE); //don't free pidl because caller will do it
}
return FALSE;
}
// IRunnableTask methods (override)
STDMETHODIMP CNscEnumTask::RunInitRT(void)
{
if (!s_dwMaxItems)
{
DWORD dwDefaultLimit = 100; // Default value for the limit
DWORD dwSize = sizeof(s_dwMaxItems);
SHRegGetUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
TEXT("PartialExpandLimit"), NULL, &s_dwMaxItems, &dwSize,
FALSE, &dwDefaultLimit, sizeof(dwDefaultLimit));
if (!s_dwMaxItems)
s_dwMaxItems = dwDefaultLimit;
}
HRESULT hr = E_OUTOFMEMORY;
_hdpa = DPA_Create(2);
if (_hdpa)
{
// We need to rebind because shell folders may not be thread safe.
hr = IEBindToObject(_pidl, &_psf);
if (SUCCEEDED(hr))
{
hr = _psf->EnumObjects(NULL, _grfFlags, &_penum);
if (S_OK != hr)
{
// Callback function takes ownership of the pidls and hdpa
_pfn(_pns, _pidl, _uId, _dwSig, _hdpa, _pidlExpandingTo, _dwOrderSig, _uDepth, _fUpdate, _fUpdatePidls);
_pidl = NULL;
_pidlExpandingTo = NULL;
_hdpa = NULL;
if (SUCCEEDED(hr))
hr = E_FAIL;
}
}
}
return hr;
}
#define FILE_JUNCTION_FOLDER_FLAGS (SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_STREAM)
STDMETHODIMP CNscEnumTask::InternalResumeRT(void)
{
HRESULT hr = S_OK;
ULONG celt;
LPITEMIDLIST pidlTemp;
while (S_OK == _penum->Next(1, &pidlTemp, &celt))
{
// filter out zip files (they are both folders and files but we treat them as files)
// on downlevel SFGAO_STREAM is the same as SFGAO_HASSTORAGE so we'll let zip files slide through (oh well)
// better than not adding filesystem folders (that have storage)
if (!(_grfFlags & SHCONTF_NONFOLDERS) && IsOS(OS_WHISTLERORGREATER) &&
(SHGetAttributes(_psf, pidlTemp, FILE_JUNCTION_FOLDER_FLAGS) & FILE_JUNCTION_FOLDER_FLAGS) == FILE_JUNCTION_FOLDER_FLAGS)
{
ILFree(pidlTemp);
}
else if (!OrderList_AppendCustom(_hdpa, pidlTemp, -1, 0))
{
hr = E_OUTOFMEMORY;
ILFree(pidlTemp);
break;
}
if (!_fForceExpand && (DPA_GetPtrCount(_hdpa) > (int)s_dwMaxItems))
{
hr = E_ABORT;
break;
}
// we were told to either suspend or quit...
if (WaitForSingleObject(_hDone, 0) == WAIT_OBJECT_0)
{
return (_lState == IRTIR_TASK_SUSPENDED) ? E_PENDING : E_FAIL;
}
}
if (hr == S_OK)
{
ORDERINFO oinfo;
oinfo.psf = _psf;
oinfo.dwSortBy = OI_SORTBYNAME; // merge depends on by name.
if (_hdpaOrder && DPA_GetPtrCount(_hdpaOrder) > 0)
{
OrderList_Merge(_hdpa, _hdpaOrder, -1, (LPARAM)&oinfo, NULL, NULL);
oinfo.dwSortBy = OI_SORTBYORDINAL;
}
else
oinfo.dwSortBy = OI_SORTBYNAME;
DPA_Sort(_hdpa, OrderItem_Compare, (LPARAM)&oinfo);
OrderList_Reorder(_hdpa);
// Callback function takes ownership of the pidls and hdpa
_pfn(_pns, _pidl, _uId, _dwSig, _hdpa, _pidlExpandingTo, _dwOrderSig, _uDepth, _fUpdate, _fUpdatePidls);
_pidl = NULL;
_pidlExpandingTo = NULL;
_hdpa = NULL;
}
return S_OK; // return S_OK even if we failed
}
HRESULT AddNscEnumTask(IShellTaskScheduler* pts, LPCITEMIDLIST pidl, PFNNSCENUMTASKBALLBACK pfn,
CNscTree *pns, UINT_PTR uId, DWORD dwSig, DWORD grfFlags, HDPA hdpaOrder,
LPCITEMIDLIST pidlExpandingTo, DWORD dwOrderSig,
BOOL fForceExpand, UINT uDepth, BOOL fUpdate, BOOL fUpdatePidls)
{
HRESULT hr;
CNscEnumTask *pTask = new CNscEnumTask(pfn, pns, uId, dwSig, grfFlags,
hdpaOrder, dwOrderSig, fForceExpand, uDepth,
fUpdate, fUpdatePidls);
if (pTask)
{
hr = pTask->Init(pidl, pidlExpandingTo);
if (SUCCEEDED(hr))
{
hr = pts->AddTask(SAFECAST(pTask, IRunnableTask*), TASKID_BackgroundEnum,
ITSAT_DEFAULT_LPARAM, PRIORITY_ENUM);
}
pTask->Release();
}
else
{
OrderList_Destroy(&hdpaOrder, TRUE); // calls DPA_Destroy(hdpaOrder)
hr = E_OUTOFMEMORY;
}
return hr;
}
#ifdef DEBUG
#define TF_NSC 0x00002000
void DumpOrderItem(IShellFolder *psf, PORDERITEM poi)
{
if (poi)
{
TCHAR szDebugName[MAX_URL_STRING] = TEXT("Desktop");
DisplayNameOf(psf, poi->pidl, SHGDN_FORPARSING, szDebugName, ARRAYSIZE(szDebugName));
TraceMsg(TF_NSC, "OrderItem (%d, %s)\n", poi->nOrder, szDebugName);
}
}
void DumpOrderList(IShellFolder *psf, HDPA hdpa)
{
if (psf && hdpa)
{
TraceMsg(TF_NSC, "OrderList dump: #of items:%d\n", DPA_GetPtrCount(hdpa));
for (int i = 0; i < DPA_GetPtrCount(hdpa); i++)
{
PORDERITEM poi = (PORDERITEM)DPA_GetPtr(hdpa, i);
DumpOrderItem(psf, poi);
}
}
}
#endif