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

915 lines
28 KiB
C++

#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<UNALIGNED ITEMIDLIST> *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<UNALIGNED ITEMIDLIST> _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<UNALIGNED ITEMIDLIST> *pdpaItems)
{
CDPA<UNALIGNED ITEMIDLIST> 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<UNALIGNED ITEMIDLIST> 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<UNALIGNED ITEMIDLIST> 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);
}
}