Leaked source code of windows server 2003
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.

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