670 lines
19 KiB
C++
670 lines
19 KiB
C++
|
|
|
|
// Copyright (c) Microsoft Corporation
|
|
|
|
// File: treewkcb.cpp
|
|
|
|
// This file contains the implementation of CBaseTreeWalkerCB, the base class for
|
|
// the shell Tree Walker Callback objects who implement IShellTreeWalkerCallBack.
|
|
// And ...
|
|
// CFolderSizeTreeWalkerCB, which walks the File System tree on a folder and computes the size
|
|
// of the folder
|
|
// CLinkResolveTreeWalkerCB, which search through the File System to resolve a lost link
|
|
// History:
|
|
// 12-9-97 by dli implemented CFolderSizeTreeWalkerCB
|
|
// 12-17-97 by dli implemendted CBaseTreeWalkerCB, added CLinkResolveTreeWalkerCB
|
|
|
|
#include "shellprv.h"
|
|
#include "treewkcb.h"
|
|
#include "resolve.h"
|
|
|
|
|
|
#define TF_TREEWKCB 0
|
|
|
|
CBaseTreeWalkerCB::CBaseTreeWalkerCB(): _cRef(1)
|
|
{
|
|
ASSERT(_pstw == NULL);
|
|
_hrInit = SHCoInitialize();
|
|
}
|
|
|
|
CBaseTreeWalkerCB::~CBaseTreeWalkerCB()
|
|
{
|
|
ATOMICRELEASE(_pstw);
|
|
SHCoUninitialize(_hrInit);
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::Initialize()
|
|
{
|
|
return CoCreateInstance(CLSID_CShellTreeWalker, NULL, CLSCTX_INPROC_SERVER, IID_IShellTreeWalker, (LPVOID *)&_pstw);
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
ASSERT(ppvObj != NULL);
|
|
|
|
static const QITAB qit[] = {
|
|
QITABENT(CBaseTreeWalkerCB, IShellTreeWalkerCallBack),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
ULONG CBaseTreeWalkerCB::AddRef()
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG CBaseTreeWalkerCB::Release()
|
|
{
|
|
_cRef--;
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::FoundFile(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::EnterFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::LeaveFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CBaseTreeWalkerCB::HandleError(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, HRESULT ErrorCode)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Folder size computation tree walker callback class
|
|
|
|
class CFolderSizeTreeWalkerCB : public CBaseTreeWalkerCB
|
|
{
|
|
friend HRESULT _stdcall FolderSize(LPCTSTR pszDir, FOLDERCONTENTSINFO * pfci);
|
|
public:
|
|
CFolderSizeTreeWalkerCB(FOLDERCONTENTSINFO * pfci);
|
|
|
|
// *** IShellTreeWalkerCallBack overridden methods ***
|
|
virtual STDMETHODIMP FoundFile(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd);
|
|
virtual STDMETHODIMP EnterFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd);
|
|
virtual STDMETHODIMP Initialize();
|
|
|
|
protected:
|
|
FOLDERCONTENTSINFO * _pfci;
|
|
TREEWALKERSTATS _twsInitial;
|
|
};
|
|
|
|
CFolderSizeTreeWalkerCB::CFolderSizeTreeWalkerCB(FOLDERCONTENTSINFO * pfci): _pfci(pfci)
|
|
{
|
|
}
|
|
|
|
|
|
// CFolderSizeTreeWalkerCB::Initialize
|
|
|
|
HRESULT CFolderSizeTreeWalkerCB::Initialize()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// we call the base class which will create the _pstw, and
|
|
// if that succeeds then we initialize it to have the starting
|
|
// size values of _pcfi.
|
|
hr = CBaseTreeWalkerCB::Initialize();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
ASSERT(_pfci);
|
|
|
|
// set the starting values for the twsInitial so we can have cumulative results
|
|
_twsInitial.nFiles = _pfci->cFiles;
|
|
_twsInitial.nFolders = _pfci->cFolders;
|
|
_twsInitial.ulTotalSize = _pfci->cbSize;
|
|
_twsInitial.ulActualSize = _pfci->cbActualSize;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
// CFolderSizeTreeWalkerCB::FoundFile
|
|
|
|
HRESULT CFolderSizeTreeWalkerCB::FoundFile(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
if (!_pfci->bContinue)
|
|
{
|
|
// we have been signaled to stop, so return failure
|
|
return E_FAIL;
|
|
}
|
|
|
|
_pfci->cbSize = _twsInitial.ulTotalSize + ptws->ulTotalSize;
|
|
_pfci->cbActualSize = _twsInitial.ulActualSize + ptws->ulActualSize;
|
|
_pfci->cFiles = _twsInitial.nFiles + ptws->nFiles;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// CFolderSizeTreeWalkerCB::EnterFolder
|
|
|
|
HRESULT CFolderSizeTreeWalkerCB::EnterFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
if (!_pfci->bContinue)
|
|
{
|
|
// we have been signaled to stop, so return failure
|
|
return E_FAIL;
|
|
}
|
|
|
|
_pfci->cFolders = _twsInitial.nFolders + ptws->nFolders;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Main function for folder size computation, friend of the CFolderSizeTreeWalkerCB class
|
|
|
|
STDAPI FolderSize(LPCTSTR pszDir, FOLDERCONTENTSINFO * pfci)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
CFolderSizeTreeWalkerCB * pfstwcb = new CFolderSizeTreeWalkerCB(pfci);
|
|
if (pfstwcb)
|
|
{
|
|
if (SUCCEEDED(pfstwcb->Initialize()))
|
|
{
|
|
LPCWSTR pwszDir;
|
|
ASSERT(IS_VALID_CODE_PTR(pfstwcb->_pstw, IShellTreeWalker));
|
|
#ifdef UNICODE
|
|
pwszDir = pszDir;
|
|
#else
|
|
WCHAR wszDir[MAX_PATH];
|
|
SHTCharToUnicode(pszDir, wszDir, ARRAYSIZE(wszDir));
|
|
pwszDir = wszDir;
|
|
#endif
|
|
hres = pfstwcb->_pstw->WalkTree(WT_NOTIFYFOLDERENTER, pwszDir, NULL, 0, SAFECAST(pfstwcb, IShellTreeWalkerCallBack *));
|
|
}
|
|
pfstwcb->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// Link Resolution tree walker callback class
|
|
|
|
class CLinkResolveTreeWalkerCB : public CBaseTreeWalkerCB
|
|
{
|
|
friend VOID DoDownLevelSearch(RESOLVE_SEARCH_DATA *prs, TCHAR szFolderPath[], int iStopScore);
|
|
public:
|
|
CLinkResolveTreeWalkerCB(RESOLVE_SEARCH_DATA *prs, int iStopScore);
|
|
|
|
// *** IShellTreeWalkerCallBack overridden methods ***
|
|
virtual STDMETHODIMP FoundFile(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd);
|
|
virtual STDMETHODIMP EnterFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd);
|
|
|
|
BOOL SearchInFolder(LPCTSTR pszFolder, int cLevels);
|
|
|
|
protected:
|
|
RESOLVE_SEARCH_DATA * _prs;
|
|
int _iStopScore;
|
|
DWORD _dwSearchFlags;
|
|
int _iFolderBonus;
|
|
|
|
WCHAR _wszSearchSpec[64];
|
|
LPCWSTR _pwszSearchSpec;
|
|
|
|
int _ScoreFindData(const WIN32_FIND_DATA *pfd);
|
|
HRESULT _ProcessFoundFile(LPCTSTR pszPath, WIN32_FIND_DATAW * pwfdw);
|
|
};
|
|
|
|
CLinkResolveTreeWalkerCB::CLinkResolveTreeWalkerCB(RESOLVE_SEARCH_DATA *prs, int iStopScore): _prs(prs), _iStopScore(iStopScore)
|
|
{
|
|
ASSERT(_pwszSearchSpec == NULL);
|
|
|
|
// Note: We only search files with the same extension, this saves us a lot
|
|
// of useless work and from the humiliation of coming up with a ridiculous answer
|
|
_dwSearchFlags = WT_NOTIFYFOLDERENTER | WT_EXCLUDEWALKROOT;
|
|
if (_prs->dwMatch & FILE_ATTRIBUTE_DIRECTORY)
|
|
_dwSearchFlags |= WT_FOLDERONLY;
|
|
else
|
|
{
|
|
// Note that this does the right thing if the file has no extension
|
|
LPTSTR pszExt = PathFindExtension(prs->pfd->cFileName);
|
|
_wszSearchSpec[0] = L'*';
|
|
SHTCharToUnicode(pszExt, &_wszSearchSpec[1], ARRAYSIZE(_wszSearchSpec) - 1);
|
|
_pwszSearchSpec = _wszSearchSpec;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare two FileTime structures. First, see if they're really equal
|
|
// (using CompareFileTime). If not, see if one has 10ms granularity,
|
|
// and if the other rounds down to the same value. This is done
|
|
// to handle the case where a file is moved from NTFS to FAT;
|
|
// FAT file tiems are 10ms granularity, while NTFS is 100ns. When
|
|
// an NTFS file is moved to FAT, its time is rounded down.
|
|
|
|
|
|
#define NTFS_UNITS_PER_FAT_UNIT 100000
|
|
|
|
BOOL IsEqualFileTimesWithTruncation( const FILETIME *pft1, const FILETIME *pft2 )
|
|
{
|
|
ULARGE_INTEGER uli1, uli2;
|
|
ULARGE_INTEGER *puliFAT, *puliNTFS;
|
|
FILETIME ftFAT, ftNTFS;
|
|
|
|
if( 0 == CompareFileTime( pft1, pft2 ))
|
|
return( TRUE );
|
|
|
|
uli1.LowPart = pft1->dwLowDateTime;
|
|
uli1.HighPart = pft1->dwHighDateTime;
|
|
|
|
uli2.LowPart = pft2->dwLowDateTime;
|
|
uli2.HighPart = pft2->dwHighDateTime;
|
|
|
|
// Is one of the times 10ms granular?
|
|
|
|
if( 0 == (uli1.QuadPart % NTFS_UNITS_PER_FAT_UNIT) )
|
|
{
|
|
puliFAT = &uli1;
|
|
puliNTFS = &uli2;
|
|
}
|
|
else if( 0 == (uli2.QuadPart % NTFS_UNITS_PER_FAT_UNIT) )
|
|
{
|
|
puliFAT = &uli2;
|
|
puliNTFS = &uli1;
|
|
}
|
|
else
|
|
{
|
|
// Neither time appears to be FAT, so they're
|
|
// really different.
|
|
return( FALSE );
|
|
}
|
|
|
|
// If uliNTFS is already 10ms granular, then again the two times
|
|
// are really different.
|
|
|
|
if( 0 == (puliNTFS->QuadPart % NTFS_UNITS_PER_FAT_UNIT) )
|
|
return( FALSE );
|
|
|
|
// Now see if the FAT time is the same as the NTFS time
|
|
// when the latter is rounded down to the nearest 10ms.
|
|
|
|
puliNTFS->QuadPart = (puliNTFS->QuadPart / NTFS_UNITS_PER_FAT_UNIT) * NTFS_UNITS_PER_FAT_UNIT;
|
|
|
|
ftNTFS.dwLowDateTime = puliNTFS->LowPart;
|
|
ftNTFS.dwHighDateTime = puliNTFS->HighPart;
|
|
ftFAT.dwLowDateTime = puliFAT->LowPart;
|
|
ftFAT.dwHighDateTime = puliFAT->HighPart;
|
|
|
|
return( 0 == CompareFileTime( &ftFAT, &ftNTFS ));
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute a weighted score for a given find
|
|
|
|
int CLinkResolveTreeWalkerCB::_ScoreFindData(const WIN32_FIND_DATA *pfd)
|
|
{
|
|
int iScore = 0;
|
|
BOOL bSameName, bSameCreateDate, bSameWriteTime, bSameExt, bHasCreateDate;
|
|
|
|
bSameName = lstrcmpi(_prs->pfd->cFileName, pfd->cFileName) == 0;
|
|
|
|
bSameExt = lstrcmpi(PathFindExtension(_prs->pfd->cFileName), PathFindExtension(pfd->cFileName)) == 0;
|
|
|
|
bHasCreateDate = !IsNullTime(&pfd->ftCreationTime);
|
|
|
|
bSameCreateDate = bHasCreateDate &&
|
|
IsEqualFileTimesWithTruncation(&pfd->ftCreationTime, &_prs->pfd->ftCreationTime);
|
|
|
|
bSameWriteTime = !IsNullTime(&pfd->ftLastWriteTime) &&
|
|
IsEqualFileTimesWithTruncation(&pfd->ftLastWriteTime, &_prs->pfd->ftLastWriteTime);
|
|
|
|
if (bSameName || bSameCreateDate)
|
|
{
|
|
if (bSameName)
|
|
iScore += bHasCreateDate ? 16 : 32;
|
|
|
|
if (bSameCreateDate)
|
|
{
|
|
iScore += 32;
|
|
|
|
if (bSameExt)
|
|
iScore += 8;
|
|
}
|
|
|
|
if (bSameWriteTime)
|
|
iScore += 8;
|
|
|
|
if (pfd->nFileSizeLow == _prs->pfd->nFileSizeLow)
|
|
iScore += 4;
|
|
|
|
// if it is in the same folder as the original give it a slight bonus
|
|
iScore += _iFolderBonus;
|
|
}
|
|
else
|
|
{
|
|
// doesn't have create date, apply different rules
|
|
|
|
if (bSameExt)
|
|
iScore += 8;
|
|
|
|
if (bSameWriteTime)
|
|
iScore += 8;
|
|
|
|
if (pfd->nFileSizeLow == _prs->pfd->nFileSizeLow)
|
|
iScore += 4;
|
|
}
|
|
|
|
return iScore;
|
|
}
|
|
|
|
|
|
// Helper function for both EnterFolder and FoundFile
|
|
|
|
HRESULT CLinkResolveTreeWalkerCB::_ProcessFoundFile(LPCTSTR pszPath, WIN32_FIND_DATAW * pwfdw)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
#ifdef UNICODE
|
|
WIN32_FIND_DATA * pwfd = pwfdw;
|
|
#else
|
|
WIN32_FIND_DATA wfd;
|
|
WIN32_FIND_DATA * pwfd = &wfd;
|
|
|
|
hmemcpy(&wfd, pwfdw, SIZEOF(WIN32_FIND_DATA));
|
|
SHUnicodeToTChar(pwfdw->cFileName, wfd.cFileName, ARRAYSIZE(wfd.cFileName));
|
|
SHUnicodeToTChar(pwfdw->cAlternateFileName, wfd.cAlternateFileName, ARRAYSIZE(wfd.cAlternateFileName));
|
|
pwfd = &wfd;
|
|
#endif
|
|
|
|
if (!PathIsLnk(pwfd->cFileName))
|
|
{
|
|
// both are files or folders, see how it scores
|
|
int iScore = _ScoreFindData(pwfd);
|
|
|
|
if (iScore > _prs->iScore)
|
|
{
|
|
_prs->fdFound = *pwfd;
|
|
|
|
TraceMsg(TF_TREEWKCB, "Better match found %s, %d", pszPath, iScore);
|
|
|
|
// store the score and fully qualified path
|
|
_prs->iScore = iScore;
|
|
lstrcpyn(_prs->fdFound.cFileName, pszPath, ARRAYSIZE(_prs->fdFound.cFileName));
|
|
}
|
|
}
|
|
|
|
if ((_prs->iScore >= _iStopScore) || (GetTickCount() >= _prs->dwTimeLimit))
|
|
{
|
|
_prs->bContinue = FALSE;
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// IShellTreeWalkerCallBack::FoundFile
|
|
|
|
HRESULT CLinkResolveTreeWalkerCB::FoundFile(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTRW(pwszPath, -1));
|
|
ASSERT(pwfd);
|
|
|
|
|
|
// Respond quickly to the Cancel button.
|
|
|
|
if (!_prs->bContinue)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// We should've excluded files if we're looking for a folder
|
|
ASSERT(!(_prs->dwMatch & FILE_ATTRIBUTE_DIRECTORY));
|
|
|
|
LPCTSTR ptszPath;
|
|
#ifdef UNICODE
|
|
ptszPath = pwszPath;
|
|
#else
|
|
TCHAR szPath[MAX_PATH];
|
|
SHUnicodeToTChar(pwszPath, szPath, ARRAYSIZE(szPath));
|
|
ptszPath = szPath;
|
|
#endif
|
|
|
|
return _ProcessFoundFile(ptszPath, pwfd);
|
|
}
|
|
|
|
|
|
// IShellTreeWalkerCallBack::EnterFolder
|
|
|
|
HRESULT CLinkResolveTreeWalkerCB::EnterFolder(LPCWSTR pwszPath, LPTREEWALKERSTATS ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
ASSERT(IS_VALID_STRING_PTRW(pwszPath, -1));
|
|
ASSERT(pwfd);
|
|
|
|
|
|
// Respond quickly to the Cancel button.
|
|
|
|
if (!_prs->bContinue)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
LPCTSTR ptszPath;
|
|
#ifdef UNICODE
|
|
ptszPath = pwszPath;
|
|
#else
|
|
TCHAR szPath[MAX_PATH];
|
|
SHUnicodeToTChar(pwszPath, szPath, ARRAYSIZE(szPath));
|
|
ptszPath = szPath;
|
|
#endif
|
|
|
|
// Once we enter a directory, we lose the "we are still in the starting
|
|
// folder" bonus.
|
|
_iFolderBonus = 0;
|
|
|
|
// If we're about to enter a directory we've already looked in,
|
|
// or if this is a dead directory, then skip it.
|
|
if (PathIsPrefix(ptszPath, _prs->pszSearchOrigin) || IsFileInBitBucket(ptszPath))
|
|
return S_FALSE;
|
|
|
|
// If our target was a folder, treat this folder as a file found
|
|
if (_prs->dwMatch & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
hres = _ProcessFoundFile(ptszPath, pwfd);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
// Wrapper around WalkTree
|
|
|
|
BOOL CLinkResolveTreeWalkerCB::SearchInFolder(LPCTSTR pszFolder, int cLevels)
|
|
{
|
|
LPCWSTR pwszFolder;
|
|
#ifdef UNICODE
|
|
pwszFolder = pszFolder;
|
|
#else
|
|
WCHAR wszFolder[MAX_PATH];
|
|
SHTCharToUnicode(pszFolder, wszFolder, ARRAYSIZE(wszFolder));
|
|
pwszFolder = wszFolder;
|
|
#endif
|
|
|
|
int iMaxDepth = 0;
|
|
|
|
// cLevels == -1 means inifinite depth
|
|
if (cLevels != -1)
|
|
{
|
|
_dwSearchFlags |= WT_MAXDEPTH;
|
|
iMaxDepth = cLevels;
|
|
}
|
|
else
|
|
_dwSearchFlags &= ~WT_MAXDEPTH;
|
|
|
|
// Our folder bonus code lies on the fact that files in the
|
|
// starting folder come before anything else.
|
|
ASSERT(!(_dwSearchFlags & WT_FOLDERFIRST));
|
|
|
|
#ifdef DEBUG
|
|
// Test the QueryInterface code path by QI'ing ourselves for IUnknown and
|
|
// making sure we get ourselves back. We have to use IUnknown because
|
|
// anything else could get usurped into a tear-off by QIStub.
|
|
IUnknown *punkSelf;
|
|
EVAL(SUCCEEDED(this->QueryInterface(IID_IShellTreeWalkerCallBack, (LPVOID *)&punkSelf)));
|
|
ASSERT(punkSelf == SAFECAST(this, IUnknown *));
|
|
punkSelf->Release();
|
|
#endif
|
|
|
|
ASSERT(IS_VALID_CODE_PTR(_pstw, IShellTreeWalker));
|
|
_pstw->WalkTree(_dwSearchFlags, pwszFolder, _pwszSearchSpec, iMaxDepth, SAFECAST(this, IShellTreeWalkerCallBack *));
|
|
_iFolderBonus = 0; // You only get one chance at the folder bonus
|
|
return _prs->bContinue;
|
|
}
|
|
|
|
|
|
// Main search function for link resolution, the result will be in prs->fdFound.cFileName
|
|
|
|
VOID DoDownLevelSearch(RESOLVE_SEARCH_DATA *prs, LPTSTR pszFolderPath, int iStopScore)
|
|
{
|
|
|
|
// Skip this search if it's restricted
|
|
|
|
|
|
if (SHRestricted(REST_NORESOLVESEARCH)
|
|
||
|
|
SLR_NOSEARCH & prs->uFlags )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CLinkResolveTreeWalkerCB * plrtwcb = new CLinkResolveTreeWalkerCB(prs, iStopScore);
|
|
if (plrtwcb)
|
|
{
|
|
if (SUCCEEDED(plrtwcb->Initialize()))
|
|
{
|
|
int cUp = LNKTRACK_HINTED_UPLEVELS;
|
|
LPITEMIDLIST pidl;
|
|
BOOL bSearchOrigin = TRUE;
|
|
TCHAR szRealSearchOrigin[MAX_PATH];
|
|
|
|
|
|
// search up from old location
|
|
|
|
|
|
// In the olden days pszSearchOriginFirst was verified to be a valid directory
|
|
// (ie it returned TRUE to PathIsDirectory) and DoDownLevelSearch was never called
|
|
// if this was not true. Alas, this is no more. Why not search the desktop and
|
|
// fixed drives anyway? In the interest of saving some time the check that used
|
|
// to be in FindInFolder which caused an early out is now here instead. The rub
|
|
// is that we only skip the downlevel search of the original volume instead of
|
|
// skipping the entire link resolution phase.
|
|
lstrcpy(szRealSearchOrigin, prs->pszSearchOriginFirst);
|
|
while (!PathIsDirectory(szRealSearchOrigin))
|
|
{
|
|
if (PathIsRoot(szRealSearchOrigin) || !PathRemoveFileSpec(szRealSearchOrigin))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("root path does not exists %s"), szRealSearchOrigin);
|
|
bSearchOrigin = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bSearchOrigin )
|
|
{
|
|
lstrcpy(pszFolderPath, szRealSearchOrigin);
|
|
prs->pszSearchOrigin = szRealSearchOrigin;
|
|
|
|
// Files found in the starting folder get a slight bonus.
|
|
// _iFolderBonus is set to zero by
|
|
// CLinkResolveTreeWalkerCB::EnterFolder when we leave
|
|
// the starting folder and enter a new one.
|
|
plrtwcb->_iFolderBonus = 2;
|
|
|
|
while (cUp-- != 0 && plrtwcb->SearchInFolder(pszFolderPath, LNKTRACK_HINTED_DOWNLEVELS))
|
|
{
|
|
if (PathIsRoot(pszFolderPath) || !PathRemoveFileSpec(pszFolderPath))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prs->bContinue)
|
|
{
|
|
|
|
// search down from desktop
|
|
|
|
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
|
|
if (hr == NOERROR)
|
|
{
|
|
if (SHGetPathFromIDList(pidl, pszFolderPath))
|
|
{
|
|
prs->pszSearchOrigin = pszFolderPath;
|
|
plrtwcb->SearchInFolder(pszFolderPath, LNKTRACK_DESKTOP_DOWNLEVELS);
|
|
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
if (prs->bContinue)
|
|
{
|
|
|
|
// search down from root of fixed drives
|
|
|
|
TCHAR atch[4];
|
|
|
|
// BUGBUG: Is it valid to assume that no fixed drive can have a letter
|
|
// less than c:? I say no, I remember a PC98 machine that had b: as
|
|
// it's hard drive letter. Research the effects of making this start
|
|
// at a: (or using GetLogicalDriveStrings?)
|
|
lstrcpy(atch, TEXT("A:\\"));
|
|
prs->pszSearchOrigin = atch;
|
|
|
|
for (; prs->bContinue && atch[0] <= TEXT('Z'); atch[0]++)
|
|
{
|
|
if (GetDriveType(atch) == DRIVE_FIXED)
|
|
{
|
|
lstrcpy(pszFolderPath, atch);
|
|
plrtwcb->SearchInFolder(pszFolderPath, LNKTRACK_ROOT_DOWNLEVELS);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prs->bContinue && bSearchOrigin)
|
|
{
|
|
|
|
// resume search of last volume (should do an exclude list)
|
|
|
|
|
|
lstrcpy(pszFolderPath, szRealSearchOrigin);
|
|
prs->pszSearchOrigin = szRealSearchOrigin;
|
|
|
|
while (plrtwcb->SearchInFolder(pszFolderPath, -1))
|
|
{
|
|
if (PathIsRoot(pszFolderPath) || !PathRemoveFileSpec(pszFolderPath))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
plrtwcb->Release();
|
|
}
|
|
}
|