#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