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.

343 lines
9.4 KiB

  1. #include <shellprv.h>
  2. #include "enumidlist.h"
  3. CEnumIDListBase::CEnumIDListBase() : _cRef(1)
  4. {
  5. }
  6. CEnumIDListBase::~CEnumIDListBase()
  7. {
  8. }
  9. STDMETHODIMP CEnumIDListBase::QueryInterface(REFIID riid, void **ppv)
  10. {
  11. static const QITAB qit[] = {
  12. QITABENT(CEnumIDListBase, IEnumIDList), // IID_IEnumIDList
  13. QITABENT(CEnumIDListBase, IObjectWithSite), // IID_IObjectWithSite
  14. { 0 },
  15. };
  16. return QISearch(this, qit, riid, ppv);
  17. }
  18. STDMETHODIMP_(ULONG) CEnumIDListBase::AddRef()
  19. {
  20. return InterlockedIncrement(&_cRef);
  21. }
  22. STDMETHODIMP_(ULONG) CEnumIDListBase::Release()
  23. {
  24. if (InterlockedDecrement(&_cRef))
  25. return _cRef;
  26. delete this;
  27. return 0;
  28. }
  29. class CEnumArray : public CEnumIDListBase
  30. {
  31. public:
  32. CEnumArray();
  33. HRESULT Init(const LPCITEMIDLIST rgpidl[], UINT cidl, UINT ulIndex);
  34. HRESULT InitFromPaths(LPCTSTR pszPaths);
  35. HRESULT InitFromCSIDLArray(const LPCTSTR rgcsidl[], UINT ccsidls, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  36. // IEnumIDList
  37. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  38. STDMETHODIMP Skip(ULONG celt);
  39. STDMETHODIMP Reset();
  40. STDMETHODIMP Clone(IEnumIDList **ppenum);
  41. protected:
  42. virtual ~CEnumArray();
  43. LPITEMIDLIST *_ppidl;
  44. BOOL _InitFolderParent(LPITEMIDLIST rgItems[], UINT cMaxItems, UINT *pcItems, LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidlParent);
  45. LPITEMIDLIST _ILLogical(LPCITEMIDLIST pidl);
  46. BOOL _ShouldEnumCSIDL(int csidl);
  47. private:
  48. LONG _cRef;
  49. ULONG _ulIndex;
  50. UINT _cItems;
  51. };
  52. CEnumArray::CEnumArray() : CEnumIDListBase()
  53. {
  54. }
  55. CEnumArray::~CEnumArray()
  56. {
  57. if (_ppidl)
  58. FreeIDListArray(_ppidl, _cItems);
  59. }
  60. HRESULT CEnumArray::Init(const LPCITEMIDLIST rgpidl[], UINT cidl, UINT ulIndex)
  61. {
  62. _ulIndex = ulIndex;
  63. HRESULT hr = CloneIDListArray(cidl, rgpidl, &_cItems, &_ppidl);
  64. if (S_FALSE == hr)
  65. hr = S_OK; // S_FALSE to S_OK
  66. return hr;
  67. }
  68. HRESULT CEnumArray::InitFromCSIDLArray(const LPCTSTR rgcsidl[], UINT ccsidls, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  69. {
  70. LPITEMIDLIST rgItems[32] = {0}; // reasonable max size, grow as needed
  71. UINT cItems = 0;
  72. LPITEMIDLIST pidlParent = NULL; // pidlFolder's pidlParent (filesystem or logical pidl)
  73. LPITEMIDLIST pidlParentLogical = NULL; // pidlFolder's pidlParent (logical pidl -- if exists)
  74. // Initialize pidlFolder's parent pidl.
  75. if (_InitFolderParent(rgItems, ARRAYSIZE(rgItems), &cItems, pidlFolder, &pidlParent))
  76. {
  77. // Retrieve pidlFolder's logical parent pidl.
  78. pidlParentLogical = _ILLogical(pidlParent);
  79. }
  80. // Initialize pidlItem.
  81. if (pidlItem &&
  82. (!pidlParent || !ILIsEqual(pidlItem, pidlParent)) &&
  83. (!pidlParentLogical || !ILIsEqual(pidlItem, pidlParentLogical)))
  84. {
  85. if (rgItems[cItems] = ILClone(pidlItem))
  86. {
  87. cItems++;
  88. }
  89. }
  90. // Initialize CSIDLs.
  91. for (UINT i = 0; (i < ccsidls) && (cItems < ARRAYSIZE(rgItems)); i++)
  92. {
  93. LPITEMIDLIST pidl;
  94. if (IS_INTRESOURCE(rgcsidl[i]))
  95. {
  96. int csidl = LOWORD((UINT_PTR)rgcsidl[i]);
  97. if (_ShouldEnumCSIDL(csidl))
  98. SHGetSpecialFolderLocation(NULL, csidl, &pidl);
  99. else
  100. pidl = NULL;
  101. }
  102. else
  103. {
  104. SHParseDisplayName((LPTSTR)rgcsidl[i], NULL, &pidl, 0, NULL);
  105. }
  106. if (pidl)
  107. {
  108. DWORD dwAttribs = SFGAO_NONENUMERATED;
  109. if ((pidlFolder && ILIsEqual(pidlFolder, pidl)) || // if pidl is not myself
  110. (pidlParent && ILIsEqual(pidlParent, pidl)) || // if pidl is not my parent
  111. (pidlParentLogical && ILIsEqual(pidlParentLogical, pidl)) || // (need to check logical parent too)
  112. (pidlItem && ILIsEqual(pidlItem, pidl)) || // if pidl is not pidlItem
  113. FAILED(SHGetNameAndFlags(pidl, 0, NULL, 0, &dwAttribs)) || // if pidl is not SFGAO_NONENUMERATED
  114. (SFGAO_NONENUMERATED & dwAttribs))
  115. {
  116. ILFree(pidl);
  117. }
  118. else
  119. {
  120. rgItems[cItems++] = pidl; // then add pidl
  121. }
  122. }
  123. }
  124. // Initialize CEnumArray with collected pidls.
  125. HRESULT hr = Init(rgItems, cItems, 0);
  126. // Cleanup.
  127. for (i = 0; i < cItems; i++)
  128. {
  129. ILFree(rgItems[i]);
  130. }
  131. ILFree(pidlParentLogical);
  132. return hr;
  133. }
  134. BOOL CEnumArray::_InitFolderParent(LPITEMIDLIST rgItems[], UINT cMaxItems, UINT *pcItems, LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidlParent)
  135. {
  136. ASSERT(*pcItems == 0); // Currently we expect to add the parent pidl as the FIRST entry.
  137. ASSERT(cMaxItems > 0); // Sanity check.
  138. // If there is a pidlFolder and it's NOT the Desktop pidl, add its parent
  139. // as the first entry in the rgItems array. Note that the reason we
  140. // exclude the Desktop pidl from having its parent added to the array is
  141. // because its parent is itself, and we don't want the folder we're
  142. // currently in appearing in rgItems since we're already there!
  143. if (pidlFolder && !ILIsEmpty(pidlFolder))
  144. {
  145. *ppidlParent = ILCloneParent(pidlFolder);
  146. if (*ppidlParent)
  147. {
  148. rgItems[*pcItems] = *ppidlParent;
  149. (*pcItems)++;
  150. }
  151. }
  152. else
  153. {
  154. *ppidlParent = NULL;
  155. }
  156. return (*ppidlParent != NULL);
  157. }
  158. // Description:
  159. // _ILLogical() will return NULL in three cases:
  160. // 1. out of memory
  161. // 2. pidl has no logical pidl equivalent
  162. // 3. pidl is SAME as logical pidl equivalent
  163. // (thus we already have the logical pidl)
  164. //
  165. // Note:
  166. // ILFree() must be called on returned pidls.
  167. //
  168. LPITEMIDLIST CEnumArray::_ILLogical(LPCITEMIDLIST pidl)
  169. {
  170. LPITEMIDLIST pidlLogical = SHLogILFromFSIL(pidl);
  171. if (pidlLogical && ILIsEqual(pidl, pidlLogical))
  172. {
  173. // If the pidl argument is logical, then we already
  174. // have the logical pidl so don't return another one.
  175. ILFree(pidlLogical);
  176. pidlLogical = NULL;
  177. }
  178. return pidlLogical;
  179. }
  180. STDMETHODIMP CEnumArray::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
  181. {
  182. HRESULT hr = S_FALSE;
  183. if (_ppidl && (_ulIndex < _cItems))
  184. {
  185. hr = SHILClone(_ppidl[_ulIndex++], ppidl);
  186. }
  187. if (pceltFetched)
  188. *pceltFetched = (hr == S_OK) ? 1 : 0;
  189. return hr;
  190. }
  191. STDMETHODIMP CEnumArray::Skip(ULONG celt)
  192. {
  193. _ulIndex = min(_cItems, _ulIndex + celt);
  194. return S_OK;
  195. }
  196. STDMETHODIMP CEnumArray::Reset()
  197. {
  198. _ulIndex = 0;
  199. return S_OK;
  200. }
  201. HRESULT _CreateIEnumIDListOnIDLists(const LPCITEMIDLIST rgpidl[], UINT cItems, UINT ulIndex, IEnumIDList **ppenum)
  202. {
  203. HRESULT hr = E_OUTOFMEMORY;
  204. *ppenum = NULL;
  205. CEnumArray *p = new CEnumArray();
  206. if (p)
  207. {
  208. hr = p->Init(rgpidl, cItems, ulIndex);
  209. if (SUCCEEDED(hr))
  210. {
  211. hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  212. }
  213. p->Release();
  214. }
  215. return hr;
  216. }
  217. STDMETHODIMP CEnumArray::Clone(IEnumIDList **ppenum)
  218. {
  219. return _CreateIEnumIDListOnIDLists(_ppidl, _cItems, _ulIndex, ppenum);
  220. }
  221. // Depending on the current state of the world, certain we may not want
  222. // to allow certain CSIDLs to be enumerated (i.e. we want to hide them).
  223. //
  224. BOOL CEnumArray::_ShouldEnumCSIDL(int csidl)
  225. {
  226. BOOL bEnum;
  227. switch (csidl)
  228. {
  229. case CSIDL_COMMON_DOCUMENTS:
  230. case CSIDL_COMMON_MUSIC:
  231. case CSIDL_COMMON_PICTURES:
  232. case CSIDL_COMMON_VIDEO:
  233. bEnum = SHShowSharedFolders();
  234. break;
  235. default:
  236. bEnum = TRUE;
  237. break;
  238. }
  239. return bEnum;
  240. }
  241. STDAPI CreateIEnumIDListOnIDLists(const LPCITEMIDLIST rgpidl[], UINT cItems, IEnumIDList **ppenum)
  242. {
  243. return _CreateIEnumIDListOnIDLists(rgpidl, cItems, 0, ppenum);
  244. }
  245. STDAPI CreateIEnumIDListOnCSIDLs(LPCITEMIDLIST pidlFolder, const LPCTSTR rgcsidl[], UINT cItems, IEnumIDList **ppenum)
  246. {
  247. return CreateIEnumIDListOnCSIDLs2(pidlFolder, NULL, rgcsidl, cItems, ppenum);
  248. }
  249. STDAPI CreateIEnumIDListOnCSIDLs2(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, const LPCTSTR rgcsidl[], UINT cItems, IEnumIDList **ppenum)
  250. {
  251. HRESULT hr = E_OUTOFMEMORY;
  252. *ppenum = NULL;
  253. CEnumArray *p = new CEnumArray();
  254. if (p)
  255. {
  256. hr = p->InitFromCSIDLArray(rgcsidl, cItems, pidlFolder, pidlItem);
  257. if (SUCCEEDED(hr))
  258. {
  259. hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  260. }
  261. p->Release();
  262. }
  263. return hr;
  264. }
  265. STDAPI CreateIEnumIDListPaths(LPCTSTR pszPaths, IEnumIDList **ppenum)
  266. {
  267. *ppenum = NULL;
  268. HRESULT hr = E_FAIL;
  269. LPITEMIDLIST rgItems[32] = {0};
  270. TCHAR szPath[MAX_PATH];
  271. LPCTSTR pszNext = pszPaths;
  272. int cItems = 0;
  273. while ((cItems < ARRAYSIZE(rgItems)) && (pszNext = NextPath(pszNext, szPath, ARRAYSIZE(szPath))))
  274. {
  275. PathRemoveBackslash(szPath);
  276. TCHAR szExpanded[MAX_PATH];
  277. if (SHExpandEnvironmentStrings(szPath, szExpanded, ARRAYSIZE(szExpanded)))
  278. {
  279. if (SUCCEEDED(SHParseDisplayName(szExpanded, NULL, &rgItems[cItems], 0, NULL)))
  280. {
  281. cItems++;
  282. }
  283. }
  284. }
  285. if (cItems > 0)
  286. {
  287. hr = _CreateIEnumIDListOnIDLists(rgItems, cItems, 0, ppenum);
  288. for (int i = 0; i < cItems; i++)
  289. ILFree(rgItems[i]);
  290. }
  291. return hr;
  292. }