|
|
#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; }
|