windows-nt/Source/XPSP1/NT/windows/appcompat/tools/appsearch/searchdb.cpp
2020-09-26 16:20:57 +08:00

447 lines
12 KiB
C++

// 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 <windows.h>
#include <assert.h>
extern "C"
{
#include <shimdb.h>
}
#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<LPARAM>(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<LPTHREAD_START_ROUTINE>(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;
}