Leaked source code of windows server 2003
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.

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