// AppSearch // // Tool for searching a user's hard drives and locating // applications that can be patched, and downloading those // patches from Windows Update // // Author: t-michkr (9 June 2000) // // searchdb.cpp // Functions for searching the shim database. #include #include extern "C" { #include } #include "searchdb.h" #include "main.h" #include "resource.h" // Private message used internally to stop a search. const int WM_SEARCHDB_STOP = WM_SEARCHDB_BASE + 3; // Some globals needed by the shimdb library typedef PVOID (*PFNRTLALLOCATEHEAP)(PVOID, ULONG, SIZE_T); typedef BOOL (*PFNRTLFREEHEAP)(PVOID, ULONG, PVOID); extern "C" { PFNRTLALLOCATEHEAP g_pfnRtlAllocateHeap; PFNRTLFREEHEAP g_pfnRtlFreeHeap; PVOID g_pShimHeap; } HSDB g_hSDB; // Parameters to search thread struct SSearchThreadParam { PTSTR szPath; HWND hwCaller; ~SSearchThreadParam() { if(szPath) delete szPath; szPath = 0; } }; // A node in the queue of matched EXE's. struct SMatchedExeQueueNode { SMatchedExe* pMatchedExe; SMatchedExeQueueNode* pNext; }; // Our queue of matched EXE's static SMatchedExeQueueNode* g_pHead = 0, *g_pTail = 0; // Handle of the search thread static HANDLE g_hThread = 0; // The ID of the search thread static DWORD g_dwThreadID = 0; // Lock for mutual exclusion static CRITICAL_SECTION g_csLock; // Internal functions static BOOL AddQueueItem(SMatchedExe* pme); static BOOL SearchDirectory(PTSTR szDir, HWND hwCaller); static unsigned int __stdcall SearchThread(SSearchThreadParam* pstp); static BOOL NotifyExeFound(HWND hwnd, PCTSTR szAppName, PCTSTR szPath); // Add an exe to the queue of found exe's. BOOL AddQueueItem(SMatchedExe* pme) { assert(pme); SMatchedExeQueueNode* pNewNode = new SMatchedExeQueueNode; if(!pNewNode) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } pNewNode->pMatchedExe = pme; pNewNode->pNext = 0; EnterCriticalSection(&g_csLock); if(g_pHead == 0) g_pHead = g_pTail = pNewNode; else { g_pTail->pNext = pNewNode; g_pTail = pNewNode; } LeaveCriticalSection(&g_csLock); return TRUE; } // Return a single exe from the list, NULL if empty. SMatchedExe* GetMatchedExe() { SMatchedExe* pRet = 0; SMatchedExeQueueNode* pNewHead = 0; EnterCriticalSection(&g_csLock); if(g_pHead != NULL) { pRet = g_pHead->pMatchedExe; pNewHead = g_pHead->pNext; delete g_pHead; g_pHead = pNewHead; } LeaveCriticalSection(&g_csLock); return pRet; } // Called recursively, TRUE return means continue, FALSE // means terminate. BOOL SearchDirectory(PTSTR szPath, HWND hwCaller) { // Send an update to the caller window . . . TCHAR* szTemp = new TCHAR[lstrlen(szPath) + 1]; if(!szTemp) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } lstrcpy(szTemp, szPath); PostMessage(hwCaller, WM_SEARCHDB_UPDATE, 0, reinterpret_cast(szTemp)); WIN32_FIND_DATA fileFindData; HANDLE hSearch; PTSTR szSearchPath; MSG msg; // Check if we've been told to terminate. if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if(msg.message == WM_SEARCHDB_STOP) return FALSE; } szSearchPath = new TCHAR[lstrlen(szPath)+lstrlen(TEXT("*.exe")) +1]; if(!szSearchPath) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } wsprintf(szSearchPath, TEXT("%s*.exe"), szPath); hSearch = FindFirstFile(szSearchPath, &fileFindData); delete szSearchPath; szSearchPath = 0; if(hSearch != INVALID_HANDLE_VALUE) { do { if(!(fileFindData.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY))) { PTSTR szExePath; szExePath = new TCHAR[lstrlen(szPath)+ lstrlen(fileFindData.cFileName)+1]; if(!szExePath) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } wsprintf(szExePath, TEXT("%s%s"), szPath, fileFindData.cFileName); SDBQUERYRESULT sdbQuery; ZeroMemory(&sdbQuery, sizeof(SDBQUERYRESULT)); SdbGetMatchingExe(g_hSDB, szExePath, NULL, NULL, SDBGMEF_IGNORE_ENVIRONMENT, &sdbQuery); if(sdbQuery.atrExes[0] != TAGREF_NULL) { DWORD dwNumExes; // // count the exes // for (dwNumExes = 0; dwNumExes < SDB_MAX_EXES; ++dwNumExes) { if (sdbQuery.atrExes[dwNumExes] == TAGREF_NULL) { break; } } // // for now, just get the info for the last exe in the list, which will // be the one with specific info for this app. // BUGBUG -- is this the right thing to do? dmunsil // TAGREF trExe = sdbQuery.atrExes[dwNumExes - 1]; TCHAR szAppName[c_nMaxStringLength] = TEXT(""); TAGREF trAppName = SdbFindFirstTagRef(g_hSDB, trExe, TAG_APP_NAME); if(trAppName == TAGREF_NULL) wsprintf(szAppName, TEXT("%s%s"), szPath, fileFindData.cFileName); else SdbReadStringTagRef(g_hSDB, trAppName, szAppName, c_nMaxStringLength); NotifyExeFound(hwCaller, szAppName, szExePath); SdbReleaseMatchingExe(g_hSDB, trExe); } delete szExePath; szExePath = 0; } } while(FindNextFile(hSearch, &fileFindData)); FindClose(hSearch); } // Recurse into subdirectories // Unsure how to do attribute based search, let's just // look for everything and take note of directories. szSearchPath = new TCHAR[lstrlen(szPath)+2]; if(!szSearchPath) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } wsprintf(szSearchPath, TEXT("%s*"), szPath); hSearch = FindFirstFile(szSearchPath, &fileFindData); delete szSearchPath; szSearchPath = 0; if(hSearch != INVALID_HANDLE_VALUE) { do { if(fileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Don't do an infinite directory recursion if( !((lstrcmp(fileFindData.cFileName, TEXT(".")) == 0) || (lstrcmp(fileFindData.cFileName, TEXT("..")) == 0))) { szSearchPath = new TCHAR[lstrlen(szPath)+ lstrlen(fileFindData.cFileName)+2]; if(!szSearchPath) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } wsprintf(szSearchPath, TEXT("%s%s\\"), szPath, fileFindData.cFileName); if(SearchDirectory(szSearchPath, hwCaller) == FALSE) { FindClose(hSearch); delete szSearchPath; return FALSE; } delete szSearchPath; szSearchPath = 0; } } } while(FindNextFile(hSearch, &fileFindData)); FindClose(hSearch); } return TRUE; } // Wrapper thread for database search. unsigned int __stdcall SearchThread(SSearchThreadParam* pstp) { if((g_hSDB = SdbInitDatabase(0, NULL)) == NULL) return 0; // If NULL is passed in, just search all user drives. if(pstp->szPath == 0) { DWORD dwLength = GetLogicalDriveStrings(0,0); TCHAR* szDrives = new TCHAR[dwLength+1]; if(szDrives) { GetLogicalDriveStrings(dwLength, szDrives); TCHAR* szCurrDrive = szDrives; while(*szCurrDrive) { if(GetDriveType(szCurrDrive) == DRIVE_FIXED) if(!SearchDirectory(szCurrDrive, pstp->hwCaller)) break; szCurrDrive += lstrlen(szCurrDrive) + 1; } } else Error(pstp->hwCaller, IDS_NOMEMSTOPSEARCH); } else SearchDirectory(pstp->szPath, pstp->hwCaller); // Notify caller that search is complete. PostMessage(pstp->hwCaller, WM_SEARCHDB_DONE, 0, 0); delete pstp; SdbReleaseDatabase(g_hSDB); return 0; } // Notify caller that an exe has been found. BOOL NotifyExeFound(HWND hwnd, PCTSTR szAppName, PCTSTR szPath) { SMatchedExe* pme = 0; pme = new SMatchedExe; if(!pme) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } pme->szAppName = new TCHAR[lstrlen(szAppName)+1]; if(!pme->szAppName) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } lstrcpy(pme->szAppName, szAppName); pme->szPath = new TCHAR[lstrlen(szPath)+1]; if(!pme->szPath) { Error(0, IDS_NOMEMSTOPSEARCH); return FALSE; } lstrcpy(pme->szPath, szPath); // Add to the queue of found exe's if(!AddQueueItem(pme)) return FALSE; // Notify the caller. PostMessage(hwnd, WM_SEARCHDB_ADDAPP, 0, 0); return TRUE; } // SearchDB() // Will search a user's hard drives looking for applications // that have entries in the AppCompat database. // szDrives is a string similar in format to // the string returned by GetLogicalDriveStrings() // If 0 is passed, it will call GetLogicalDriveStrings() // and parse all hard drives. // Messages will be posted to the caller window // in response to events BOOL SearchDB(PCTSTR szPath, HWND hwCaller) { SSearchThreadParam* p = new SSearchThreadParam; if(!p) { Error(hwCaller, IDS_NOMEMSTOPSEARCH); return FALSE; } p->hwCaller = hwCaller; if(szPath) { p->szPath = new TCHAR[lstrlen(szPath)+1]; if(!p->szPath) { Error(hwCaller, IDS_NOMEMSTOPSEARCH); return FALSE; } lstrcpy(p->szPath, szPath); } else p->szPath = NULL; if(g_hThread) StopSearchDB(); g_hThread = CreateThread(0, 0, reinterpret_cast(SearchThread), p, 0, &g_dwThreadID); if(g_hThread == NULL) return FALSE; return TRUE; } // Terminate the search. void StopSearchDB() { // As long as the thread is active if(g_hThread && (WaitForSingleObject(g_hThread, 0) != WAIT_OBJECT_0)) { // Post in a while to ensure that the message queue was // created. while((PostThreadMessage(g_dwThreadID, WM_SEARCHDB_STOP, 0, 0)==FALSE) && (GetLastError() != ERROR_INVALID_THREAD_ID)) Sleep(0); if(GetLastError() != ERROR_INVALID_THREAD_ID) WaitForSingleObject(g_hThread, INFINITE); } if(g_hThread) CloseHandle(g_hThread); g_hThread = 0; } // Setup all globals needed. BOOL InitSearchDB() { InitializeCriticalSection(&g_csLock); g_pShimHeap = GetProcessHeap(); g_pfnRtlAllocateHeap = HeapAlloc; g_pfnRtlFreeHeap = HeapFree; return TRUE; }