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.

376 lines
10 KiB

  1. #include "shellprv.h"
  2. #include "filefldr.h"
  3. #include "recdocs.h"
  4. #include "ids.h"
  5. #include "mtpt.h"
  6. class CFileSysEnum : public IEnumIDList
  7. {
  8. public:
  9. // IUnknown
  10. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  11. STDMETHOD_(ULONG,AddRef)();
  12. STDMETHOD_(ULONG,Release)();
  13. // IEnumIDList
  14. STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  15. STDMETHOD(Skip)(ULONG celt);
  16. STDMETHOD(Reset)();
  17. STDMETHOD(Clone)(IEnumIDList **ppenum);
  18. CFileSysEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags);
  19. HRESULT Init();
  20. private:
  21. ~CFileSysEnum();
  22. BOOL _FindNextFile();
  23. void _HideFiles(); // operates on _fd data
  24. LONG _cRef;
  25. CFSFolder *_pfsf;
  26. DWORD _grfFlags;
  27. HWND _hwnd;
  28. HANDLE _hfind;
  29. TCHAR _szFolder[MAX_PATH];
  30. BOOL _fMoreToEnum;
  31. WIN32_FIND_DATA _fd;
  32. int _cHiddenFiles;
  33. ULONGLONG _cbSize;
  34. IMruDataList *_pmruRecent;
  35. int _iIndexMRU;
  36. BOOL _fShowSuperHidden;
  37. BOOL _fIsRootDrive;
  38. BOOL _fIsCDFS;
  39. };
  40. CFileSysEnum::CFileSysEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags) :
  41. _cRef(1), _pfsf(pfsf), _hwnd(hwnd), _grfFlags(grfFlags), _hfind(INVALID_HANDLE_VALUE)
  42. {
  43. _fShowSuperHidden = ShowSuperHidden();
  44. _pfsf->AddRef();
  45. }
  46. CFileSysEnum::~CFileSysEnum()
  47. {
  48. if (_hfind != INVALID_HANDLE_VALUE)
  49. {
  50. // this handle can be the find file or MRU list in the case of RECENTDOCSDIR
  51. ATOMICRELEASE(_pmruRecent);
  52. FindClose(_hfind);
  53. _hfind = INVALID_HANDLE_VALUE;
  54. }
  55. _pfsf->Release();
  56. }
  57. HRESULT CFileSysEnum::Init()
  58. {
  59. TCHAR szPath[MAX_PATH];
  60. HRESULT hr = _pfsf->_GetPath(_szFolder);
  61. if (SUCCEEDED(hr) && !PathIsUNC(_szFolder))
  62. {
  63. TCHAR szRoot[] = TEXT("A:\\");
  64. _fIsRootDrive = PathIsRoot(_szFolder);
  65. // For mapped net drives, register a change
  66. // notify alias for the corresponding UNC path.
  67. szRoot[0] = _szFolder[0];
  68. if (DRIVE_REMOTE == GetDriveType(szRoot))
  69. {
  70. MountPoint_RegisterChangeNotifyAlias(DRIVEID(_szFolder));
  71. }
  72. TCHAR szFileSystem[6];
  73. _fIsCDFS = (DRIVE_CDROM == GetDriveType(szRoot)) &&
  74. GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, NULL, szFileSystem, ARRAYSIZE(szFileSystem)) &&
  75. (StrCmpI(L"CDFS", szFileSystem) == 0);
  76. }
  77. if (SUCCEEDED(hr) &&
  78. PathCombine(szPath, _szFolder, c_szStarDotStar))
  79. {
  80. // let name mapper see the path/PIDL pair (for UNC root mapping)
  81. // skip the My Net Places entry when passing it to NPTRegisterNameToPidlTranslation.
  82. LPCITEMIDLIST pidlToMap = _pfsf->_pidlTarget ? _pfsf->_pidlTarget:_pfsf->_pidl;
  83. if (IsIDListInNameSpace(pidlToMap, &CLSID_NetworkPlaces))
  84. {
  85. NPTRegisterNameToPidlTranslation(_szFolder, _ILNext(pidlToMap));
  86. }
  87. if (_grfFlags == SHCONTF_FOLDERS)
  88. {
  89. // use mask to only find folders, mask is in the hi byte of dwFileAttributes
  90. // algorithm: (((attrib_on_disk & mask) ^ mask) == 0)
  91. // signature to tell SHFindFirstFileRetry() to use the attribs specified
  92. _fd.dwFileAttributes = (FILE_ATTRIBUTE_DIRECTORY << 8) |
  93. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
  94. _fd.dwReserved0 = 0x56504347;
  95. }
  96. hr = SHFindFirstFileRetry(_hwnd, NULL, szPath, &_fd, &_hfind, SHPPFW_NONE);
  97. if (SUCCEEDED(hr))
  98. {
  99. _HideFiles();
  100. ASSERT(hr == S_OK ? (_hfind != INVALID_HANDLE_VALUE) : TRUE);
  101. _fMoreToEnum = (hr == S_OK);
  102. if (!(_grfFlags & SHCONTF_INCLUDEHIDDEN))
  103. {
  104. if (_pfsf->_IsCSIDL(CSIDL_RECENT))
  105. {
  106. CreateRecentMRUList(&_pmruRecent);
  107. }
  108. }
  109. hr = S_OK; // convert S_FALSE to S_OK to match ::EnumObjects() returns
  110. }
  111. }
  112. else if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
  113. {
  114. // Tracking target doesn't exist; return an empty enumerator
  115. _fMoreToEnum = FALSE;
  116. hr = S_OK;
  117. }
  118. else
  119. {
  120. // _GetPathForItem & PathCombine() fail when path is too long
  121. if (_hwnd)
  122. {
  123. ShellMessageBox(HINST_THISDLL, _hwnd, MAKEINTRESOURCE(IDS_ENUMERR_PATHTOOLONG),
  124. NULL, MB_OK | MB_ICONHAND);
  125. }
  126. // This error value tells callers that we have already displayed error UI so skip
  127. // displaying errors.
  128. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  129. }
  130. return hr;
  131. }
  132. STDMETHODIMP CFileSysEnum::QueryInterface(REFIID riid, void **ppv)
  133. {
  134. static const QITAB qit[] = {
  135. QITABENT(CFileSysEnum, IEnumIDList), // IID_IEnumIDList
  136. { 0 },
  137. };
  138. return QISearch(this, qit, riid, ppv);
  139. }
  140. STDMETHODIMP_(ULONG) CFileSysEnum::AddRef()
  141. {
  142. return InterlockedIncrement(&_cRef);
  143. }
  144. STDMETHODIMP_(ULONG) CFileSysEnum::Release()
  145. {
  146. if (InterlockedDecrement(&_cRef))
  147. return _cRef;
  148. delete this;
  149. return 0;
  150. }
  151. const LPCWSTR c_rgFilesToHideInRoot[] =
  152. {
  153. L"AUTOEXEC.BAT", // case sensitive
  154. L"CONFIG.SYS",
  155. L"COMMAND.COM"
  156. };
  157. const LPCWSTR c_rgFilesToHideOnCDFS[] =
  158. {
  159. L"thumbs.db",
  160. L"desktop.ini"
  161. };
  162. void CFileSysEnum::_HideFiles()
  163. {
  164. // only do this if HIDDEN and SYSTEM attributes are not set on the file
  165. // (we assume if the file has these bits these files are setup properly)
  166. if (0 == (_fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)))
  167. {
  168. // only do this for root drives
  169. if (_fIsRootDrive)
  170. {
  171. for (int i = 0; i < ARRAYSIZE(c_rgFilesToHideInRoot); i++)
  172. {
  173. // case sensitive to make it faster
  174. if (0 == StrCmpC(c_rgFilesToHideInRoot[i], _fd.cFileName))
  175. {
  176. _fd.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
  177. break;
  178. }
  179. }
  180. }
  181. // only do this if we're on a normal CD filesystem
  182. if (_fIsCDFS)
  183. {
  184. for (int i = 0; i < ARRAYSIZE(c_rgFilesToHideOnCDFS); i++)
  185. {
  186. // dont share code from above since these can be upper or lower
  187. if (0 == StrCmpI(c_rgFilesToHideOnCDFS[i], _fd.cFileName))
  188. {
  189. _fd.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
  190. break;
  191. }
  192. }
  193. }
  194. }
  195. }
  196. BOOL CFileSysEnum::_FindNextFile()
  197. {
  198. BOOL fMoreToEnum = FALSE;
  199. if (_pmruRecent)
  200. {
  201. LPITEMIDLIST pidl;
  202. while (SUCCEEDED(RecentDocs_Enum(_pmruRecent, _iIndexMRU, &pidl)))
  203. {
  204. // confirm that the item stil exists in the file system, fill in the _fd data
  205. TCHAR szPath[MAX_PATH];
  206. HANDLE h;
  207. _pfsf->_GetPathForItem(_pfsf->_IsValidID(pidl), szPath);
  208. ILFree(pidl);
  209. h = FindFirstFile(szPath, &_fd);
  210. if (h != INVALID_HANDLE_VALUE)
  211. {
  212. fMoreToEnum = TRUE;
  213. _iIndexMRU++;
  214. FindClose(h);
  215. break;
  216. }
  217. else
  218. {
  219. //
  220. // WARNING - if the list is corrupt we torch it - ZekeL 19-JUN-98
  221. // we could do some special stuff, i guess, to weed out the bad
  222. // items, but it seems simpler to just blow it away.
  223. // the only reason this should happen is if somebody
  224. // has been mushing around with RECENT directory directly,
  225. // which they shouldnt do since it is hidden...
  226. //
  227. // kill this invalid entry, and then try the same index again...
  228. _pmruRecent->Delete(_iIndexMRU);
  229. }
  230. }
  231. }
  232. else
  233. {
  234. fMoreToEnum = FindNextFile(_hfind, &_fd);
  235. _HideFiles();
  236. }
  237. return fMoreToEnum;
  238. }
  239. STDMETHODIMP CFileSysEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
  240. {
  241. HRESULT hr;
  242. for (; _fMoreToEnum; _fMoreToEnum = _FindNextFile())
  243. {
  244. if (_fMoreToEnum == (BOOL)42)
  245. continue; // we already processed the current item, skip it now
  246. if (PathIsDotOrDotDot(_fd.cFileName))
  247. continue;
  248. if (!(_grfFlags & SHCONTF_STORAGE))
  249. {
  250. if (_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  251. {
  252. if (!(_grfFlags & SHCONTF_FOLDERS))
  253. continue; // item is folder but client does not want folders
  254. }
  255. else if (!(_grfFlags & SHCONTF_NONFOLDERS))
  256. continue; // item is file, but client only wants folders
  257. // skip hidden and system things unconditionally, don't even count them
  258. if (!_fShowSuperHidden && IS_SYSTEM_HIDDEN(_fd.dwFileAttributes))
  259. continue;
  260. }
  261. _cbSize += MAKELONGLONG(_fd.nFileSizeLow, _fd.nFileSizeHigh);
  262. if (!(_grfFlags & (SHCONTF_INCLUDEHIDDEN | SHCONTF_STORAGE)) &&
  263. (_fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
  264. {
  265. _cHiddenFiles++;
  266. continue;
  267. }
  268. break;
  269. }
  270. if (_fMoreToEnum)
  271. {
  272. hr = _pfsf->_CreateIDList(&_fd, NULL, ppidl);
  273. _fMoreToEnum = (BOOL)42; // we have processed the current item, skip it next time
  274. }
  275. else
  276. {
  277. *ppidl = NULL;
  278. hr = S_FALSE; // no more items
  279. // completed the enum, stash some items back into the folder
  280. // PERF ??: we could QueryService for the view callback at this point and
  281. // poke these in directly there instead of pushing these into the folder
  282. _pfsf->_cHiddenFiles = _cHiddenFiles;
  283. _pfsf->_cbSize = _cbSize;
  284. }
  285. if (pceltFetched)
  286. *pceltFetched = (hr == S_OK) ? 1 : 0;
  287. return hr;
  288. }
  289. STDMETHODIMP CFileSysEnum::Skip(ULONG celt)
  290. {
  291. return E_NOTIMPL;
  292. }
  293. STDMETHODIMP CFileSysEnum::Reset()
  294. {
  295. return S_OK;
  296. }
  297. STDMETHODIMP CFileSysEnum::Clone(IEnumIDList **ppenum)
  298. {
  299. *ppenum = NULL;
  300. return E_NOTIMPL;
  301. }
  302. STDAPI CFSFolder_CreateEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  303. {
  304. HRESULT hr;
  305. CFileSysEnum *penum = new CFileSysEnum(pfsf, hwnd, grfFlags);
  306. if (penum)
  307. {
  308. hr = penum->Init();
  309. if (SUCCEEDED(hr))
  310. hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  311. penum->Release();
  312. }
  313. else
  314. {
  315. hr = E_OUTOFMEMORY;
  316. *ppenum = NULL;
  317. }
  318. return hr;
  319. }