405 lines
13 KiB
C++
405 lines
13 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// File: slowfind.cpp
|
|
//
|
|
// Implements CProgFilesAppFinder
|
|
// CStartMenuAppFinder
|
|
// History:
|
|
// 3-01-98 by dli implemented CProgFilesAppFinder
|
|
// 4-15-98 by dli implemented CStartMenuAppFinder
|
|
//------------------------------------------------------------------------
|
|
#include "priv.h"
|
|
|
|
// Do not build this file if on Win9X or NT4
|
|
#ifndef DOWNLEVEL_PLATFORM
|
|
|
|
#include "appsize.h"
|
|
#include "findapp.h"
|
|
#include "slowfind.h"
|
|
|
|
|
|
// Todo: Remember the find result somewhere in the registry or cache it in code
|
|
// so that we don't waste time repeatedly computing it.
|
|
|
|
//
|
|
// App Folder Finder tree walker callback class
|
|
//
|
|
class CProgFilesAppFinder : public CAppFolderSize
|
|
{
|
|
friend BOOL SlowFindAppFolder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder);
|
|
public:
|
|
CProgFilesAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, BOOL * pfFound, LPTSTR pszFolder);
|
|
|
|
// *** IShellTreeWalkerCallBack methods ***
|
|
virtual STDMETHODIMP EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
|
|
|
|
HRESULT SearchInFolder(LPCTSTR pszStart);
|
|
void SetRootSearch(BOOL bRootSearch);
|
|
protected:
|
|
|
|
LPCTSTR _pszFullName;
|
|
LPCTSTR _pszShortName;
|
|
|
|
// The Result
|
|
LPTSTR _pszFolder;
|
|
|
|
// Best match found
|
|
int _iBest;
|
|
int _iCurDepth;
|
|
|
|
// found it or not?
|
|
BOOL * _pfFound;
|
|
|
|
// are we searching from root dirs like c:?
|
|
BOOL _fRootSearch;
|
|
|
|
// system directory used by the root search
|
|
TCHAR _szSystemDir[MAX_PATH];
|
|
};
|
|
|
|
CProgFilesAppFinder::CProgFilesAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, BOOL * pfFound, LPTSTR pszFolder) :
|
|
CAppFolderSize(NULL), _pszFullName(pszFullName), _pszShortName(pszShortName), _pfFound(pfFound), _pszFolder(pszFolder)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
|
|
|
|
ASSERT(pfFound);
|
|
ASSERT(*pfFound == FALSE);
|
|
ASSERT(_fRootSearch == FALSE);
|
|
}
|
|
|
|
|
|
void CProgFilesAppFinder::SetRootSearch(BOOL bRootSearch)
|
|
{
|
|
_fRootSearch = bRootSearch;
|
|
GetSystemDirectory(_szSystemDir, ARRAYSIZE(_szSystemDir));
|
|
}
|
|
|
|
//
|
|
// IShellTreeWalkerCallBack::EnterFolder
|
|
//
|
|
HRESULT CProgFilesAppFinder::EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
TCHAR szFolder[MAX_PATH];
|
|
|
|
ASSERT(IS_VALID_STRING_PTRW(pwszFolder, -1));
|
|
|
|
#ifdef UNICODE
|
|
lstrcpy(szFolder, pwszFolder);
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
pwszFolder, -1,
|
|
szFolder, ARRAYSIZE(szFolder), NULL, NULL);
|
|
#endif
|
|
|
|
TraceMsg(TF_SLOWFIND, "Enter Folder: %s -- %s Depth %d", _pszFullName, szFolder, ptws->nDepth);
|
|
|
|
LPTSTR pszName = PathFindFileName(szFolder);
|
|
|
|
// Don't go into common files or where we already have seen
|
|
// FEATURE: These should be in the registry.
|
|
if (_fRootSearch)
|
|
{
|
|
if (!lstrcmpi(pszName, TEXT("Program Files")) || !lstrcmpi(pszName, TEXT("Windows")) ||
|
|
!lstrcmpi(pszName, TEXT("Temp")) || !lstrcmpi(pszName, TEXT("Users")) || StrStrI(pszName, TEXT("WINNT")) ||
|
|
!lstrcmpi(_szSystemDir, szFolder))
|
|
return S_FALSE;
|
|
}
|
|
else if (!lstrcmpi(pszName, TEXT("Common Files")) || !lstrcmpi(pszName, TEXT("Windows NT"))
|
|
|| !lstrcmpi(pszName, TEXT("Plus!")) || !lstrcmpi(pszName, TEXT("Uninstall Information")))
|
|
return S_FALSE;
|
|
|
|
if (pszName)
|
|
{
|
|
int iMatch = MatchAppName(pszName, _pszFullName, _pszShortName, TRUE);
|
|
|
|
// The deeper the match folder is down the tree, the better a match
|
|
// it is.
|
|
if ((iMatch > _iBest) || ((iMatch > 0) && (ptws->nDepth > _iCurDepth)))
|
|
{
|
|
_iBest = iMatch;
|
|
_iCurDepth = ptws->nDepth;
|
|
|
|
TraceMsg(TF_SLOWFIND, "Slow Match Found: %s -- %s Depth %d", _pszFullName, szFolder, _iCurDepth);
|
|
ASSERT(IS_VALID_STRING_PTR(_pszFolder, -1));
|
|
lstrcpy(_pszFolder, szFolder);
|
|
|
|
if (iMatch == MATCH_LEVEL_HIGH)
|
|
{
|
|
*_pfFound = TRUE;
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
hres = CAppFolderSize::EnterFolder(pwszFolder, ptws, pwfd);
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Wrapper around WalkTree
|
|
//
|
|
HRESULT CProgFilesAppFinder::SearchInFolder(LPCTSTR pszStart)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
WCHAR wszDir[MAX_PATH];
|
|
DWORD dwSearchFlags = WT_MAXDEPTH | WT_NOTIFYFOLDERENTER | WT_FOLDERONLY;
|
|
|
|
SHTCharToUnicode(pszStart, wszDir, SIZECHARS(wszDir));
|
|
|
|
if (_pstw)
|
|
hres = _pstw->WalkTree(dwSearchFlags, wszDir, NULL, MAX_PROGFILES_SEARCH_DEPTH, SAFECAST(this, IShellTreeWalkerCallBack *));
|
|
|
|
return hres;
|
|
}
|
|
|
|
CStartMenuAppFinder::CStartMenuAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder) :
|
|
CAppFolderSize(NULL), _pszFullName(pszFullName), _pszShortName(pszShortName), _pszFolder(pszFolder)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
|
|
|
|
}
|
|
|
|
//
|
|
// get the target of a shortcut.
|
|
//
|
|
// NOTE: pszPath is WCHAR string
|
|
//
|
|
HRESULT GetShortcutTarget(LPCWSTR pszLinkPath, LPTSTR pszTargetPath, UINT cchTargetPath)
|
|
{
|
|
IShellLink* psl;
|
|
HRESULT hres = E_FAIL;
|
|
HRESULT hresT = LoadFromFile(CLSID_ShellLink, pszLinkPath, IID_PPV_ARG(IShellLink, &psl));
|
|
if (SUCCEEDED(hresT))
|
|
{
|
|
IShellLinkDataList* psldl;
|
|
hresT = psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psldl));
|
|
if (SUCCEEDED(hresT))
|
|
{
|
|
EXP_DARWIN_LINK* pexpDarwin;
|
|
BOOL bDarwin = FALSE;
|
|
hresT = psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin);
|
|
if (SUCCEEDED(hresT))
|
|
{
|
|
// This is a darwin link, so we return S_FALSE here.
|
|
LocalFree(pexpDarwin);
|
|
bDarwin = TRUE;
|
|
}
|
|
|
|
hresT = psl->GetPath(pszTargetPath, cchTargetPath, NULL, NULL);
|
|
if (hresT == S_OK)
|
|
{
|
|
// Return S_FALSE for the darwin apps.
|
|
hres = bDarwin ? S_FALSE : hresT;
|
|
}
|
|
|
|
psldl->Release();
|
|
}
|
|
psl->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// IShellTreeWalkerCallBack::EnterFolder
|
|
//
|
|
HRESULT CStartMenuAppFinder::EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
TCHAR szFolder[MAX_PATH];
|
|
|
|
ASSERT(IS_VALID_STRING_PTRW(pwszFolder, -1));
|
|
|
|
SHUnicodeToTChar(pwszFolder, szFolder, SIZECHARS(szFolder));
|
|
|
|
LPTSTR pszName = PathFindFileName(szFolder);
|
|
|
|
// Skip menus like the "Administrative Tools" and the "Accessories"
|
|
// FEATURE (scotth): these strings should be resourced-based
|
|
if (FindSubWord(pszName, TEXT("Administrative")) ||
|
|
FindSubWord(pszName, TEXT("Accessories")))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
return CAppFolderSize::EnterFolder(pwszFolder, ptws, pwfd);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Checks if the given shortcut filename closely matches this
|
|
app's fullname or shortname. Returns TRUE if it does.
|
|
*/
|
|
BOOL CStartMenuAppFinder::_MatchSMLinkWithApp(LPCTSTR pszLnkFile)
|
|
{
|
|
TCHAR szLnkFile[MAX_PATH];
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszLnkFile, -1));
|
|
|
|
lstrcpyn(szLnkFile, pszLnkFile, SIZECHARS(szLnkFile));
|
|
LPTSTR pszFileName = PathFindFileName(szLnkFile);
|
|
PathRemoveExtension(pszFileName);
|
|
|
|
if (MATCH_LEVEL_NORMAL <= MatchAppName(pszFileName, _pszFullName, _pszShortName, FALSE))
|
|
return TRUE;
|
|
|
|
PathRemoveFileSpec(szLnkFile);
|
|
LPTSTR pszDirName = PathFindFileName(szLnkFile);
|
|
if (MatchAppName(pszFileName, _pszFullName, _pszShortName, FALSE) >= MATCH_LEVEL_NORMAL)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// IShellTreeWalkerCallBack::FoundFile
|
|
//
|
|
HRESULT CStartMenuAppFinder::FoundFile(LPCWSTR pwszFile, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
TCHAR szLnkFile[MAX_PATH];
|
|
|
|
ASSERT(IS_VALID_STRING_PTRW(pwszFile, -1));
|
|
|
|
SHUnicodeToTChar(pwszFile, szLnkFile, ARRAYSIZE(szLnkFile));
|
|
TraceMsg(TF_SLOWFIND, "CSMAF:Lnk %s -- %s %s", _pszFullName, szLnkFile);
|
|
|
|
if (!_MatchSMLinkWithApp(szLnkFile))
|
|
return S_FALSE;
|
|
|
|
|
|
TCHAR szTargetFile[MAX_PATH];
|
|
HRESULT hresT = GetShortcutTarget(pwszFile, szTargetFile, ARRAYSIZE(szTargetFile));
|
|
if (hresT == S_OK)
|
|
{
|
|
if(!PathIsRoot(szTargetFile) && !PathIsUnderWindows(szTargetFile) && !PathIsSetup(szTargetFile, 3)
|
|
&& !PathIsCommonFiles(szTargetFile))
|
|
{
|
|
TraceMsg(TF_SLOWFIND, "CSMAF:Target %s -- %s %s", _pszFullName, szTargetFile);
|
|
PathRemoveFileSpec(szTargetFile);
|
|
if (!PathIsRoot(szTargetFile))
|
|
{
|
|
int iMatch = FindBestMatch(szTargetFile, _pszFullName, _pszShortName, FALSE, _pszFolder);
|
|
// The deeper the match folder is down the tree, the better a match
|
|
// it is.
|
|
if (iMatch > _iBest)
|
|
{
|
|
_iBest = iMatch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(_pszFolder, -1));
|
|
ASSERT(PathIsPrefix(_pszFolder, szTargetFile));
|
|
TraceMsg(TF_SLOWFIND, "CSMAF: Slow Match Found: %s -- %s", _pszFullName, szLnkFile);
|
|
|
|
if (iMatch == MATCH_LEVEL_HIGH)
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
hres = CAppFolderSize::FoundFile(pwszFile, ptws, pwfd);
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Wrapper around WalkTree
|
|
//
|
|
HRESULT CStartMenuAppFinder::SearchInFolder(LPCTSTR pszStart)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
DWORD dwSearchFlags = WT_MAXDEPTH | WT_NOTIFYFOLDERENTER | WT_FOLDERFIRST;
|
|
|
|
if (_pstw)
|
|
hres = _pstw->WalkTree(dwSearchFlags, pszStart, L"*.lnk", MAX_STARTMENU_SEARCH_DEPTH, SAFECAST(this, IShellTreeWalkerCallBack *));
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// NOTE: assuming pszFolder was allocated MAX_PATH long
|
|
// pszFolder will contain the result as return
|
|
//
|
|
BOOL SlowFindAppFolder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
|
|
|
|
int iMatch = MATCH_LEVEL_NOMATCH;
|
|
|
|
// Search from the start menu
|
|
CStartMenuAppFinder * psmaf = new CStartMenuAppFinder(pszFullName, pszShortName, pszFolder);
|
|
if (psmaf)
|
|
{
|
|
if (SUCCEEDED(psmaf->Initialize()))
|
|
{
|
|
TCHAR szStartMenu[MAX_PATH];
|
|
if (SHGetSpecialFolderPath(NULL, szStartMenu, CSIDL_COMMON_STARTMENU, FALSE))
|
|
psmaf->SearchInFolder(szStartMenu);
|
|
|
|
if ((psmaf->_iBest == MATCH_LEVEL_NOMATCH) && (SHGetSpecialFolderPath(NULL, szStartMenu, CSIDL_STARTMENU, FALSE)))
|
|
psmaf->SearchInFolder(szStartMenu);
|
|
|
|
iMatch = psmaf->_iBest;
|
|
}
|
|
psmaf->Release();
|
|
}
|
|
|
|
if (iMatch == MATCH_LEVEL_NOMATCH)
|
|
{
|
|
BOOL fFound = FALSE;
|
|
|
|
// Start searching from stratch, no hints on where to start what so ever
|
|
CProgFilesAppFinder * psaff = new CProgFilesAppFinder(pszFullName, pszShortName, &fFound, pszFolder);
|
|
if (psaff)
|
|
{
|
|
if (SUCCEEDED(psaff->Initialize()))
|
|
{
|
|
// search down from "Program Files" directory under root of all fixed drives
|
|
TCHAR szDrive[4];
|
|
TCHAR szProgFiles[30];
|
|
lstrcpy(szDrive, TEXT("A:\\"));
|
|
lstrcpy(szProgFiles, TEXT("A:\\Program Files"));
|
|
for (; !fFound && szDrive[0] <= TEXT('Z'); szProgFiles[0]++, szDrive[0]++)
|
|
{
|
|
ASSERT(szDrive[0] == szProgFiles[0]);
|
|
if (GetDriveType(szDrive) == DRIVE_FIXED)
|
|
psaff->SearchInFolder(szProgFiles);
|
|
}
|
|
|
|
}
|
|
|
|
if (!fFound)
|
|
{
|
|
psaff->SetRootSearch(TRUE);
|
|
|
|
TCHAR szDrive[4];
|
|
lstrcpy(szDrive, TEXT("A:\\"));
|
|
for (; !fFound && szDrive[0] <= TEXT('Z'); szDrive[0]++)
|
|
{
|
|
if (GetDriveType(szDrive) == DRIVE_FIXED)
|
|
psaff->SearchInFolder(szDrive);
|
|
}
|
|
}
|
|
|
|
iMatch = psaff->_iBest;
|
|
psaff->Release();
|
|
}
|
|
|
|
if (iMatch > MATCH_LEVEL_NOMATCH)
|
|
TraceMsg(TF_ALWAYS, "CPFAF: Found %s at %s", pszFullName, pszFolder);
|
|
}
|
|
else
|
|
TraceMsg(TF_ALWAYS, "CSMAF: Found %s at %s", pszFullName, pszFolder);
|
|
|
|
return iMatch;
|
|
}
|
|
|
|
#endif //DOWNLEVEL_PLATFORM
|