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.

446 lines
12 KiB

  1. // AppSearch
  2. //
  3. // Tool for searching a user's hard drives and locating
  4. // applications that can be patched, and downloading those
  5. // patches from Windows Update
  6. //
  7. // Author: t-michkr (9 June 2000)
  8. //
  9. // searchdb.cpp
  10. // Functions for searching the shim database.
  11. #include <windows.h>
  12. #include <assert.h>
  13. extern "C"
  14. {
  15. #include <shimdb.h>
  16. }
  17. #include "searchdb.h"
  18. #include "main.h"
  19. #include "resource.h"
  20. // Private message used internally to stop a search.
  21. const int WM_SEARCHDB_STOP = WM_SEARCHDB_BASE + 3;
  22. // Some globals needed by the shimdb library
  23. typedef PVOID (*PFNRTLALLOCATEHEAP)(PVOID, ULONG, SIZE_T);
  24. typedef BOOL (*PFNRTLFREEHEAP)(PVOID, ULONG, PVOID);
  25. extern "C"
  26. {
  27. PFNRTLALLOCATEHEAP g_pfnRtlAllocateHeap;
  28. PFNRTLFREEHEAP g_pfnRtlFreeHeap;
  29. PVOID g_pShimHeap;
  30. }
  31. HSDB g_hSDB;
  32. // Parameters to search thread
  33. struct SSearchThreadParam
  34. {
  35. PTSTR szPath;
  36. HWND hwCaller;
  37. ~SSearchThreadParam()
  38. {
  39. if(szPath)
  40. delete szPath;
  41. szPath = 0;
  42. }
  43. };
  44. // A node in the queue of matched EXE's.
  45. struct SMatchedExeQueueNode
  46. {
  47. SMatchedExe* pMatchedExe;
  48. SMatchedExeQueueNode* pNext;
  49. };
  50. // Our queue of matched EXE's
  51. static SMatchedExeQueueNode* g_pHead = 0, *g_pTail = 0;
  52. // Handle of the search thread
  53. static HANDLE g_hThread = 0;
  54. // The ID of the search thread
  55. static DWORD g_dwThreadID = 0;
  56. // Lock for mutual exclusion
  57. static CRITICAL_SECTION g_csLock;
  58. // Internal functions
  59. static BOOL AddQueueItem(SMatchedExe* pme);
  60. static BOOL SearchDirectory(PTSTR szDir, HWND hwCaller);
  61. static unsigned int __stdcall SearchThread(SSearchThreadParam* pstp);
  62. static BOOL NotifyExeFound(HWND hwnd, PCTSTR szAppName, PCTSTR szPath);
  63. // Add an exe to the queue of found exe's.
  64. BOOL AddQueueItem(SMatchedExe* pme)
  65. {
  66. assert(pme);
  67. SMatchedExeQueueNode* pNewNode = new SMatchedExeQueueNode;
  68. if(!pNewNode)
  69. {
  70. Error(0, IDS_NOMEMSTOPSEARCH);
  71. return FALSE;
  72. }
  73. pNewNode->pMatchedExe = pme;
  74. pNewNode->pNext = 0;
  75. EnterCriticalSection(&g_csLock);
  76. if(g_pHead == 0)
  77. g_pHead = g_pTail = pNewNode;
  78. else
  79. {
  80. g_pTail->pNext = pNewNode;
  81. g_pTail = pNewNode;
  82. }
  83. LeaveCriticalSection(&g_csLock);
  84. return TRUE;
  85. }
  86. // Return a single exe from the list, NULL if empty.
  87. SMatchedExe* GetMatchedExe()
  88. {
  89. SMatchedExe* pRet = 0;
  90. SMatchedExeQueueNode* pNewHead = 0;
  91. EnterCriticalSection(&g_csLock);
  92. if(g_pHead != NULL)
  93. {
  94. pRet = g_pHead->pMatchedExe;
  95. pNewHead = g_pHead->pNext;
  96. delete g_pHead;
  97. g_pHead = pNewHead;
  98. }
  99. LeaveCriticalSection(&g_csLock);
  100. return pRet;
  101. }
  102. // Called recursively, TRUE return means continue, FALSE
  103. // means terminate.
  104. BOOL SearchDirectory(PTSTR szPath, HWND hwCaller)
  105. {
  106. // Send an update to the caller window . . .
  107. TCHAR* szTemp = new TCHAR[lstrlen(szPath) + 1];
  108. if(!szTemp)
  109. {
  110. Error(0, IDS_NOMEMSTOPSEARCH);
  111. return FALSE;
  112. }
  113. lstrcpy(szTemp, szPath);
  114. PostMessage(hwCaller, WM_SEARCHDB_UPDATE, 0, reinterpret_cast<LPARAM>(szTemp));
  115. WIN32_FIND_DATA fileFindData;
  116. HANDLE hSearch;
  117. PTSTR szSearchPath;
  118. MSG msg;
  119. // Check if we've been told to terminate.
  120. if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  121. {
  122. if(msg.message == WM_SEARCHDB_STOP)
  123. return FALSE;
  124. }
  125. szSearchPath = new TCHAR[lstrlen(szPath)+lstrlen(TEXT("*.exe")) +1];
  126. if(!szSearchPath)
  127. {
  128. Error(0, IDS_NOMEMSTOPSEARCH);
  129. return FALSE;
  130. }
  131. wsprintf(szSearchPath, TEXT("%s*.exe"), szPath);
  132. hSearch = FindFirstFile(szSearchPath, &fileFindData);
  133. delete szSearchPath;
  134. szSearchPath = 0;
  135. if(hSearch != INVALID_HANDLE_VALUE)
  136. {
  137. do
  138. {
  139. if(!(fileFindData.dwFileAttributes &
  140. (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN
  141. | FILE_ATTRIBUTE_DIRECTORY)))
  142. {
  143. PTSTR szExePath;
  144. szExePath = new TCHAR[lstrlen(szPath)+
  145. lstrlen(fileFindData.cFileName)+1];
  146. if(!szExePath)
  147. {
  148. Error(0, IDS_NOMEMSTOPSEARCH);
  149. return FALSE;
  150. }
  151. wsprintf(szExePath, TEXT("%s%s"), szPath,
  152. fileFindData.cFileName);
  153. SDBQUERYRESULT sdbQuery;
  154. ZeroMemory(&sdbQuery, sizeof(SDBQUERYRESULT));
  155. SdbGetMatchingExe(g_hSDB, szExePath, NULL, NULL, SDBGMEF_IGNORE_ENVIRONMENT, &sdbQuery);
  156. if(sdbQuery.atrExes[0] != TAGREF_NULL)
  157. {
  158. DWORD dwNumExes;
  159. //
  160. // count the exes
  161. //
  162. for (dwNumExes = 0; dwNumExes < SDB_MAX_EXES; ++dwNumExes) {
  163. if (sdbQuery.atrExes[dwNumExes] == TAGREF_NULL) {
  164. break;
  165. }
  166. }
  167. //
  168. // for now, just get the info for the last exe in the list, which will
  169. // be the one with specific info for this app.
  170. // BUGBUG -- is this the right thing to do? dmunsil
  171. //
  172. TAGREF trExe = sdbQuery.atrExes[dwNumExes - 1];
  173. TCHAR szAppName[c_nMaxStringLength] = TEXT("");
  174. TAGREF trAppName = SdbFindFirstTagRef(g_hSDB, trExe, TAG_APP_NAME);
  175. if(trAppName == TAGREF_NULL)
  176. wsprintf(szAppName, TEXT("%s%s"), szPath, fileFindData.cFileName);
  177. else
  178. SdbReadStringTagRef(g_hSDB, trAppName, szAppName, c_nMaxStringLength);
  179. NotifyExeFound(hwCaller, szAppName, szExePath);
  180. SdbReleaseMatchingExe(g_hSDB, trExe);
  181. }
  182. delete szExePath;
  183. szExePath = 0;
  184. }
  185. } while(FindNextFile(hSearch, &fileFindData));
  186. FindClose(hSearch);
  187. }
  188. // Recurse into subdirectories
  189. // Unsure how to do attribute based search, let's just
  190. // look for everything and take note of directories.
  191. szSearchPath = new TCHAR[lstrlen(szPath)+2];
  192. if(!szSearchPath)
  193. {
  194. Error(0, IDS_NOMEMSTOPSEARCH);
  195. return FALSE;
  196. }
  197. wsprintf(szSearchPath, TEXT("%s*"), szPath);
  198. hSearch = FindFirstFile(szSearchPath, &fileFindData);
  199. delete szSearchPath;
  200. szSearchPath = 0;
  201. if(hSearch != INVALID_HANDLE_VALUE)
  202. {
  203. do
  204. {
  205. if(fileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  206. {
  207. // Don't do an infinite directory recursion
  208. if( !((lstrcmp(fileFindData.cFileName, TEXT(".")) == 0) ||
  209. (lstrcmp(fileFindData.cFileName, TEXT("..")) == 0)))
  210. {
  211. szSearchPath = new TCHAR[lstrlen(szPath)+
  212. lstrlen(fileFindData.cFileName)+2];
  213. if(!szSearchPath)
  214. {
  215. Error(0, IDS_NOMEMSTOPSEARCH);
  216. return FALSE;
  217. }
  218. wsprintf(szSearchPath, TEXT("%s%s\\"), szPath,
  219. fileFindData.cFileName);
  220. if(SearchDirectory(szSearchPath, hwCaller) == FALSE)
  221. {
  222. FindClose(hSearch);
  223. delete szSearchPath;
  224. return FALSE;
  225. }
  226. delete szSearchPath;
  227. szSearchPath = 0;
  228. }
  229. }
  230. } while(FindNextFile(hSearch, &fileFindData));
  231. FindClose(hSearch);
  232. }
  233. return TRUE;
  234. }
  235. // Wrapper thread for database search.
  236. unsigned int __stdcall SearchThread(SSearchThreadParam* pstp)
  237. {
  238. if((g_hSDB = SdbInitDatabase(0, NULL)) == NULL)
  239. return 0;
  240. // If NULL is passed in, just search all user drives.
  241. if(pstp->szPath == 0)
  242. {
  243. DWORD dwLength = GetLogicalDriveStrings(0,0);
  244. TCHAR* szDrives = new TCHAR[dwLength+1];
  245. if(szDrives)
  246. {
  247. GetLogicalDriveStrings(dwLength, szDrives);
  248. TCHAR* szCurrDrive = szDrives;
  249. while(*szCurrDrive)
  250. {
  251. if(GetDriveType(szCurrDrive) == DRIVE_FIXED)
  252. if(!SearchDirectory(szCurrDrive, pstp->hwCaller))
  253. break;
  254. szCurrDrive += lstrlen(szCurrDrive) + 1;
  255. }
  256. }
  257. else
  258. Error(pstp->hwCaller, IDS_NOMEMSTOPSEARCH);
  259. }
  260. else
  261. SearchDirectory(pstp->szPath, pstp->hwCaller);
  262. // Notify caller that search is complete.
  263. PostMessage(pstp->hwCaller, WM_SEARCHDB_DONE, 0, 0);
  264. delete pstp;
  265. SdbReleaseDatabase(g_hSDB);
  266. return 0;
  267. }
  268. // Notify caller that an exe has been found.
  269. BOOL NotifyExeFound(HWND hwnd, PCTSTR szAppName, PCTSTR szPath)
  270. {
  271. SMatchedExe* pme = 0;
  272. pme = new SMatchedExe;
  273. if(!pme)
  274. {
  275. Error(0, IDS_NOMEMSTOPSEARCH);
  276. return FALSE;
  277. }
  278. pme->szAppName = new TCHAR[lstrlen(szAppName)+1];
  279. if(!pme->szAppName)
  280. {
  281. Error(0, IDS_NOMEMSTOPSEARCH);
  282. return FALSE;
  283. }
  284. lstrcpy(pme->szAppName, szAppName);
  285. pme->szPath = new TCHAR[lstrlen(szPath)+1];
  286. if(!pme->szPath)
  287. {
  288. Error(0, IDS_NOMEMSTOPSEARCH);
  289. return FALSE;
  290. }
  291. lstrcpy(pme->szPath, szPath);
  292. // Add to the queue of found exe's
  293. if(!AddQueueItem(pme))
  294. return FALSE;
  295. // Notify the caller.
  296. PostMessage(hwnd, WM_SEARCHDB_ADDAPP, 0, 0);
  297. return TRUE;
  298. }
  299. // SearchDB()
  300. // Will search a user's hard drives looking for applications
  301. // that have entries in the AppCompat database.
  302. // szDrives is a string similar in format to
  303. // the string returned by GetLogicalDriveStrings()
  304. // If 0 is passed, it will call GetLogicalDriveStrings()
  305. // and parse all hard drives.
  306. // Messages will be posted to the caller window
  307. // in response to events
  308. BOOL SearchDB(PCTSTR szPath, HWND hwCaller)
  309. {
  310. SSearchThreadParam* p = new SSearchThreadParam;
  311. if(!p)
  312. {
  313. Error(hwCaller, IDS_NOMEMSTOPSEARCH);
  314. return FALSE;
  315. }
  316. p->hwCaller = hwCaller;
  317. if(szPath)
  318. {
  319. p->szPath = new TCHAR[lstrlen(szPath)+1];
  320. if(!p->szPath)
  321. {
  322. Error(hwCaller, IDS_NOMEMSTOPSEARCH);
  323. return FALSE;
  324. }
  325. lstrcpy(p->szPath, szPath);
  326. }
  327. else
  328. p->szPath = NULL;
  329. if(g_hThread)
  330. StopSearchDB();
  331. g_hThread = CreateThread(0, 0,
  332. reinterpret_cast<LPTHREAD_START_ROUTINE>(SearchThread), p,
  333. 0, &g_dwThreadID);
  334. if(g_hThread == NULL)
  335. return FALSE;
  336. return TRUE;
  337. }
  338. // Terminate the search.
  339. void StopSearchDB()
  340. {
  341. // As long as the thread is active
  342. if(g_hThread && (WaitForSingleObject(g_hThread, 0) != WAIT_OBJECT_0))
  343. {
  344. // Post in a while to ensure that the message queue was
  345. // created.
  346. while((PostThreadMessage(g_dwThreadID, WM_SEARCHDB_STOP, 0, 0)==FALSE)
  347. && (GetLastError() != ERROR_INVALID_THREAD_ID))
  348. Sleep(0);
  349. if(GetLastError() != ERROR_INVALID_THREAD_ID)
  350. WaitForSingleObject(g_hThread, INFINITE);
  351. }
  352. if(g_hThread)
  353. CloseHandle(g_hThread);
  354. g_hThread = 0;
  355. }
  356. // Setup all globals needed.
  357. BOOL InitSearchDB()
  358. {
  359. InitializeCriticalSection(&g_csLock);
  360. g_pShimHeap = GetProcessHeap();
  361. g_pfnRtlAllocateHeap = HeapAlloc;
  362. g_pfnRtlFreeHeap = HeapFree;
  363. return TRUE;
  364. }