Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

404 lines
13 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // File: slowfind.cpp
  6. //
  7. // Implements CProgFilesAppFinder
  8. // CStartMenuAppFinder
  9. // History:
  10. // 3-01-98 by dli implemented CProgFilesAppFinder
  11. // 4-15-98 by dli implemented CStartMenuAppFinder
  12. //------------------------------------------------------------------------
  13. #include "priv.h"
  14. // Do not build this file if on Win9X or NT4
  15. #ifndef DOWNLEVEL_PLATFORM
  16. #include "appsize.h"
  17. #include "findapp.h"
  18. #include "slowfind.h"
  19. // Todo: Remember the find result somewhere in the registry or cache it in code
  20. // so that we don't waste time repeatedly computing it.
  21. //
  22. // App Folder Finder tree walker callback class
  23. //
  24. class CProgFilesAppFinder : public CAppFolderSize
  25. {
  26. friend BOOL SlowFindAppFolder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder);
  27. public:
  28. CProgFilesAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, BOOL * pfFound, LPTSTR pszFolder);
  29. // *** IShellTreeWalkerCallBack methods ***
  30. virtual STDMETHODIMP EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd);
  31. HRESULT SearchInFolder(LPCTSTR pszStart);
  32. void SetRootSearch(BOOL bRootSearch);
  33. protected:
  34. LPCTSTR _pszFullName;
  35. LPCTSTR _pszShortName;
  36. // The Result
  37. LPTSTR _pszFolder;
  38. // Best match found
  39. int _iBest;
  40. int _iCurDepth;
  41. // found it or not?
  42. BOOL * _pfFound;
  43. // are we searching from root dirs like c:?
  44. BOOL _fRootSearch;
  45. // system directory used by the root search
  46. TCHAR _szSystemDir[MAX_PATH];
  47. };
  48. CProgFilesAppFinder::CProgFilesAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, BOOL * pfFound, LPTSTR pszFolder) :
  49. CAppFolderSize(NULL), _pszFullName(pszFullName), _pszShortName(pszShortName), _pfFound(pfFound), _pszFolder(pszFolder)
  50. {
  51. ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
  52. ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
  53. ASSERT(pfFound);
  54. ASSERT(*pfFound == FALSE);
  55. ASSERT(_fRootSearch == FALSE);
  56. }
  57. void CProgFilesAppFinder::SetRootSearch(BOOL bRootSearch)
  58. {
  59. _fRootSearch = bRootSearch;
  60. GetSystemDirectory(_szSystemDir, ARRAYSIZE(_szSystemDir));
  61. }
  62. //
  63. // IShellTreeWalkerCallBack::EnterFolder
  64. //
  65. HRESULT CProgFilesAppFinder::EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
  66. {
  67. HRESULT hres = S_OK;
  68. TCHAR szFolder[MAX_PATH];
  69. ASSERT(IS_VALID_STRING_PTRW(pwszFolder, -1));
  70. #ifdef UNICODE
  71. lstrcpy(szFolder, pwszFolder);
  72. #else
  73. WideCharToMultiByte(CP_ACP, 0,
  74. pwszFolder, -1,
  75. szFolder, ARRAYSIZE(szFolder), NULL, NULL);
  76. #endif
  77. TraceMsg(TF_SLOWFIND, "Enter Folder: %s -- %s Depth %d", _pszFullName, szFolder, ptws->nDepth);
  78. LPTSTR pszName = PathFindFileName(szFolder);
  79. // Don't go into common files or where we already have seen
  80. // FEATURE: These should be in the registry.
  81. if (_fRootSearch)
  82. {
  83. if (!lstrcmpi(pszName, TEXT("Program Files")) || !lstrcmpi(pszName, TEXT("Windows")) ||
  84. !lstrcmpi(pszName, TEXT("Temp")) || !lstrcmpi(pszName, TEXT("Users")) || StrStrI(pszName, TEXT("WINNT")) ||
  85. !lstrcmpi(_szSystemDir, szFolder))
  86. return S_FALSE;
  87. }
  88. else if (!lstrcmpi(pszName, TEXT("Common Files")) || !lstrcmpi(pszName, TEXT("Windows NT"))
  89. || !lstrcmpi(pszName, TEXT("Plus!")) || !lstrcmpi(pszName, TEXT("Uninstall Information")))
  90. return S_FALSE;
  91. if (pszName)
  92. {
  93. int iMatch = MatchAppName(pszName, _pszFullName, _pszShortName, TRUE);
  94. // The deeper the match folder is down the tree, the better a match
  95. // it is.
  96. if ((iMatch > _iBest) || ((iMatch > 0) && (ptws->nDepth > _iCurDepth)))
  97. {
  98. _iBest = iMatch;
  99. _iCurDepth = ptws->nDepth;
  100. TraceMsg(TF_SLOWFIND, "Slow Match Found: %s -- %s Depth %d", _pszFullName, szFolder, _iCurDepth);
  101. ASSERT(IS_VALID_STRING_PTR(_pszFolder, -1));
  102. lstrcpy(_pszFolder, szFolder);
  103. if (iMatch == MATCH_LEVEL_HIGH)
  104. {
  105. *_pfFound = TRUE;
  106. hres = E_FAIL;
  107. }
  108. }
  109. }
  110. if (SUCCEEDED(hres))
  111. hres = CAppFolderSize::EnterFolder(pwszFolder, ptws, pwfd);
  112. return hres;
  113. }
  114. //
  115. // Wrapper around WalkTree
  116. //
  117. HRESULT CProgFilesAppFinder::SearchInFolder(LPCTSTR pszStart)
  118. {
  119. HRESULT hres = E_FAIL;
  120. WCHAR wszDir[MAX_PATH];
  121. DWORD dwSearchFlags = WT_MAXDEPTH | WT_NOTIFYFOLDERENTER | WT_FOLDERONLY;
  122. SHTCharToUnicode(pszStart, wszDir, SIZECHARS(wszDir));
  123. if (_pstw)
  124. hres = _pstw->WalkTree(dwSearchFlags, wszDir, NULL, MAX_PROGFILES_SEARCH_DEPTH, SAFECAST(this, IShellTreeWalkerCallBack *));
  125. return hres;
  126. }
  127. CStartMenuAppFinder::CStartMenuAppFinder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder) :
  128. CAppFolderSize(NULL), _pszFullName(pszFullName), _pszShortName(pszShortName), _pszFolder(pszFolder)
  129. {
  130. ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
  131. ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
  132. }
  133. //
  134. // get the target of a shortcut.
  135. //
  136. // NOTE: pszPath is WCHAR string
  137. //
  138. HRESULT GetShortcutTarget(LPCWSTR pszLinkPath, LPTSTR pszTargetPath, UINT cchTargetPath)
  139. {
  140. IShellLink* psl;
  141. HRESULT hres = E_FAIL;
  142. HRESULT hresT = LoadFromFile(CLSID_ShellLink, pszLinkPath, IID_PPV_ARG(IShellLink, &psl));
  143. if (SUCCEEDED(hresT))
  144. {
  145. IShellLinkDataList* psldl;
  146. hresT = psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psldl));
  147. if (SUCCEEDED(hresT))
  148. {
  149. EXP_DARWIN_LINK* pexpDarwin;
  150. BOOL bDarwin = FALSE;
  151. hresT = psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin);
  152. if (SUCCEEDED(hresT))
  153. {
  154. // This is a darwin link, so we return S_FALSE here.
  155. LocalFree(pexpDarwin);
  156. bDarwin = TRUE;
  157. }
  158. hresT = psl->GetPath(pszTargetPath, cchTargetPath, NULL, NULL);
  159. if (hresT == S_OK)
  160. {
  161. // Return S_FALSE for the darwin apps.
  162. hres = bDarwin ? S_FALSE : hresT;
  163. }
  164. psldl->Release();
  165. }
  166. psl->Release();
  167. }
  168. return hres;
  169. }
  170. //
  171. // IShellTreeWalkerCallBack::EnterFolder
  172. //
  173. HRESULT CStartMenuAppFinder::EnterFolder(LPCWSTR pwszFolder, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
  174. {
  175. TCHAR szFolder[MAX_PATH];
  176. ASSERT(IS_VALID_STRING_PTRW(pwszFolder, -1));
  177. SHUnicodeToTChar(pwszFolder, szFolder, SIZECHARS(szFolder));
  178. LPTSTR pszName = PathFindFileName(szFolder);
  179. // Skip menus like the "Administrative Tools" and the "Accessories"
  180. // FEATURE (scotth): these strings should be resourced-based
  181. if (FindSubWord(pszName, TEXT("Administrative")) ||
  182. FindSubWord(pszName, TEXT("Accessories")))
  183. {
  184. return S_FALSE;
  185. }
  186. return CAppFolderSize::EnterFolder(pwszFolder, ptws, pwfd);
  187. }
  188. /*-------------------------------------------------------------------------
  189. Purpose: Checks if the given shortcut filename closely matches this
  190. app's fullname or shortname. Returns TRUE if it does.
  191. */
  192. BOOL CStartMenuAppFinder::_MatchSMLinkWithApp(LPCTSTR pszLnkFile)
  193. {
  194. TCHAR szLnkFile[MAX_PATH];
  195. ASSERT(IS_VALID_STRING_PTR(pszLnkFile, -1));
  196. lstrcpyn(szLnkFile, pszLnkFile, SIZECHARS(szLnkFile));
  197. LPTSTR pszFileName = PathFindFileName(szLnkFile);
  198. PathRemoveExtension(pszFileName);
  199. if (MATCH_LEVEL_NORMAL <= MatchAppName(pszFileName, _pszFullName, _pszShortName, FALSE))
  200. return TRUE;
  201. PathRemoveFileSpec(szLnkFile);
  202. LPTSTR pszDirName = PathFindFileName(szLnkFile);
  203. if (MatchAppName(pszFileName, _pszFullName, _pszShortName, FALSE) >= MATCH_LEVEL_NORMAL)
  204. return TRUE;
  205. return FALSE;
  206. }
  207. //
  208. // IShellTreeWalkerCallBack::FoundFile
  209. //
  210. HRESULT CStartMenuAppFinder::FoundFile(LPCWSTR pwszFile, TREEWALKERSTATS *ptws, WIN32_FIND_DATAW * pwfd)
  211. {
  212. HRESULT hres = S_OK;
  213. TCHAR szLnkFile[MAX_PATH];
  214. ASSERT(IS_VALID_STRING_PTRW(pwszFile, -1));
  215. SHUnicodeToTChar(pwszFile, szLnkFile, ARRAYSIZE(szLnkFile));
  216. TraceMsg(TF_SLOWFIND, "CSMAF:Lnk %s -- %s %s", _pszFullName, szLnkFile);
  217. if (!_MatchSMLinkWithApp(szLnkFile))
  218. return S_FALSE;
  219. TCHAR szTargetFile[MAX_PATH];
  220. HRESULT hresT = GetShortcutTarget(pwszFile, szTargetFile, ARRAYSIZE(szTargetFile));
  221. if (hresT == S_OK)
  222. {
  223. if(!PathIsRoot(szTargetFile) && !PathIsUnderWindows(szTargetFile) && !PathIsSetup(szTargetFile, 3)
  224. && !PathIsCommonFiles(szTargetFile))
  225. {
  226. TraceMsg(TF_SLOWFIND, "CSMAF:Target %s -- %s %s", _pszFullName, szTargetFile);
  227. PathRemoveFileSpec(szTargetFile);
  228. if (!PathIsRoot(szTargetFile))
  229. {
  230. int iMatch = FindBestMatch(szTargetFile, _pszFullName, _pszShortName, FALSE, _pszFolder);
  231. // The deeper the match folder is down the tree, the better a match
  232. // it is.
  233. if (iMatch > _iBest)
  234. {
  235. _iBest = iMatch;
  236. ASSERT(IS_VALID_STRING_PTR(_pszFolder, -1));
  237. ASSERT(PathIsPrefix(_pszFolder, szTargetFile));
  238. TraceMsg(TF_SLOWFIND, "CSMAF: Slow Match Found: %s -- %s", _pszFullName, szLnkFile);
  239. if (iMatch == MATCH_LEVEL_HIGH)
  240. hres = E_FAIL;
  241. }
  242. }
  243. }
  244. }
  245. if (SUCCEEDED(hres))
  246. hres = CAppFolderSize::FoundFile(pwszFile, ptws, pwfd);
  247. return hres;
  248. }
  249. //
  250. // Wrapper around WalkTree
  251. //
  252. HRESULT CStartMenuAppFinder::SearchInFolder(LPCTSTR pszStart)
  253. {
  254. HRESULT hres = E_FAIL;
  255. DWORD dwSearchFlags = WT_MAXDEPTH | WT_NOTIFYFOLDERENTER | WT_FOLDERFIRST;
  256. if (_pstw)
  257. hres = _pstw->WalkTree(dwSearchFlags, pszStart, L"*.lnk", MAX_STARTMENU_SEARCH_DEPTH, SAFECAST(this, IShellTreeWalkerCallBack *));
  258. return hres;
  259. }
  260. //
  261. // NOTE: assuming pszFolder was allocated MAX_PATH long
  262. // pszFolder will contain the result as return
  263. //
  264. BOOL SlowFindAppFolder(LPCTSTR pszFullName, LPCTSTR pszShortName, LPTSTR pszFolder)
  265. {
  266. ASSERT(IS_VALID_STRING_PTR(pszFolder, -1));
  267. ASSERT(IS_VALID_STRING_PTR(pszFullName, -1) || IS_VALID_STRING_PTR(pszShortName, -1));
  268. int iMatch = MATCH_LEVEL_NOMATCH;
  269. // Search from the start menu
  270. CStartMenuAppFinder * psmaf = new CStartMenuAppFinder(pszFullName, pszShortName, pszFolder);
  271. if (psmaf)
  272. {
  273. if (SUCCEEDED(psmaf->Initialize()))
  274. {
  275. TCHAR szStartMenu[MAX_PATH];
  276. if (SHGetSpecialFolderPath(NULL, szStartMenu, CSIDL_COMMON_STARTMENU, FALSE))
  277. psmaf->SearchInFolder(szStartMenu);
  278. if ((psmaf->_iBest == MATCH_LEVEL_NOMATCH) && (SHGetSpecialFolderPath(NULL, szStartMenu, CSIDL_STARTMENU, FALSE)))
  279. psmaf->SearchInFolder(szStartMenu);
  280. iMatch = psmaf->_iBest;
  281. }
  282. psmaf->Release();
  283. }
  284. if (iMatch == MATCH_LEVEL_NOMATCH)
  285. {
  286. BOOL fFound = FALSE;
  287. // Start searching from stratch, no hints on where to start what so ever
  288. CProgFilesAppFinder * psaff = new CProgFilesAppFinder(pszFullName, pszShortName, &fFound, pszFolder);
  289. if (psaff)
  290. {
  291. if (SUCCEEDED(psaff->Initialize()))
  292. {
  293. // search down from "Program Files" directory under root of all fixed drives
  294. TCHAR szDrive[4];
  295. TCHAR szProgFiles[30];
  296. lstrcpy(szDrive, TEXT("A:\\"));
  297. lstrcpy(szProgFiles, TEXT("A:\\Program Files"));
  298. for (; !fFound && szDrive[0] <= TEXT('Z'); szProgFiles[0]++, szDrive[0]++)
  299. {
  300. ASSERT(szDrive[0] == szProgFiles[0]);
  301. if (GetDriveType(szDrive) == DRIVE_FIXED)
  302. psaff->SearchInFolder(szProgFiles);
  303. }
  304. }
  305. if (!fFound)
  306. {
  307. psaff->SetRootSearch(TRUE);
  308. TCHAR szDrive[4];
  309. lstrcpy(szDrive, TEXT("A:\\"));
  310. for (; !fFound && szDrive[0] <= TEXT('Z'); szDrive[0]++)
  311. {
  312. if (GetDriveType(szDrive) == DRIVE_FIXED)
  313. psaff->SearchInFolder(szDrive);
  314. }
  315. }
  316. iMatch = psaff->_iBest;
  317. psaff->Release();
  318. }
  319. if (iMatch > MATCH_LEVEL_NOMATCH)
  320. TraceMsg(TF_ALWAYS, "CPFAF: Found %s at %s", pszFullName, pszFolder);
  321. }
  322. else
  323. TraceMsg(TF_ALWAYS, "CSMAF: Found %s at %s", pszFullName, pszFolder);
  324. return iMatch;
  325. }
  326. #endif //DOWNLEVEL_PLATFORM