Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

914 lines
28 KiB

  1. #include "shellprv.h"
  2. #include "dpa.h"
  3. #include "datautil.h"
  4. typedef enum
  5. {
  6. NSWALK_DONTWALK,
  7. NSWALK_FOLDER,
  8. NSWALK_ITEM,
  9. NSWALK_LINK
  10. } NSWALK_ELEMENT_TYPE;
  11. class CNamespaceWalk : public INamespaceWalk
  12. {
  13. public:
  14. CNamespaceWalk();
  15. // IUnknown
  16. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  17. STDMETHODIMP_(ULONG) AddRef();
  18. STDMETHODIMP_(ULONG) Release();
  19. // INamespaceWalk
  20. STDMETHODIMP Walk(IUnknown *punkToWalk, DWORD dwFlags, int cDepth, INamespaceWalkCB *pnswcb);
  21. STDMETHODIMP GetIDArrayResult(UINT *pcItems, LPITEMIDLIST **pppidl);
  22. private:
  23. ~CNamespaceWalk();
  24. static int CALLBACK _FreeItems(LPITEMIDLIST pidl, IShellFolder *psf);
  25. static int CALLBACK _CompareItems(LPITEMIDLIST p1, LPITEMIDLIST p2, IShellFolder *psf);
  26. HRESULT _EnsureDPA();
  27. HRESULT _AddItem(IShellFolder *psf, LPCITEMIDLIST pidl);
  28. HRESULT _AppendFull(LPCITEMIDLIST pidlFull);
  29. HRESULT _EnumFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, CDPA<UNALIGNED ITEMIDLIST> *pdpaItems);
  30. HRESULT _GetShortcutTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget);
  31. BOOL _IsFolderTarget(IShellFolder *psf, LPCITEMIDLIST pidl);
  32. HRESULT _WalkView(IFolderView *pfv);
  33. HRESULT _WalkFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, int cDepth);
  34. HRESULT _WalkDataObject(IDataObject *pdtobj);
  35. HRESULT _WalkParentAndItem(IParentAndItem *ppai);
  36. HRESULT _WalkIDList(IShellFolder *psfRoot, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta);
  37. HRESULT _WalkFolderItem(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth);
  38. HRESULT _WalkShortcut(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta);
  39. NSWALK_ELEMENT_TYPE _GetType(IShellFolder *psf, LPCITEMIDLIST pidl);
  40. BOOL _OneImpliesAll(IShellFolder *psf, LPCITEMIDLIST pidl);
  41. HRESULT _ProgressDialogQueryCancel();
  42. void _ProgressDialogBegin();
  43. void _ProgressDialogUpdate(LPCWSTR pszText);
  44. void _ProgressDialogEnd();
  45. LONG _cRef;
  46. DWORD _dwFlags;
  47. int _cDepthMax;
  48. INamespaceWalkCB *_pnswcb;
  49. IActionProgressDialog *_papd;
  50. IActionProgress *_pap;
  51. #ifdef DEBUG
  52. TCHAR _szLastFolder[MAX_PATH]; // to track what we failed on
  53. #endif
  54. CDPA<UNALIGNED ITEMIDLIST> _dpaItems;
  55. };
  56. STDAPI CNamespaceWalk_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  57. {
  58. CNamespaceWalk *pnsw = new CNamespaceWalk();
  59. if (!pnsw)
  60. return E_OUTOFMEMORY;
  61. HRESULT hr = pnsw->QueryInterface(riid, ppv);
  62. pnsw->Release();
  63. return hr;
  64. }
  65. CNamespaceWalk::CNamespaceWalk() : _cRef(1)
  66. {
  67. _papd = NULL;
  68. _pap = NULL;
  69. }
  70. CNamespaceWalk::~CNamespaceWalk()
  71. {
  72. ASSERT(!_papd);
  73. ASSERT(!_pap);
  74. if ((HDPA)_dpaItems)
  75. _dpaItems.DestroyCallbackEx(_FreeItems, (IShellFolder *)NULL);
  76. }
  77. STDMETHODIMP_(ULONG) CNamespaceWalk::AddRef()
  78. {
  79. return InterlockedIncrement(&_cRef);
  80. }
  81. STDMETHODIMP_(ULONG) CNamespaceWalk::Release()
  82. {
  83. if (InterlockedDecrement(&_cRef))
  84. return _cRef;
  85. delete this;
  86. return 0;
  87. }
  88. HRESULT CNamespaceWalk::QueryInterface(REFIID riid, void **ppv)
  89. {
  90. static const QITAB qit[] =
  91. {
  92. QITABENT(CNamespaceWalk, INamespaceWalk),
  93. { 0 },
  94. };
  95. return QISearch(this, qit, riid, ppv);
  96. }
  97. int CALLBACK CNamespaceWalk::_FreeItems(LPITEMIDLIST pidl, IShellFolder *psf)
  98. {
  99. ILFree(pidl);
  100. return 1;
  101. }
  102. HRESULT CNamespaceWalk::_EnsureDPA()
  103. {
  104. return (HDPA)_dpaItems ? S_OK : (_dpaItems.Create(10) ? S_OK : E_OUTOFMEMORY);
  105. }
  106. // consumes pidl in all cases (success and failure)
  107. HRESULT CNamespaceWalk::_AppendFull(LPCITEMIDLIST pidlFull)
  108. {
  109. HRESULT hr = _ProgressDialogQueryCancel(); // ERROR_CANCELLED -> cancelled
  110. if (SUCCEEDED(hr))
  111. {
  112. if (NSWF_DONT_ACCUMULATE_RESULT & _dwFlags)
  113. {
  114. hr = S_OK;
  115. }
  116. else
  117. {
  118. hr = _EnsureDPA();
  119. if (SUCCEEDED(hr))
  120. {
  121. LPITEMIDLIST pidlClone;
  122. hr = SHILClone(pidlFull, &pidlClone);
  123. if (SUCCEEDED(hr) && (-1 == _dpaItems.AppendPtr(pidlClone)))
  124. {
  125. hr = E_OUTOFMEMORY;
  126. ILFree(pidlClone);
  127. }
  128. }
  129. }
  130. }
  131. return hr;
  132. }
  133. HRESULT CNamespaceWalk::_AddItem(IShellFolder *psf, LPCITEMIDLIST pidl)
  134. {
  135. HRESULT hr = S_OK;
  136. LPITEMIDLIST pidlFull = NULL;
  137. if (!(NSWF_DONT_ACCUMULATE_RESULT & _dwFlags))
  138. {
  139. hr = SUCCEEDED(SHFullIDListFromFolderAndItem(psf, pidl, &pidlFull)) ? S_OK : S_FALSE;//couldn't get the pidl? Just skip the item
  140. }
  141. if (S_OK == hr)
  142. {
  143. hr = _pnswcb ? _pnswcb->FoundItem(psf, pidl) : S_OK;
  144. if (S_OK == hr)
  145. {
  146. hr = _AppendFull(pidlFull);
  147. }
  148. ILFree(pidlFull);
  149. }
  150. return SUCCEEDED(hr) ? S_OK : hr; // filter out S_FALSE success cases
  151. }
  152. int CALLBACK CNamespaceWalk::_CompareItems(LPITEMIDLIST p1, LPITEMIDLIST p2, IShellFolder *psf)
  153. {
  154. HRESULT hr = psf->CompareIDs(0, p1, p2);
  155. return (short)HRESULT_CODE(hr);
  156. }
  157. HRESULT CNamespaceWalk::_EnumFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, CDPA<UNALIGNED ITEMIDLIST> *pdpaItems)
  158. {
  159. CDPA<UNALIGNED ITEMIDLIST> dpaItems;
  160. HRESULT hr = dpaItems.Create(16) ? S_OK : E_OUTOFMEMORY;
  161. if (SUCCEEDED(hr))
  162. {
  163. IEnumIDList *penum;
  164. if (S_OK == psf->EnumObjects(NULL, SHCONTF_NONFOLDERS | SHCONTF_FOLDERS, &penum))
  165. {
  166. LPITEMIDLIST pidl;
  167. ULONG c;
  168. while (SUCCEEDED(hr) && (S_OK == penum->Next(1, &pidl, &c)))
  169. {
  170. if (-1 == dpaItems.AppendPtr(pidl))
  171. {
  172. ILFree(pidl);
  173. hr = E_OUTOFMEMORY;
  174. }
  175. else
  176. {
  177. hr = _ProgressDialogQueryCancel();
  178. }
  179. }
  180. penum->Release();
  181. }
  182. if (SUCCEEDED(hr))
  183. {
  184. dpaItems.SortEx(_CompareItems, psf);
  185. if (pidlFirst && !(NSWF_FLAG_VIEWORDER & _dwFlags))
  186. {
  187. // rotate the items array so pidlFirst is first in the list
  188. // cast for bogus SearchEx decl
  189. int iMid = dpaItems.SearchEx((LPITEMIDLIST)pidlFirst, 0, _CompareItems, psf, DPAS_SORTED);
  190. if (-1 != iMid)
  191. {
  192. int cItems = dpaItems.GetPtrCount();
  193. CDPA<UNALIGNED ITEMIDLIST> dpaTemp;
  194. if (dpaTemp.Create(cItems))
  195. {
  196. for (int i = 0; i < cItems; i++)
  197. {
  198. dpaTemp.SetPtr(i, dpaItems.GetPtr(iMid++));
  199. if (iMid >= cItems)
  200. iMid = 0;
  201. }
  202. for (int i = 0; i < cItems; i++)
  203. {
  204. dpaItems.SetPtr(i, dpaTemp.GetPtr(i));
  205. }
  206. dpaTemp.Destroy(); // don't free the pidls, just the array
  207. }
  208. }
  209. else
  210. {
  211. // pidlFirst not found in the enum, it might be hidden or filters
  212. // out some way, but make sure this always ends up in the dpa in this case
  213. LPITEMIDLIST pidlClone = ILClone(pidlFirst);
  214. if (pidlClone)
  215. {
  216. dpaItems.InsertPtr(0, pidlClone);
  217. }
  218. }
  219. }
  220. }
  221. }
  222. if (FAILED(hr))
  223. {
  224. dpaItems.DestroyCallbackEx(_FreeItems, psf);
  225. dpaItems = NULL;
  226. }
  227. *pdpaItems = dpaItems;
  228. return hr;
  229. }
  230. NSWALK_ELEMENT_TYPE CNamespaceWalk::_GetType(IShellFolder *psf, LPCITEMIDLIST pidl)
  231. {
  232. NSWALK_ELEMENT_TYPE nwet = NSWALK_DONTWALK;
  233. DWORD dwAttribs = SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM | SFGAO_LINK);
  234. if ((dwAttribs & SFGAO_FOLDER) && (!(dwAttribs & SFGAO_STREAM) || (NSWF_TRAVERSE_STREAM_JUNCTIONS & _dwFlags)))
  235. {
  236. nwet = NSWALK_FOLDER;
  237. }
  238. else if ((dwAttribs & SFGAO_LINK) && !(NSWF_DONT_TRAVERSE_LINKS & _dwFlags))
  239. {
  240. nwet = NSWALK_LINK;
  241. }
  242. else if ((dwAttribs & SFGAO_FILESYSTEM) || !(NSWF_FILESYSTEM_ONLY & _dwFlags))
  243. {
  244. nwet = NSWALK_ITEM;
  245. }
  246. return nwet;
  247. }
  248. HRESULT CNamespaceWalk::_WalkIDList(IShellFolder *psfRoot, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta)
  249. {
  250. IShellFolder *psf;
  251. LPCITEMIDLIST pidlLast;
  252. HRESULT hr = SHBindToFolderIDListParent(psfRoot, pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  253. if (SUCCEEDED(hr))
  254. {
  255. switch (_GetType(psf, pidlLast))
  256. {
  257. case NSWALK_FOLDER:
  258. hr = _WalkFolderItem(psf, pidlLast, cDepth + cFolderDepthDelta);
  259. break;
  260. case NSWALK_LINK:
  261. hr = _WalkShortcut(psf, pidlLast, cDepth, cFolderDepthDelta);
  262. break;
  263. case NSWALK_ITEM:
  264. hr = _AddItem(psf, pidlLast);
  265. break;
  266. }
  267. psf->Release();
  268. }
  269. return hr;
  270. }
  271. HRESULT CNamespaceWalk::_WalkShortcut(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth, int cFolderDepthDelta)
  272. {
  273. HRESULT hr = S_OK;
  274. // If an error occured trying to resolve a shortcut then we simply skip
  275. // this shortcut and continue
  276. LPITEMIDLIST pidlTarget;
  277. if (SUCCEEDED(_GetShortcutTarget(psf, pidl, &pidlTarget)))
  278. {
  279. hr = _WalkIDList(NULL, pidlTarget, cDepth, cFolderDepthDelta);
  280. ILFree(pidlTarget);
  281. }
  282. return hr;
  283. }
  284. HRESULT CNamespaceWalk::_GetShortcutTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget)
  285. {
  286. *ppidlTarget = NULL;
  287. IShellLink *psl;
  288. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_PPV_ARG_NULL(IShellLink, &psl))))
  289. {
  290. if (S_OK == psl->Resolve(NULL, SLR_UPDATE | SLR_NO_UI))
  291. {
  292. psl->GetIDList(ppidlTarget);
  293. }
  294. psl->Release();
  295. }
  296. return *ppidlTarget ? S_OK : E_FAIL;
  297. }
  298. HRESULT CNamespaceWalk::_WalkFolder(IShellFolder *psf, LPCITEMIDLIST pidlFirst, int cDepth)
  299. {
  300. if (cDepth > _cDepthMax)
  301. return S_OK; // done
  302. CDPA<UNALIGNED ITEMIDLIST> dpaItems;
  303. HRESULT hr = _EnumFolder(psf, pidlFirst, &dpaItems);
  304. if (SUCCEEDED(hr))
  305. {
  306. UINT cFolders = 0;
  307. // breadth first traversal, so do the items (non folders) first
  308. // (this includes shortcuts and those can point to folders)
  309. for (int i = 0; (S_OK == hr) && (i < dpaItems.GetPtrCount()); i++)
  310. {
  311. switch (_GetType(psf, dpaItems.GetPtr(i)))
  312. {
  313. case NSWALK_FOLDER:
  314. cFolders++;
  315. break;
  316. case NSWALK_LINK:
  317. hr = _WalkShortcut(psf, dpaItems.GetPtr(i), cDepth, 1);
  318. break;
  319. case NSWALK_ITEM:
  320. hr = _AddItem(psf, dpaItems.GetPtr(i));
  321. break;
  322. }
  323. }
  324. // no go deep into the folders
  325. if (cFolders)
  326. {
  327. for (int i = 0; (S_OK == hr) && (i < dpaItems.GetPtrCount()); i++)
  328. {
  329. if (NSWALK_FOLDER == _GetType(psf, dpaItems.GetPtr(i)))
  330. {
  331. hr = _WalkFolderItem(psf, dpaItems.GetPtr(i), cDepth + 1);
  332. }
  333. }
  334. }
  335. dpaItems.DestroyCallbackEx(_FreeItems, psf);
  336. }
  337. return hr;
  338. }
  339. HRESULT CNamespaceWalk::_WalkFolderItem(IShellFolder *psf, LPCITEMIDLIST pidl, int cDepth)
  340. {
  341. IShellFolder *psfNew;
  342. HRESULT hr = psf->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfNew));
  343. if (SUCCEEDED(hr))
  344. {
  345. #ifdef DEBUG
  346. DisplayNameOf(psf, pidl, SHGDN_FORPARSING, _szLastFolder, ARRAYSIZE(_szLastFolder));
  347. #endif
  348. hr = _pnswcb ? _pnswcb->EnterFolder(psf, pidl) : S_OK;
  349. if (S_OK == hr)
  350. {
  351. // Update progress dialog; note we only update the progress dialog
  352. // with the folder names we're currently traversing. Updating on a
  353. // per filename basis just caused far too much flicker, looked bad.
  354. if (NSWF_SHOW_PROGRESS & _dwFlags)
  355. {
  356. WCHAR sz[MAX_PATH];
  357. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_NORMAL, sz, ARRAYSIZE(sz))))
  358. _ProgressDialogUpdate(sz);
  359. hr = _ProgressDialogQueryCancel(); // ERROR_CANCELLED -> cancelled
  360. }
  361. if (SUCCEEDED(hr))
  362. {
  363. hr = _WalkFolder(psfNew, NULL, cDepth);
  364. if (_pnswcb)
  365. _pnswcb->LeaveFolder(psf, pidl); // ignore result
  366. }
  367. }
  368. hr = SUCCEEDED(hr) ? S_OK : hr; // filter out S_FALSE success cases
  369. psfNew->Release();
  370. }
  371. return hr;
  372. }
  373. BOOL CNamespaceWalk::_IsFolderTarget(IShellFolder *psf, LPCITEMIDLIST pidl)
  374. {
  375. BOOL bIsFolder = FALSE;
  376. LPITEMIDLIST pidlTarget;
  377. if (SUCCEEDED(_GetShortcutTarget(psf, pidl, &pidlTarget)))
  378. {
  379. bIsFolder = SHGetAttributes(NULL, pidlTarget, SFGAO_FOLDER);
  380. ILFree(pidlTarget);
  381. }
  382. return bIsFolder;
  383. }
  384. // NSWF_ONE_IMPLIES_ALL applies only when the "one" is not a folder
  385. // and if it is a shortcut if the target of the shortcut is a file
  386. BOOL CNamespaceWalk::_OneImpliesAll(IShellFolder *psf, LPCITEMIDLIST pidl)
  387. {
  388. BOOL bOneImpliesAll = FALSE;
  389. if (NSWF_ONE_IMPLIES_ALL & _dwFlags)
  390. {
  391. switch (_GetType(psf, pidl))
  392. {
  393. case NSWALK_LINK:
  394. if (!_IsFolderTarget(psf, pidl))
  395. {
  396. bOneImpliesAll = TRUE; // shortcut to non folder, one-implies-all applies
  397. }
  398. break;
  399. case NSWALK_ITEM:
  400. bOneImpliesAll = TRUE; // non folder
  401. break;
  402. }
  403. }
  404. return bOneImpliesAll;
  405. }
  406. // walk an IShellFolderView implementation. this is usually defview (only such impl now)
  407. // the depth beings at level 0 here
  408. HRESULT CNamespaceWalk::_WalkView(IFolderView *pfv)
  409. {
  410. IShellFolder2 *psf;
  411. HRESULT hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder2, &psf));
  412. if (SUCCEEDED(hr))
  413. {
  414. int uSelectedCount;
  415. hr = pfv->ItemCount(SVGIO_SELECTION, &uSelectedCount);
  416. if (SUCCEEDED(hr))
  417. {
  418. // folders explictly selected in the view are level 0
  419. // folders implictly selected are level 1
  420. UINT cFolderStartDepth = 0; // assume all folders explictly selected
  421. IEnumIDList *penum;
  422. // prop the NSWF_ flags to the IFolderView SVGIO_ flags
  423. UINT uFlags = (NSWF_FLAG_VIEWORDER & _dwFlags) ? SVGIO_FLAG_VIEWORDER : 0;
  424. if (uSelectedCount > 1)
  425. {
  426. hr = pfv->Items(SVGIO_SELECTION | uFlags, IID_PPV_ARG(IEnumIDList, &penum));
  427. }
  428. else if (uSelectedCount == 1)
  429. {
  430. hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IEnumIDList, &penum));
  431. if (SUCCEEDED(hr))
  432. {
  433. LPITEMIDLIST pidl;
  434. ULONG c;
  435. if (S_OK == penum->Next(1, &pidl, &c))
  436. {
  437. if (_OneImpliesAll(psf, pidl))
  438. {
  439. // this implies pidl is not a folder so folders are implictly selected
  440. // consider them depth 1
  441. cFolderStartDepth = 1;
  442. // one implies all -> release the "one" and grab "all"
  443. penum->Release();
  444. hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IEnumIDList, &penum));
  445. }
  446. else
  447. {
  448. // folder selected, keep this enumerator for below loop
  449. penum->Reset();
  450. }
  451. ILFree(pidl);
  452. }
  453. }
  454. }
  455. else if (uSelectedCount == 0)
  456. {
  457. // folders implictly selected, consider them depth 1
  458. cFolderStartDepth = 1;
  459. // get "all" or the selection. in the selection case we know that will be empty
  460. // given uSelectedCount == 0
  461. uFlags |= ((NSWF_NONE_IMPLIES_ALL & _dwFlags) ? SVGIO_ALLVIEW : SVGIO_SELECTION);
  462. hr = pfv->Items(uFlags, IID_PPV_ARG(IEnumIDList, &penum));
  463. }
  464. if (SUCCEEDED(hr))
  465. {
  466. UINT cFolders = 0;
  467. LPITEMIDLIST pidl;
  468. ULONG c;
  469. while ((S_OK == hr) && (S_OK == penum->Next(1, &pidl, &c)))
  470. {
  471. switch (_GetType(psf, pidl))
  472. {
  473. case NSWALK_FOLDER:
  474. cFolders++;
  475. break;
  476. case NSWALK_LINK:
  477. hr = _WalkShortcut(psf, pidl, 0, cFolderStartDepth);
  478. break;
  479. case NSWALK_ITEM:
  480. hr = _AddItem(psf, pidl);
  481. break;
  482. }
  483. ILFree(pidl);
  484. }
  485. if (cFolders)
  486. {
  487. penum->Reset();
  488. ULONG c;
  489. while ((S_OK == hr) && (S_OK == penum->Next(1, &pidl, &c)))
  490. {
  491. if (NSWALK_FOLDER == _GetType(psf, pidl))
  492. {
  493. hr = _WalkFolderItem(psf, pidl, cFolderStartDepth);
  494. }
  495. ILFree(pidl);
  496. }
  497. }
  498. penum->Release();
  499. }
  500. }
  501. psf->Release();
  502. }
  503. return hr;
  504. }
  505. HRESULT _GetHIDA(IDataObject *pdtobj, BOOL fIgnoreAutoPlay, STGMEDIUM *pmed, LPIDA *ppida)
  506. {
  507. HRESULT hr = E_FAIL;
  508. if (!fIgnoreAutoPlay)
  509. {
  510. IDLData_InitializeClipboardFormats(); // init our registerd formats
  511. *ppida = DataObj_GetHIDAEx(pdtobj, g_cfAutoPlayHIDA, pmed);
  512. hr = *ppida ? S_FALSE : E_FAIL;
  513. }
  514. if (FAILED(hr))
  515. {
  516. *ppida = DataObj_GetHIDA(pdtobj, pmed);
  517. hr = *ppida ? S_OK : E_FAIL;
  518. }
  519. return hr;
  520. }
  521. HRESULT CNamespaceWalk::_WalkDataObject(IDataObject *pdtobj)
  522. {
  523. STGMEDIUM medium = {0};
  524. LPIDA pida;
  525. HRESULT hr = _GetHIDA(pdtobj, NSWF_IGNORE_AUTOPLAY_HIDA & _dwFlags, &medium, &pida);
  526. if (SUCCEEDED(hr))
  527. {
  528. // if we picked up the autoplay hida, then we dont want
  529. // to do a full traversal
  530. if (hr == S_FALSE)
  531. _cDepthMax = 0;
  532. IShellFolder *psfRoot;
  533. hr = SHBindToObjectEx(NULL, HIDA_GetPIDLFolder(pida), NULL, IID_PPV_ARG(IShellFolder, &psfRoot));
  534. if (SUCCEEDED(hr))
  535. {
  536. BOOL cFolders = 0;
  537. // pass 1, non folders and shortcuts
  538. for (UINT i = 0; (S_OK == hr) && (i < pida->cidl); i++)
  539. {
  540. IShellFolder *psf;
  541. LPCITEMIDLIST pidlLast;
  542. hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  543. if (SUCCEEDED(hr))
  544. {
  545. if ((pida->cidl == 1) && _OneImpliesAll(psf, pidlLast))
  546. {
  547. // when doing one implies all ignore the view order
  548. // flag as that should only apply to explictly selected items
  549. _dwFlags &= ~NSWF_FLAG_VIEWORDER;
  550. hr = _WalkFolder(psf, pidlLast, 0);
  551. }
  552. else
  553. {
  554. switch (_GetType(psf, pidlLast))
  555. {
  556. case NSWALK_FOLDER:
  557. cFolders++;
  558. break;
  559. case NSWALK_LINK:
  560. hr = _WalkShortcut(psf, pidlLast, 0, 0);
  561. break;
  562. case NSWALK_ITEM:
  563. hr = _AddItem(psf, pidlLast);
  564. break;
  565. }
  566. }
  567. psf->Release();
  568. }
  569. }
  570. if (cFolders)
  571. {
  572. // pass 2, recurse into folders
  573. for (UINT i = 0; (S_OK == hr) && (i < pida->cidl); i++)
  574. {
  575. IShellFolder *psf;
  576. LPCITEMIDLIST pidlLast;
  577. hr = SHBindToFolderIDListParent(psfRoot, IDA_GetIDListPtr(pida, i), IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  578. if (SUCCEEDED(hr))
  579. {
  580. if (NSWALK_FOLDER == _GetType(psf, pidlLast))
  581. {
  582. if (ILIsEmpty(pidlLast))
  583. {
  584. // in case of desktop folder we just walk the folder
  585. // because empty pidl is not its child, and there can
  586. // only be one desktop in the data object so always level 0
  587. hr = _WalkFolder(psf, NULL, 0);
  588. }
  589. else
  590. {
  591. // all folders that are explictly selected are level 0
  592. // in the walk. if the folder is in the data object then it is selected
  593. hr = _WalkFolderItem(psf, pidlLast, 0);
  594. }
  595. }
  596. psf->Release();
  597. }
  598. }
  599. }
  600. psfRoot->Release();
  601. }
  602. HIDA_ReleaseStgMedium(pida, &medium);
  603. }
  604. else
  605. {
  606. // we have to use CF_HDROP instead of HIDA because this
  607. // data object comes from AutoPlay and that only supports CF_HDROP
  608. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  609. hr = pdtobj->GetData(&fmte, &medium);
  610. if (SUCCEEDED(hr))
  611. {
  612. TCHAR szPath[MAX_PATH];
  613. for (int i = 0; SUCCEEDED(hr) && DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
  614. {
  615. LPITEMIDLIST pidl;
  616. hr = SHParseDisplayName(szPath, NULL, &pidl, 0, NULL);
  617. if (SUCCEEDED(hr))
  618. {
  619. // note, no filter being applied here!
  620. hr = _AppendFull(pidl);
  621. ILFree(pidl);
  622. }
  623. }
  624. ReleaseStgMedium(&medium);
  625. }
  626. }
  627. return hr;
  628. }
  629. HRESULT CNamespaceWalk::_WalkParentAndItem(IParentAndItem *ppai)
  630. {
  631. LPITEMIDLIST pidlChild;
  632. IShellFolder *psf;
  633. HRESULT hr = ppai->GetParentAndItem(NULL, &psf, &pidlChild);
  634. if (SUCCEEDED(hr))
  635. {
  636. if (_OneImpliesAll(psf, pidlChild))
  637. {
  638. // a non folder item, this is level 0 of walk
  639. hr = _WalkFolder(psf, pidlChild, 0);
  640. }
  641. else
  642. {
  643. // folder or non folder, this is level 0 of walk
  644. // and level 0 if the item is a folder
  645. hr = _WalkIDList(psf, pidlChild, 0, 0);
  646. }
  647. psf->Release();
  648. ILFree(pidlChild);
  649. }
  650. return hr;
  651. }
  652. // punkToWalk can be a...
  653. // site that gives access to IFolderView (defview)
  654. // IShellFolder
  655. // IDataObject
  656. // IParentAndItem (CLSID_ShellItem usually)
  657. STDMETHODIMP CNamespaceWalk::Walk(IUnknown *punkToWalk, DWORD dwFlags, int cDepth, INamespaceWalkCB *pnswcb)
  658. {
  659. _dwFlags = dwFlags;
  660. _cDepthMax = cDepth;
  661. if (pnswcb)
  662. pnswcb->QueryInterface(IID_PPV_ARG(INamespaceWalkCB, &_pnswcb));
  663. _ProgressDialogBegin();
  664. IFolderView *pfv;
  665. HRESULT hr = IUnknown_QueryService(punkToWalk, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  666. if (SUCCEEDED(hr))
  667. {
  668. hr = _WalkView(pfv);
  669. pfv->Release();
  670. }
  671. else
  672. {
  673. IShellFolder *psf;
  674. hr = punkToWalk->QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  675. if (SUCCEEDED(hr))
  676. {
  677. hr = _WalkFolder(psf, NULL, 0);
  678. psf->Release();
  679. }
  680. else
  681. {
  682. IDataObject *pdtobj;
  683. hr = punkToWalk->QueryInterface(IID_PPV_ARG(IDataObject, &pdtobj));
  684. if (SUCCEEDED(hr))
  685. {
  686. hr = _WalkDataObject(pdtobj);
  687. pdtobj->Release();
  688. }
  689. else
  690. {
  691. // IShellItem case, get to the things to walk via IParentAndItem
  692. IParentAndItem *ppai;
  693. hr = punkToWalk->QueryInterface(IID_PPV_ARG(IParentAndItem, &ppai));
  694. if (SUCCEEDED(hr))
  695. {
  696. hr = _WalkParentAndItem(ppai);
  697. ppai->Release();
  698. }
  699. }
  700. }
  701. }
  702. _ProgressDialogEnd();
  703. if (_pnswcb)
  704. _pnswcb->Release();
  705. return hr;
  706. }
  707. // caller should use FreeIDListArray() (inline helper in the .h file) to free this array
  708. STDMETHODIMP CNamespaceWalk::GetIDArrayResult(UINT *pcItems, LPITEMIDLIST **pppidl)
  709. {
  710. HRESULT hr;
  711. *pppidl = NULL;
  712. *pcItems = (HDPA)_dpaItems ? _dpaItems.GetPtrCount() : 0;
  713. if (*pcItems)
  714. {
  715. ULONG cb = *pcItems * sizeof(*pppidl);
  716. *pppidl = (LPITEMIDLIST *)CoTaskMemAlloc(cb);
  717. if (*pppidl)
  718. {
  719. memcpy(*pppidl, _dpaItems.GetPtrPtr(), cb); // transfer ownership of pidls here
  720. _dpaItems.Destroy(); // don't free the pidls, just the array
  721. hr = S_OK;
  722. }
  723. else
  724. {
  725. hr = E_OUTOFMEMORY;
  726. *pcItems = 0;
  727. }
  728. }
  729. else
  730. {
  731. hr = S_FALSE;
  732. }
  733. return hr;
  734. }
  735. void CNamespaceWalk::_ProgressDialogBegin()
  736. {
  737. ASSERT(!_papd); // Why are we initializing more than once???
  738. ASSERT(!_pap); // Why are we initializing more than once???
  739. if (_dwFlags & NSWF_SHOW_PROGRESS)
  740. {
  741. if (!_papd)
  742. {
  743. HRESULT hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActionProgressDialog, &_papd));
  744. if (SUCCEEDED(hr))
  745. {
  746. LPWSTR pszTitle = NULL, pszCancel = NULL;
  747. // Retrieve dialog text from callback.
  748. hr = _pnswcb ? _pnswcb->InitializeProgressDialog(&pszTitle, &pszCancel) : S_OK;
  749. if (SUCCEEDED(hr))
  750. {
  751. hr = _papd->Initialize(SPINITF_MODAL, pszTitle, pszCancel);
  752. if (SUCCEEDED(hr))
  753. {
  754. hr = _papd->QueryInterface(IID_PPV_ARG(IActionProgress, &_pap));
  755. if (SUCCEEDED(hr))
  756. {
  757. hr = _pap->Begin(SPACTION_SEARCHING_FILES, SPBEGINF_MARQUEEPROGRESS);
  758. if (FAILED(hr))
  759. {
  760. ATOMICRELEASE(_pap); // Cleanup if necessary.
  761. }
  762. }
  763. }
  764. }
  765. CoTaskMemFree(pszTitle);
  766. CoTaskMemFree(pszCancel);
  767. // Cleanup if necessary.
  768. if (FAILED(hr))
  769. {
  770. ATOMICRELEASE(_papd);
  771. }
  772. }
  773. }
  774. }
  775. }
  776. void CNamespaceWalk::_ProgressDialogUpdate(LPCWSTR pszText)
  777. {
  778. if (_pap)
  779. _pap->UpdateText(SPTEXT_ACTIONDETAIL, pszText, TRUE);
  780. }
  781. // Note:
  782. // Returns S_OK if we should continue our walk.
  783. // Returns ERROR_CANCELLED if we should abort our walk due to user "Cancel".
  784. //
  785. HRESULT CNamespaceWalk::_ProgressDialogQueryCancel()
  786. {
  787. HRESULT hr = S_OK; // assume we keep going
  788. // Check progress dialog to see if user cancelled walk.
  789. if (_pap)
  790. {
  791. BOOL bCancelled;
  792. hr = _pap->QueryCancel(&bCancelled);
  793. if (SUCCEEDED(hr) && bCancelled)
  794. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  795. }
  796. return hr;
  797. }
  798. void CNamespaceWalk::_ProgressDialogEnd()
  799. {
  800. if (_pap)
  801. {
  802. _pap->End();
  803. ATOMICRELEASE(_pap);
  804. }
  805. if (_papd)
  806. {
  807. _papd->Stop();
  808. ATOMICRELEASE(_papd);
  809. }
  810. }