WindowsXP-SP1/shell/shell32/fileenum.cpp

377 lines
11 KiB
C++

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