#include "shellprv.h" #include "filefldr.h" #include "recdocs.h" #include "ids.h" #include "mtpt.h" class CFileSysEnum : public IEnumIDList { public: // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG,AddRef)(); STDMETHOD_(ULONG,Release)(); // IEnumIDList STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHOD(Skip)(ULONG celt); STDMETHOD(Reset)(); STDMETHOD(Clone)(IEnumIDList **ppenum); CFileSysEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags); HRESULT Init(); private: ~CFileSysEnum(); BOOL _FindNextFile(); void _HideFiles(); // operates on _fd data LONG _cRef; CFSFolder *_pfsf; DWORD _grfFlags; HWND _hwnd; HANDLE _hfind; TCHAR _szFolder[MAX_PATH]; BOOL _fMoreToEnum; WIN32_FIND_DATA _fd; int _cHiddenFiles; ULONGLONG _cbSize; IMruDataList *_pmruRecent; int _iIndexMRU; BOOL _fShowSuperHidden; BOOL _fIsRootDrive; BOOL _fIsCDFS; }; CFileSysEnum::CFileSysEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags) : _cRef(1), _pfsf(pfsf), _hwnd(hwnd), _grfFlags(grfFlags), _hfind(INVALID_HANDLE_VALUE) { _fShowSuperHidden = ShowSuperHidden(); _pfsf->AddRef(); } CFileSysEnum::~CFileSysEnum() { if (_hfind != INVALID_HANDLE_VALUE) { // this handle can be the find file or MRU list in the case of RECENTDOCSDIR ATOMICRELEASE(_pmruRecent); FindClose(_hfind); _hfind = INVALID_HANDLE_VALUE; } _pfsf->Release(); } HRESULT CFileSysEnum::Init() { TCHAR szPath[MAX_PATH]; HRESULT hr = _pfsf->_GetPath(_szFolder); if (SUCCEEDED(hr) && !PathIsUNC(_szFolder)) { TCHAR szRoot[] = TEXT("A:\\"); _fIsRootDrive = PathIsRoot(_szFolder); // For mapped net drives, register a change // notify alias for the corresponding UNC path. szRoot[0] = _szFolder[0]; if (DRIVE_REMOTE == GetDriveType(szRoot)) { MountPoint_RegisterChangeNotifyAlias(DRIVEID(_szFolder)); } TCHAR szFileSystem[6]; _fIsCDFS = (DRIVE_CDROM == GetDriveType(szRoot)) && GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, NULL, szFileSystem, ARRAYSIZE(szFileSystem)) && (StrCmpI(L"CDFS", szFileSystem) == 0); } if (SUCCEEDED(hr) && PathCombine(szPath, _szFolder, c_szStarDotStar)) { // let name mapper see the path/PIDL pair (for UNC root mapping) // skip the My Net Places entry when passing it to NPTRegisterNameToPidlTranslation. LPCITEMIDLIST pidlToMap = _pfsf->_pidlTarget ? _pfsf->_pidlTarget:_pfsf->_pidl; if (IsIDListInNameSpace(pidlToMap, &CLSID_NetworkPlaces)) { NPTRegisterNameToPidlTranslation(_szFolder, _ILNext(pidlToMap)); } if (_grfFlags == SHCONTF_FOLDERS) { // use mask to only find folders, mask is in the hi byte of dwFileAttributes // algorithm: (((attrib_on_disk & mask) ^ mask) == 0) // signature to tell SHFindFirstFileRetry() to use the attribs specified _fd.dwFileAttributes = (FILE_ATTRIBUTE_DIRECTORY << 8) | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY; _fd.dwReserved0 = 0x56504347; } hr = SHFindFirstFileRetry(_hwnd, NULL, szPath, &_fd, &_hfind, SHPPFW_NONE); if (SUCCEEDED(hr)) { _HideFiles(); ASSERT(hr == S_OK ? (_hfind != INVALID_HANDLE_VALUE) : TRUE); _fMoreToEnum = (hr == S_OK); if (!(_grfFlags & SHCONTF_INCLUDEHIDDEN)) { if (_pfsf->_IsCSIDL(CSIDL_RECENT)) { CreateRecentMRUList(&_pmruRecent); } } hr = S_OK; // convert S_FALSE to S_OK to match ::EnumObjects() returns } } else if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) { // Tracking target doesn't exist; return an empty enumerator _fMoreToEnum = FALSE; hr = S_OK; } else { // _GetPathForItem & PathCombine() fail when path is too long if (_hwnd) { ShellMessageBox(HINST_THISDLL, _hwnd, MAKEINTRESOURCE(IDS_ENUMERR_PATHTOOLONG), NULL, MB_OK | MB_ICONHAND); } // This error value tells callers that we have already displayed error UI so skip // displaying errors. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } return hr; } STDMETHODIMP CFileSysEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CFileSysEnum, IEnumIDList), // IID_IEnumIDList { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CFileSysEnum::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CFileSysEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } const LPCWSTR c_rgFilesToHideInRoot[] = { L"AUTOEXEC.BAT", // case sensitive L"CONFIG.SYS", L"COMMAND.COM" }; const LPCWSTR c_rgFilesToHideOnCDFS[] = { L"thumbs.db", L"desktop.ini" }; void CFileSysEnum::_HideFiles() { // only do this if HIDDEN and SYSTEM attributes are not set on the file // (we assume if the file has these bits these files are setup properly) if (0 == (_fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))) { // only do this for root drives if (_fIsRootDrive) { for (int i = 0; i < ARRAYSIZE(c_rgFilesToHideInRoot); i++) { // case sensitive to make it faster if (0 == StrCmpC(c_rgFilesToHideInRoot[i], _fd.cFileName)) { _fd.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; break; } } } // only do this if we're on a normal CD filesystem if (_fIsCDFS) { for (int i = 0; i < ARRAYSIZE(c_rgFilesToHideOnCDFS); i++) { // dont share code from above since these can be upper or lower if (0 == StrCmpI(c_rgFilesToHideOnCDFS[i], _fd.cFileName)) { _fd.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; break; } } } } } BOOL CFileSysEnum::_FindNextFile() { BOOL fMoreToEnum = FALSE; if (_pmruRecent) { LPITEMIDLIST pidl; while (SUCCEEDED(RecentDocs_Enum(_pmruRecent, _iIndexMRU, &pidl))) { // confirm that the item stil exists in the file system, fill in the _fd data TCHAR szPath[MAX_PATH]; HANDLE h; _pfsf->_GetPathForItem(_pfsf->_IsValidID(pidl), szPath); ILFree(pidl); h = FindFirstFile(szPath, &_fd); if (h != INVALID_HANDLE_VALUE) { fMoreToEnum = TRUE; _iIndexMRU++; FindClose(h); break; } else { // // WARNING - if the list is corrupt we torch it - ZekeL 19-JUN-98 // we could do some special stuff, i guess, to weed out the bad // items, but it seems simpler to just blow it away. // the only reason this should happen is if somebody // has been mushing around with RECENT directory directly, // which they shouldnt do since it is hidden... // // kill this invalid entry, and then try the same index again... _pmruRecent->Delete(_iIndexMRU); } } } else { fMoreToEnum = FindNextFile(_hfind, &_fd); _HideFiles(); } return fMoreToEnum; } STDMETHODIMP CFileSysEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched) { HRESULT hr; for (; _fMoreToEnum; _fMoreToEnum = _FindNextFile()) { if (_fMoreToEnum == (BOOL)42) continue; // we already processed the current item, skip it now if (PathIsDotOrDotDot(_fd.cFileName)) continue; if (!(_grfFlags & SHCONTF_STORAGE)) { if (_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!(_grfFlags & SHCONTF_FOLDERS)) continue; // item is folder but client does not want folders } else if (!(_grfFlags & SHCONTF_NONFOLDERS)) continue; // item is file, but client only wants folders // skip hidden and system things unconditionally, don't even count them if (!_fShowSuperHidden && IS_SYSTEM_HIDDEN(_fd.dwFileAttributes)) continue; } _cbSize += MAKELONGLONG(_fd.nFileSizeLow, _fd.nFileSizeHigh); if (!(_grfFlags & (SHCONTF_INCLUDEHIDDEN | SHCONTF_STORAGE)) && (_fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) { _cHiddenFiles++; continue; } break; } if (_fMoreToEnum) { hr = _pfsf->_CreateIDList(&_fd, NULL, ppidl); _fMoreToEnum = (BOOL)42; // we have processed the current item, skip it next time } else { *ppidl = NULL; hr = S_FALSE; // no more items // completed the enum, stash some items back into the folder // PERF ??: we could QueryService for the view callback at this point and // poke these in directly there instead of pushing these into the folder _pfsf->_cHiddenFiles = _cHiddenFiles; _pfsf->_cbSize = _cbSize; } if (pceltFetched) *pceltFetched = (hr == S_OK) ? 1 : 0; return hr; } STDMETHODIMP CFileSysEnum::Skip(ULONG celt) { return E_NOTIMPL; } STDMETHODIMP CFileSysEnum::Reset() { return S_OK; } STDMETHODIMP CFileSysEnum::Clone(IEnumIDList **ppenum) { *ppenum = NULL; return E_NOTIMPL; } STDAPI CFSFolder_CreateEnum(CFSFolder *pfsf, HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum) { HRESULT hr; CFileSysEnum *penum = new CFileSysEnum(pfsf, hwnd, grfFlags); if (penum) { hr = penum->Init(); if (SUCCEEDED(hr)) hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); penum->Release(); } else { hr = E_OUTOFMEMORY; *ppenum = NULL; } return hr; }