#include "shellprv.h" #include "dpa.h" #include "datautil.h" typedef enum { NSWALK_DONTWALK, NSWALK_FOLDER, NSWALK_ITEM, NSWALK_LINK } NSWALK_ELEMENT_TYPE; class CNamespaceWalk : public INamespaceWalk { public: CNamespaceWalk(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // INamespaceWalk STDMETHODIMP Walk(IUnknown *punkToWalk, DWORD dwFlags, int cDepth, INamespaceWalkCB *pnswcb); STDMETHODIMP GetIDArrayResult(UINT *pcItems, LPITEMIDLIST **pppidl); private: ~CNamespaceWalk(); static int CALLBACK _FreeItems(LPITEMIDLIST pidl, IShellFolder *psf); static int CALLBACK _CompareItems(LPITEMIDLIST p1, LPITEMIDLIST p2, IShellFolder *psf); HRESULT _EnsureDPA(); HRESULT _AddItem(IShellFolder *psf, LPCITEMIDLIST pidl); HRESULT _AppendFull(LPCITEMIDLIST pidlFull); HRESULT _EnumFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, CDPA *pdpaItems); HRESULT _GetShortcutTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget); BOOL _IsFolderTarget(IShellFolder *psf, LPCITEMIDLIST pidl); HRESULT _WalkView(IFolderView *pfv); HRESULT _WalkFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, int cDepth); HRESULT _WalkDataObject(IDataObject *pdtobj); HRESULT _WalkParentAndItem(IParentAndItem *ppai); HRESULT _WalkIDList(IShellFolder *psfRoot, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta); HRESULT _WalkFolderItem(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth); HRESULT _WalkShortcut(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta); NSWALK_ELEMENT_TYPE _GetType(IShellFolder *psf, LPCITEMIDLIST pidl); BOOL _OneImpliesAll(IShellFolder *psf, LPCITEMIDLIST pidl); HRESULT _ProgressDialogQueryCancel(); void _ProgressDialogBegin(); void _ProgressDialogUpdate(LPCWSTR pszText); void _ProgressDialogEnd(); LONG _cRef; DWORD _dwFlags; int _cDepthMax; INamespaceWalkCB *_pnswcb; IActionProgressDialog *_papd; IActionProgress *_pap; #ifdef DEBUG TCHAR _szLastFolder[MAX_PATH]; // to track what we failed on #endif CDPA _dpaItems; }; STDAPI CNamespaceWalk_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { CNamespaceWalk *pnsw = new CNamespaceWalk(); if (!pnsw) return E_OUTOFMEMORY; HRESULT hr = pnsw->QueryInterface(riid, ppv); pnsw->Release(); return hr; } CNamespaceWalk::CNamespaceWalk() : _cRef(1) { _papd = NULL; _pap = NULL; } CNamespaceWalk::~CNamespaceWalk() { ASSERT(!_papd); ASSERT(!_pap); if ((HDPA)_dpaItems) _dpaItems.DestroyCallbackEx(_FreeItems, (IShellFolder *)NULL); } STDMETHODIMP_(ULONG) CNamespaceWalk::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CNamespaceWalk::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } HRESULT CNamespaceWalk::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CNamespaceWalk, INamespaceWalk), { 0 }, }; return QISearch(this, qit, riid, ppv); } int CALLBACK CNamespaceWalk::_FreeItems(LPITEMIDLIST pidl, IShellFolder *psf) { ILFree(pidl); return 1; } HRESULT CNamespaceWalk::_EnsureDPA() { return (HDPA)_dpaItems ? S_OK : (_dpaItems.Create(10) ? S_OK : E_OUTOFMEMORY); } // consumes pidl in all cases (success and failure) HRESULT CNamespaceWalk::_AppendFull(LPCITEMIDLIST pidlFull) { HRESULT hr = _ProgressDialogQueryCancel(); // ERROR_CANCELLED -> cancelled if (SUCCEEDED(hr)) { if (NSWF_DONT_ACCUMULATE_RESULT & _dwFlags) { hr = S_OK; } else { hr = _EnsureDPA(); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlClone; hr = SHILClone(pidlFull, &pidlClone); if (SUCCEEDED(hr) && (-1 == _dpaItems.AppendPtr(pidlClone))) { hr = E_OUTOFMEMORY; ILFree(pidlClone); } } } } return hr; } HRESULT CNamespaceWalk::_AddItem(IShellFolder *psf, LPCITEMIDLIST pidl) { HRESULT hr = S_OK; LPITEMIDLIST pidlFull = NULL; if (!(NSWF_DONT_ACCUMULATE_RESULT & _dwFlags)) { hr = SUCCEEDED(SHFullIDListFromFolderAndItem(psf, pidl, &pidlFull)) ? S_OK : S_FALSE;//couldn't get the pidl? Just skip the item } if (S_OK == hr) { hr = _pnswcb ? _pnswcb->FoundItem(psf, pidl) : S_OK; if (S_OK == hr) { hr = _AppendFull(pidlFull); } ILFree(pidlFull); } return SUCCEEDED(hr) ? S_OK : hr; // filter out S_FALSE success cases } int CALLBACK CNamespaceWalk::_CompareItems(LPITEMIDLIST p1, LPITEMIDLIST p2, IShellFolder *psf) { HRESULT hr = psf->CompareIDs(0, p1, p2); return (short)HRESULT_CODE(hr); } HRESULT CNamespaceWalk::_EnumFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, CDPA *pdpaItems) { CDPA dpaItems; HRESULT hr = dpaItems.Create(16) ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { IEnumIDList *penum; if (S_OK == psf->EnumObjects(NULL, SHCONTF_NONFOLDERS | SHCONTF_FOLDERS, &penum)) { LPITEMIDLIST pidl; ULONG c; while (SUCCEEDED(hr) && (S_OK == penum->Next(1, &pidl, &c))) { if (-1 == dpaItems.AppendPtr(pidl)) { ILFree(pidl); hr = E_OUTOFMEMORY; } else { hr = _ProgressDialogQueryCancel(); } } penum->Release(); } if (SUCCEEDED(hr)) { dpaItems.SortEx(_CompareItems, psf); if (pidlFirst && !(NSWF_FLAG_VIEWORDER & _dwFlags)) { // rotate the items array so pidlFirst is first in the list // cast for bogus SearchEx decl int iMid = dpaItems.SearchEx((LPITEMIDLIST)pidlFirst, 0, _CompareItems, psf, DPAS_SORTED); if (-1 != iMid) { int cItems = dpaItems.GetPtrCount(); CDPA dpaTemp; if (dpaTemp.Create(cItems)) { for (int i = 0; i < cItems; i++) { dpaTemp.SetPtr(i, dpaItems.GetPtr(iMid++)); if (iMid >= cItems) iMid = 0; } for (int i = 0; i < cItems; i++) { dpaItems.SetPtr(i, dpaTemp.GetPtr(i)); } dpaTemp.Destroy(); // don't free the pidls, just the array } } else { // pidlFirst not found in the enum, it might be hidden or filters // out some way, but make sure this always ends up in the dpa in this case LPITEMIDLIST pidlClone = ILClone(pidlFirst); if (pidlClone) { dpaItems.InsertPtr(0, pidlClone); } } } } } if (FAILED(hr)) { dpaItems.DestroyCallbackEx(_FreeItems, psf); dpaItems = NULL; } *pdpaItems = dpaItems; return hr; } NSWALK_ELEMENT_TYPE CNamespaceWalk::_GetType(IShellFolder *psf, LPCITEMIDLIST pidl) { NSWALK_ELEMENT_TYPE nwet = NSWALK_DONTWALK; DWORD dwAttribs = SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM | SFGAO_LINK); if ((dwAttribs & SFGAO_FOLDER) && (!(dwAttribs & SFGAO_STREAM) || (NSWF_TRAVERSE_STREAM_JUNCTIONS & _dwFlags))) { nwet = NSWALK_FOLDER; } else if ((dwAttribs & SFGAO_LINK) && !(NSWF_DONT_TRAVERSE_LINKS & _dwFlags)) { nwet = NSWALK_LINK; } else if ((dwAttribs & SFGAO_FILESYSTEM) || !(NSWF_FILESYSTEM_ONLY & _dwFlags)) { nwet = NSWALK_ITEM; } return nwet; } HRESULT CNamespaceWalk::_WalkIDList(IShellFolder *psfRoot, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta) { IShellFolder *psf; LPCITEMIDLIST pidlLast; HRESULT hr = SHBindToFolderIDListParent(psfRoot, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { switch (_GetType(psf, pidlLast)) { case NSWALK_FOLDER: hr = _WalkFolderItem(psf, pidlLast, cDepth + cFolderDepthDelta); break; case NSWALK_LINK: hr = _WalkShortcut(psf, pidlLast, cDepth, cFolderDepthDelta); break; case NSWALK_ITEM: hr = _AddItem(psf, pidlLast); break; } psf->Release(); } return hr; } HRESULT CNamespaceWalk::_WalkShortcut(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta) { HRESULT hr = S_OK; // If an error occured trying to resolve a shortcut then we simply skip // this shortcut and continue LPITEMIDLIST pidlTarget; if (SUCCEEDED(_GetShortcutTarget(psf, pidl, &pidlTarget))) { hr = _WalkIDList(NULL, pidlTarget, cDepth, cFolderDepthDelta); ILFree(pidlTarget); } return hr; } HRESULT CNamespaceWalk::_GetShortcutTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget) { *ppidlTarget = NULL; IShellLink *psl; if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_PPV_ARG_NULL(IShellLink, &psl)))) { if (S_OK == psl->Resolve(NULL, SLR_UPDATE | SLR_NO_UI)) { psl->GetIDList(ppidlTarget); } psl->Release(); } return *ppidlTarget ? S_OK : E_FAIL; } HRESULT CNamespaceWalk::_WalkFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, int cDepth) { if (cDepth > _cDepthMax) return S_OK; // done CDPA dpaItems; HRESULT hr = _EnumFolder(psf, pidlFirst, &dpaItems); if (SUCCEEDED(hr)) { UINT cFolders = 0; // breadth first traversal, so do the items (non folders) first // (this includes shortcuts and those can point to folders) for (int i = 0; (S_OK == hr) && (i < dpaItems.GetPtrCount()); i++) { switch (_GetType(psf, dpaItems.GetPtr(i))) { case NSWALK_FOLDER: cFolders++; break; case NSWALK_LINK: hr = _WalkShortcut(psf, dpaItems.GetPtr(i), cDepth, 1); break; case NSWALK_ITEM: hr = _AddItem(psf, dpaItems.GetPtr(i)); break; } } // no go deep into the folders if (cFolders) { for (int i = 0; (S_OK == hr) && (i < dpaItems.GetPtrCount()); i++) { if (NSWALK_FOLDER == _GetType(psf, dpaItems.GetPtr(i))) { hr = _WalkFolderItem(psf, dpaItems.GetPtr(i), cDepth + 1); } } } dpaItems.DestroyCallbackEx(_FreeItems, psf); } return hr; } HRESULT CNamespaceWalk::_WalkFolderItem(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth) { IShellFolder *psfNew; HRESULT hr = psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfNew)); if (SUCCEEDED(hr)) { #ifdef DEBUG DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szLastFolder, ARRAYSIZE(_szLastFolder)); #endif hr = _pnswcb ? _pnswcb->EnterFolder(psf, pidl) : S_OK; if (S_OK == hr) { // Update progress dialog; note we only update the progress dialog // with the folder names we're currently traversing. Updating on a // per filename basis just caused far too much flicker, looked bad. if (NSWF_SHOW_PROGRESS & _dwFlags) { WCHAR sz[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_NORMAL, sz, ARRAYSIZE(sz)))) _ProgressDialogUpdate(sz); hr = _ProgressDialogQueryCancel(); // ERROR_CANCELLED -> cancelled } if (SUCCEEDED(hr)) { hr = _WalkFolder(psfNew, NULL, cDepth); if (_pnswcb) _pnswcb->LeaveFolder(psf, pidl); // ignore result } } hr = SUCCEEDED(hr) ? S_OK : hr; // filter out S_FALSE success cases psfNew->Release(); } return hr; } BOOL CNamespaceWalk::_IsFolderTarget(IShellFolder *psf, LPCITEMIDLIST pidl) { BOOL bIsFolder = FALSE; LPITEMIDLIST pidlTarget; if (SUCCEEDED(_GetShortcutTarget(psf, pidl, &pidlTarget))) { bIsFolder = SHGetAttributes(NULL, pidlTarget, SFGAO_FOLDER); ILFree(pidlTarget); } return bIsFolder; } // NSWF_ONE_IMPLIES_ALL applies only when the "one" is not a folder // and if it is a shortcut if the target of the shortcut is a file BOOL CNamespaceWalk::_OneImpliesAll(IShellFolder *psf, LPCITEMIDLIST pidl) { BOOL bOneImpliesAll = FALSE; if (NSWF_ONE_IMPLIES_ALL & _dwFlags) { switch (_GetType(psf, pidl)) { case NSWALK_LINK: if (!_IsFolderTarget(psf, pidl)) { bOneImpliesAll = TRUE; // shortcut to non folder, one-implies-all applies } break; case NSWALK_ITEM: bOneImpliesAll = TRUE; // non folder break; } } return bOneImpliesAll; } // walk an IShellFolderView implementation. this is usually defview (only such impl now) // the depth beings at level 0 here HRESULT CNamespaceWalk::_WalkView(IFolderView *pfv) { IShellFolder2 *psf; HRESULT hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder2, &psf)); if (SUCCEEDED(hr)) { int uSelectedCount; hr = pfv->ItemCount(SVGIO_SELECTION, &uSelectedCount); if (SUCCEEDED(hr)) { // folders explictly selected in the view are level 0 // folders implictly selected are level 1 UINT cFolderStartDepth = 0; // assume all folders explictly selected IEnumIDList *penum; // prop the NSWF_ flags to the IFolderView SVGIO_ flags UINT uFlags = (NSWF_FLAG_VIEWORDER & _dwFlags) ? SVGIO_FLAG_VIEWORDER : 0; if (uSelectedCount > 1) { hr = pfv->Items(SVGIO_SELECTION | uFlags, IID_PPV_ARG(IEnumIDList, &penum)); } else if (uSelectedCount == 1) { hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IEnumIDList, &penum)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; ULONG c; if (S_OK == penum->Next(1, &pidl, &c)) { if (_OneImpliesAll(psf, pidl)) { // this implies pidl is not a folder so folders are implictly selected // consider them depth 1 cFolderStartDepth = 1; // one implies all -> release the "one" and grab "all" penum->Release(); hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IEnumIDList, &penum)); } else { // folder selected, keep this enumerator for below loop penum->Reset(); } ILFree(pidl); } } } else if (uSelectedCount == 0) { // folders implictly selected, consider them depth 1 cFolderStartDepth = 1; // get "all" or the selection. in the selection case we know that will be empty // given uSelectedCount == 0 uFlags |= ((NSWF_NONE_IMPLIES_ALL & _dwFlags) ? SVGIO_ALLVIEW : SVGIO_SELECTION); hr = pfv->Items(uFlags, IID_PPV_ARG(IEnumIDList, &penum)); } if (SUCCEEDED(hr)) { UINT cFolders = 0; LPITEMIDLIST pidl; ULONG c; while ((S_OK == hr) && (S_OK == penum->Next(1, &pidl, &c))) { switch (_GetType(psf, pidl)) { case NSWALK_FOLDER: cFolders++; break; case NSWALK_LINK: hr = _WalkShortcut(psf, pidl, 0, cFolderStartDepth); break; case NSWALK_ITEM: hr = _AddItem(psf, pidl); break; } ILFree(pidl); } if (cFolders) { penum->Reset(); ULONG c; while ((S_OK == hr) && (S_OK == penum->Next(1, &pidl, &c))) { if (NSWALK_FOLDER == _GetType(psf, pidl)) { hr = _WalkFolderItem(psf, pidl, cFolderStartDepth); } ILFree(pidl); } } penum->Release(); } } psf->Release(); } return hr; } HRESULT _GetHIDA(IDataObject *pdtobj, BOOL fIgnoreAutoPlay, STGMEDIUM *pmed, LPIDA *ppida) { HRESULT hr = E_FAIL; if (!fIgnoreAutoPlay) { IDLData_InitializeClipboardFormats(); // init our registerd formats *ppida = DataObj_GetHIDAEx(pdtobj, g_cfAutoPlayHIDA, pmed); hr = *ppida ? S_FALSE : E_FAIL; } if (FAILED(hr)) { *ppida = DataObj_GetHIDA(pdtobj, pmed); hr = *ppida ? S_OK : E_FAIL; } return hr; } HRESULT CNamespaceWalk::_WalkDataObject(IDataObject *pdtobj) { STGMEDIUM medium = {0}; LPIDA pida; HRESULT hr = _GetHIDA(pdtobj, NSWF_IGNORE_AUTOPLAY_HIDA & _dwFlags, &medium, &pida); if (SUCCEEDED(hr)) { // if we picked up the autoplay hida, then we dont want // to do a full traversal if (hr == S_FALSE) _cDepthMax = 0; IShellFolder *psfRoot; hr = SHBindToObjectEx(NULL, HIDA_GetPIDLFolder(pida), NULL, IID_PPV_ARG(IShellFolder, &psfRoot)); if (SUCCEEDED(hr)) { BOOL cFolders = 0; // pass 1, non folders and shortcuts for (UINT i = 0; (S_OK == hr) && (i < pida->cidl); i++) { IShellFolder *psf; LPCITEMIDLIST pidlLast; hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { if ((pida->cidl == 1) && _OneImpliesAll(psf, pidlLast)) { // when doing one implies all ignore the view order // flag as that should only apply to explictly selected items _dwFlags &= ~NSWF_FLAG_VIEWORDER; hr = _WalkFolder(psf, pidlLast, 0); } else { switch (_GetType(psf, pidlLast)) { case NSWALK_FOLDER: cFolders++; break; case NSWALK_LINK: hr = _WalkShortcut(psf, pidlLast, 0, 0); break; case NSWALK_ITEM: hr = _AddItem(psf, pidlLast); break; } } psf->Release(); } } if (cFolders) { // pass 2, recurse into folders for (UINT i = 0; (S_OK == hr) && (i < pida->cidl); i++) { IShellFolder *psf; LPCITEMIDLIST pidlLast; hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { if (NSWALK_FOLDER == _GetType(psf, pidlLast)) { if (ILIsEmpty(pidlLast)) { // in case of desktop folder we just walk the folder // because empty pidl is not its child, and there can // only be one desktop in the data object so always level 0 hr = _WalkFolder(psf, NULL, 0); } else { // all folders that are explictly selected are level 0 // in the walk. if the folder is in the data object then it is selected hr = _WalkFolderItem(psf, pidlLast, 0); } } psf->Release(); } } } psfRoot->Release(); } HIDA_ReleaseStgMedium(pida, &medium); } else { // we have to use CF_HDROP instead of HIDA because this // data object comes from AutoPlay and that only supports CF_HDROP FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; hr = pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hr)) { TCHAR szPath[MAX_PATH]; for (int i = 0; SUCCEEDED(hr) && DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++) { LPITEMIDLIST pidl; hr = SHParseDisplayName(szPath, NULL, &pidl, 0, NULL); if (SUCCEEDED(hr)) { // note, no filter being applied here! hr = _AppendFull(pidl); ILFree(pidl); } } ReleaseStgMedium(&medium); } } return hr; } HRESULT CNamespaceWalk::_WalkParentAndItem(IParentAndItem *ppai) { LPITEMIDLIST pidlChild; IShellFolder *psf; HRESULT hr = ppai->GetParentAndItem(NULL, &psf, &pidlChild); if (SUCCEEDED(hr)) { if (_OneImpliesAll(psf, pidlChild)) { // a non folder item, this is level 0 of walk hr = _WalkFolder(psf, pidlChild, 0); } else { // folder or non folder, this is level 0 of walk // and level 0 if the item is a folder hr = _WalkIDList(psf, pidlChild, 0, 0); } psf->Release(); ILFree(pidlChild); } return hr; } // punkToWalk can be a... // site that gives access to IFolderView (defview) // IShellFolder // IDataObject // IParentAndItem (CLSID_ShellItem usually) STDMETHODIMP CNamespaceWalk::Walk(IUnknown *punkToWalk, DWORD dwFlags, int cDepth, INamespaceWalkCB *pnswcb) { _dwFlags = dwFlags; _cDepthMax = cDepth; if (pnswcb) pnswcb->QueryInterface(IID_PPV_ARG(INamespaceWalkCB, &_pnswcb)); _ProgressDialogBegin(); IFolderView *pfv; HRESULT hr = IUnknown_QueryService(punkToWalk, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv)); if (SUCCEEDED(hr)) { hr = _WalkView(pfv); pfv->Release(); } else { IShellFolder *psf; hr = punkToWalk->QueryInterface(IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = _WalkFolder(psf, NULL, 0); psf->Release(); } else { IDataObject *pdtobj; hr = punkToWalk->QueryInterface(IID_PPV_ARG(IDataObject, &pdtobj)); if (SUCCEEDED(hr)) { hr = _WalkDataObject(pdtobj); pdtobj->Release(); } else { // IShellItem case, get to the things to walk via IParentAndItem IParentAndItem *ppai; hr = punkToWalk->QueryInterface(IID_PPV_ARG(IParentAndItem, &ppai)); if (SUCCEEDED(hr)) { hr = _WalkParentAndItem(ppai); ppai->Release(); } } } } _ProgressDialogEnd(); if (_pnswcb) _pnswcb->Release(); return hr; } // caller should use FreeIDListArray() (inline helper in the .h file) to free this array STDMETHODIMP CNamespaceWalk::GetIDArrayResult(UINT *pcItems, LPITEMIDLIST **pppidl) { HRESULT hr; *pppidl = NULL; *pcItems = (HDPA)_dpaItems ? _dpaItems.GetPtrCount() : 0; if (*pcItems) { ULONG cb = *pcItems * sizeof(*pppidl); *pppidl = (LPITEMIDLIST *)CoTaskMemAlloc(cb); if (*pppidl) { memcpy(*pppidl, _dpaItems.GetPtrPtr(), cb); // transfer ownership of pidls here _dpaItems.Destroy(); // don't free the pidls, just the array hr = S_OK; } else { hr = E_OUTOFMEMORY; *pcItems = 0; } } else { hr = S_FALSE; } return hr; } void CNamespaceWalk::_ProgressDialogBegin() { ASSERT(!_papd); // Why are we initializing more than once??? ASSERT(!_pap); // Why are we initializing more than once??? if (_dwFlags & NSWF_SHOW_PROGRESS) { if (!_papd) { HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActionProgressDialog, &_papd)); if (SUCCEEDED(hr)) { LPWSTR pszTitle = NULL, pszCancel = NULL; // Retrieve dialog text from callback. hr = _pnswcb ? _pnswcb->InitializeProgressDialog(&pszTitle, &pszCancel) : S_OK; if (SUCCEEDED(hr)) { hr = _papd->Initialize(SPINITF_MODAL, pszTitle, pszCancel); if (SUCCEEDED(hr)) { hr = _papd->QueryInterface(IID_PPV_ARG(IActionProgress, &_pap)); if (SUCCEEDED(hr)) { hr = _pap->Begin(SPACTION_SEARCHING_FILES, SPBEGINF_MARQUEEPROGRESS); if (FAILED(hr)) { ATOMICRELEASE(_pap); // Cleanup if necessary. } } } } CoTaskMemFree(pszTitle); CoTaskMemFree(pszCancel); // Cleanup if necessary. if (FAILED(hr)) { ATOMICRELEASE(_papd); } } } } } void CNamespaceWalk::_ProgressDialogUpdate(LPCWSTR pszText) { if (_pap) _pap->UpdateText(SPTEXT_ACTIONDETAIL, pszText, TRUE); } // Note: // Returns S_OK if we should continue our walk. // Returns ERROR_CANCELLED if we should abort our walk due to user "Cancel". // HRESULT CNamespaceWalk::_ProgressDialogQueryCancel() { HRESULT hr = S_OK; // assume we keep going // Check progress dialog to see if user cancelled walk. if (_pap) { BOOL bCancelled; hr = _pap->QueryCancel(&bCancelled); if (SUCCEEDED(hr) && bCancelled) hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } return hr; } void CNamespaceWalk::_ProgressDialogEnd() { if (_pap) { _pap->End(); ATOMICRELEASE(_pap); } if (_papd) { _papd->Stop(); ATOMICRELEASE(_papd); } }