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.
2088 lines
53 KiB
2088 lines
53 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CSearch.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains code that handles the searching of the disk for
|
|
fixed entries.
|
|
|
|
Author:
|
|
|
|
kinshu created July 2, 2001
|
|
|
|
Notes:
|
|
|
|
The search window is implemented as a modeless window that has NULL as its parent.
|
|
We had to do this because we want the users to tab between the main window and the
|
|
search window
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
/////////////////////// Extern variables //////////////////////////////////////
|
|
|
|
extern BOOL g_bMainAppExpanded;
|
|
extern BOOL g_bSomeWizardActive;
|
|
extern HINSTANCE g_hInstance;
|
|
extern HWND g_hDlg;
|
|
extern HIMAGELIST g_hImageList;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////// Defines //////////////////////////////////////////////
|
|
|
|
// We're using the high 4 bits of the TAGID to say what PDB the TAGID is from.
|
|
#define PDB_MAIN 0x00000000
|
|
#define PDB_TEST 0x10000000
|
|
#define PDB_LOCAL 0x20000000
|
|
|
|
// Used to get the tag ref from the tagid, the low 28 bits
|
|
#define TAGREF_STRIP_TAGID 0x0FFFFFFF
|
|
|
|
// Used to get the PDB from the tagid, the high 4 bits
|
|
#define TAGREF_STRIP_PDB 0xF0000000
|
|
|
|
// Subitems for the columns of the list view
|
|
#define SEARCH_COL_AFFECTEDFILE 0
|
|
#define SEARCH_COL_PATH 1
|
|
#define SEARCH_COL_APP 2
|
|
#define SEARCH_COL_ACTION 3
|
|
#define SEARCH_COL_DBTYPE 4
|
|
|
|
// Total number of columns in the search dialog list view
|
|
#define TOT_COLS 5
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////// Global variables /////////////////////////////////////
|
|
|
|
// Index where the next element will be inserted in the list view
|
|
UINT g_nIndex = 0;
|
|
|
|
// The search object
|
|
CSearch* g_pSearch;
|
|
|
|
// width and height of the dialog box. These are required in the WM_SIZE handler
|
|
int g_cWidthSrch;
|
|
int g_cHeightSrch;
|
|
|
|
//
|
|
// This will hold the path that we want to search. e.g c:\*.exe or c:\
|
|
// This will be the content of the text box
|
|
static TCHAR s_szPath[MAX_PATH + 5]; // just so that we can have *.exe at the end, if needed. This will be an invalid path.
|
|
|
|
// The path that the user last searched on.
|
|
static TCHAR s_szPrevPath[MAX_PATH + 5]; // just so that we can have *.exe at the end, if needed. This will be an invalid path.
|
|
|
|
//
|
|
// What type of entries are we looking for. The values of these will be set depending
|
|
// upon if the corresponding check boxes are set
|
|
BOOL s_bAppHelp; // We want to see entries with Apphelp
|
|
BOOL s_bShims; // We want to see entries with shims/flags or patches
|
|
BOOL s_bLayers; // We want to see entries with layers
|
|
|
|
HSDB g_hSDB;
|
|
|
|
// The handle to the search dialog
|
|
HWND g_hSearchDlg;
|
|
|
|
// The thread which does all the job
|
|
HANDLE g_hSearchThread = NULL;
|
|
|
|
// The handle to the search results list
|
|
HWND g_hwndSearchList;
|
|
|
|
// If this is TRUE, we must abort the search. Typically set when the user presses STOP button
|
|
BOOL g_bAbort;
|
|
|
|
// The critical section that guards g_bAbort and access to the list view
|
|
CRITICAL_SECTION g_CritSect;
|
|
|
|
// The handle to the main dialog
|
|
HWND g_hdlgSearchDB;
|
|
|
|
// This is a bit array that describes which cols are sorted in which fashion
|
|
static LONG s_lColumnSort;
|
|
|
|
//
|
|
// This will contain the cur dir before we started search
|
|
TCHAR g_szPresentDir[MAX_PATH];
|
|
|
|
// This will be the path that we want to show in the status bar
|
|
TCHAR g_szNewPathFound[MAX_PATH];
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////// Function Declarations //////////////////////////////
|
|
|
|
|
|
void
|
|
ShowContextMenu(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
OnBrowse(
|
|
HWND hdlg
|
|
);
|
|
|
|
BOOL
|
|
AddNewResult(
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
DoSearch(
|
|
HWND hDlg
|
|
);
|
|
|
|
void
|
|
OnSearchInitDialog(
|
|
HWND hDlg,
|
|
LPARAM lParam
|
|
);
|
|
|
|
void
|
|
SaveResults(
|
|
HWND hdlg
|
|
);
|
|
|
|
void
|
|
SearchDirectory(
|
|
LPTSTR szDir,
|
|
LPTSTR szExt
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
GetCheckStatus(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
GetCheckStatus
|
|
|
|
Desc: Ses static variables by looking which check boxes have been selected
|
|
|
|
Params:
|
|
IN HWND hDlg: The search dialog box
|
|
|
|
Return:
|
|
void
|
|
|
|
Notes: The check boxes work in OR manner. So if we select all of them it means select fixes
|
|
that have either of them
|
|
--*/
|
|
{
|
|
//
|
|
// Do we want to search for entries with Apphelp?
|
|
//
|
|
s_bAppHelp = (IsDlgButtonChecked(hDlg, IDC_CHKAPP) == BST_CHECKED) ? TRUE : FALSE;
|
|
|
|
//
|
|
// Do we want to search for entries with shims, flags or patches?
|
|
//
|
|
s_bShims = (IsDlgButtonChecked(hDlg, IDC_CHKSHI) == BST_CHECKED) ? TRUE : FALSE;
|
|
|
|
//
|
|
// Do we want to search for entries with layers?
|
|
//
|
|
s_bLayers = (IsDlgButtonChecked(hDlg, IDC_CHKLAY) == BST_CHECKED) ? TRUE : FALSE;
|
|
}
|
|
|
|
void
|
|
StopSearch(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
StopSearch
|
|
|
|
Desc: Enables/Disables the various buttons and does other stuff that has to
|
|
be done after the search has stopped because it was complete or the user
|
|
pressed Stop button
|
|
|
|
Notes: Does not actually stop the search, but performs the necessary actions after
|
|
search has been stopped
|
|
--*/
|
|
{
|
|
HWND hwndList = NULL;
|
|
|
|
if (g_hSearchThread) {
|
|
CloseHandle(g_hSearchThread);
|
|
g_hSearchThread = NULL;
|
|
}
|
|
|
|
KillTimer(g_hSearchDlg, 0);
|
|
|
|
Animate_Stop(GetDlgItem(g_hSearchDlg, IDC_ANIMATE));
|
|
|
|
SetCurrentDirectory(g_szPresentDir);
|
|
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_STOP), FALSE);
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_SEARCH), TRUE);
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_NEWSEARCH), TRUE);
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_SAVE), TRUE);
|
|
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_CHKAPP), TRUE);
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_CHKLAY), TRUE);
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_CHKSHI), TRUE);
|
|
|
|
hwndList = GetDlgItem(g_hSearchDlg, IDC_LIST);
|
|
|
|
//
|
|
// We need to enable the static control if we have found some results in search
|
|
//
|
|
EnableWindow(GetDlgItem(g_hSearchDlg, IDC_STATIC_CAPTION),
|
|
ListView_GetItemCount(hwndList) > 0);
|
|
|
|
SetActiveWindow(g_hdlgSearchDB);
|
|
SetFocus(g_hdlgSearchDB);
|
|
}
|
|
|
|
void
|
|
HandleSearchSizing(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
HandleSearchSizing
|
|
|
|
Desc: Handles WM_SIZE for the search dialog
|
|
|
|
Paras:
|
|
IN HWND hDlg: The search dialog
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
int nWidth;
|
|
int nHeight;
|
|
int nStatusbarTop;
|
|
RECT rDlg;
|
|
|
|
if (g_cWidthSrch == 0 || g_cHeightSrch == 0) {
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hDlg, &rDlg);
|
|
|
|
nWidth = rDlg.right - rDlg.left;
|
|
nHeight = rDlg.bottom - rDlg.top;
|
|
|
|
int deltaW = nWidth - g_cWidthSrch;
|
|
int deltaH = nHeight - g_cHeightSrch;
|
|
|
|
HWND hwnd;
|
|
RECT r;
|
|
|
|
HDWP hdwp = BeginDeferWindowPos(10);
|
|
|
|
if (hdwp == NULL) {
|
|
//
|
|
// NULL indicates that insufficient system resources are available to
|
|
// allocate the structure. To get extended error information, call GetLastError.
|
|
//
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// The status bar
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_STATUSBAR);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
nStatusbarTop = r.top + deltaH,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top + deltaH,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The result list view
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_LIST);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left + deltaW,
|
|
nStatusbarTop - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The browse button
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_BROWSE);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The search button
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_SEARCH);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The save button. Used to export results to a tab separated text file
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_SAVE);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The stop button
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_STOP);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The new search button
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_NEWSEARCH);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The help button
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_SEARCH_HELP);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The animate control
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_ANIMATE);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left + deltaW,
|
|
r.top,
|
|
r.right - r.left,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The text box
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_PATH);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
//
|
|
// The group control
|
|
//
|
|
hwnd = GetDlgItem(hDlg, IDC_GROUP);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
r.left,
|
|
r.top,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
|
|
ListView_SetColumnWidth(g_hwndSearchList, TOT_COLS - 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
g_cWidthSrch = nWidth;
|
|
g_cHeightSrch = nHeight;
|
|
|
|
End:
|
|
return;
|
|
}
|
|
|
|
INT_PTR
|
|
HandleTextChange(
|
|
IN HWND hdlg,
|
|
IN WPARAM wParam
|
|
)
|
|
/*++
|
|
|
|
HandleTextChange
|
|
|
|
Desc: Handles the WM_COMMAND messages for the text box
|
|
|
|
Params:
|
|
IN HWND hdlg: The handle to the query dilaog box
|
|
IN WPARAM wParam: The wParam that comes with WM_COMMAND
|
|
|
|
Return:
|
|
TRUE: If we process this message
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
TCHAR szText[MAX_PATH];
|
|
DWORD dwFlags;
|
|
BOOL bEnable;
|
|
INT_PTR ipReturn = FALSE;
|
|
|
|
switch (HIWORD(wParam)) {
|
|
case EN_CHANGE:
|
|
//
|
|
// We disable the search button if there is no path that we can search on//
|
|
//
|
|
*szText = 0;
|
|
GetDlgItemText(hdlg, IDC_PATH, szText, ARRAYSIZE(szText));
|
|
|
|
bEnable = ValidInput(szText);
|
|
|
|
//
|
|
// If we have some text in the text field, enable the search button, otherwise
|
|
// disable it
|
|
//
|
|
EnableWindow(GetDlgItem(hdlg, IDC_SEARCH), bEnable);
|
|
ipReturn = TRUE;
|
|
break;
|
|
|
|
default: ipReturn = FALSE;
|
|
}
|
|
|
|
return ipReturn;
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
SearchDialog(
|
|
IN HWND hDlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
SearchDialog
|
|
|
|
Desc: Dialog proc for the search dialog
|
|
|
|
Paras: Standard dialog handler parameters
|
|
|
|
IN HWND hDlg
|
|
IN UINT uMsg
|
|
IN WPARAM wParam
|
|
IN LPARAM lParam
|
|
|
|
Return: Standard dialog handler return
|
|
|
|
--*/
|
|
{
|
|
switch (uMsg) {
|
|
|
|
case WM_SIZE:
|
|
|
|
if (wParam != SIZE_MINIMIZED) {
|
|
HandleSearchSizing(hDlg);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
|
|
|
|
pmmi->ptMinTrackSize.x = 400;
|
|
pmmi->ptMinTrackSize.y = 365;
|
|
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
OnSearchInitDialog(hDlg, lParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
HIMAGELIST hImageList = ListView_GetImageList(g_hwndSearchList, LVSIL_SMALL);
|
|
|
|
if (hImageList) {
|
|
ImageList_Destroy(hImageList);
|
|
}
|
|
|
|
hImageList = ListView_GetImageList(g_hwndSearchList, LVSIL_NORMAL);
|
|
|
|
if (hImageList) {
|
|
ImageList_Destroy(hImageList);
|
|
}
|
|
|
|
g_hdlgSearchDB = NULL;
|
|
|
|
if (g_pSearch) {
|
|
delete g_pSearch;
|
|
g_pSearch = NULL;
|
|
}
|
|
|
|
DeleteCriticalSection(&g_CritSect);
|
|
//
|
|
// Remove the list view contents and the items that are tied with it.
|
|
//
|
|
ClearResults(hDlg, TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_USER_NEWMATCH:
|
|
|
|
AddNewResult(lParam);
|
|
break;
|
|
|
|
case WM_USER_NEWFILE:
|
|
{
|
|
EnterCriticalSection(&g_CritSect);
|
|
|
|
if (g_pSearch) {
|
|
SetWindowText(g_pSearch->m_hStatusBar , (LPTSTR)lParam);
|
|
}
|
|
|
|
if (lParam) {
|
|
delete[] ((TCHAR*)lParam);
|
|
}
|
|
|
|
LeaveCriticalSection(&g_CritSect);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_CONTEXTMENU:
|
|
|
|
ShowContextMenu(wParam, lParam);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR lpnmhdr = (LPNMHDR)lParam;
|
|
|
|
if (lpnmhdr && lpnmhdr->idFrom == IDC_LIST) {
|
|
return HandleSearchListNotification(hDlg, lParam);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
|
|
if (g_hSearchThread) {
|
|
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(g_hSearchThread, 0)) {
|
|
|
|
StopSearch();
|
|
|
|
K_SIZE k_size = 260;
|
|
|
|
TCHAR* pszString = new TCHAR[k_size];
|
|
|
|
if (pszString == NULL) {
|
|
MEM_ERR;
|
|
break;
|
|
}
|
|
|
|
SafeCpyN(pszString, CSTRING(IDS_SEARCHCOMPLETE), k_size);
|
|
|
|
SendNotifyMessage(g_hSearchDlg, WM_USER_NEWFILE, 0,(LPARAM)pszString);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_STOP:
|
|
|
|
g_bAbort = TRUE;
|
|
break;
|
|
|
|
case IDC_BROWSE:
|
|
|
|
OnBrowse(hDlg);
|
|
break;
|
|
|
|
case IDC_SEARCH:
|
|
|
|
DoSearch(hDlg);
|
|
break;
|
|
|
|
case IDC_NEWSEARCH:
|
|
|
|
ClearResults(hDlg, TRUE);
|
|
break;
|
|
|
|
case IDC_SAVE:
|
|
|
|
SaveResults(hDlg);
|
|
break;
|
|
|
|
case IDC_PATH:
|
|
|
|
HandleTextChange(hDlg, wParam);
|
|
break;
|
|
|
|
case IDC_SEARCH_HELP:
|
|
|
|
ShowInlineHelp(TEXT("searching_for_fixes.htm"));
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
{
|
|
Animate_Close(GetDlgItem(hDlg, IDC_ANIMATE));
|
|
|
|
g_bAbort = TRUE;
|
|
|
|
if (g_hSearchThread) {
|
|
WaitForSingleObject(g_hSearchThread, INFINITE);
|
|
}
|
|
|
|
DestroyWindow(hDlg);
|
|
break;
|
|
}
|
|
|
|
case ID_VIEWCONTENTS:
|
|
{
|
|
|
|
LVITEM lvi;
|
|
PMATCHEDENTRY pmMatched;
|
|
INT iSelection;
|
|
|
|
iSelection = ListView_GetSelectionMark(g_hwndSearchList);
|
|
|
|
if (iSelection == -1) {
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
lvi.iItem = iSelection;
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_PARAM;
|
|
|
|
if (ListView_GetItem(g_hwndSearchList, &lvi)) {
|
|
pmMatched = (PMATCHEDENTRY)lvi.lParam;
|
|
GotoEntry(pmMatched);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SearchThread(
|
|
IN LPVOID pVoid
|
|
)
|
|
/*++
|
|
|
|
SearchThread
|
|
|
|
Desc: The thread routine that does the actual search
|
|
|
|
Params:
|
|
IN LPVOID pVoid: Pointer to the search string. We get it trimmed
|
|
|
|
Return:
|
|
0
|
|
--*/
|
|
{
|
|
LPTSTR szSearch = (LPTSTR)pVoid;
|
|
PTCHAR pchFirstSlash = NULL;
|
|
DWORD dwReturn;
|
|
|
|
//
|
|
// Separate the extension and the directory
|
|
//
|
|
TCHAR szDrive[_MAX_DRIVE], szDir[MAX_PATH], szFile[MAX_PATH * 2] , szExt[MAX_PATH], szDirWithDrive[MAX_PATH * 2];
|
|
|
|
*szDirWithDrive = *szDrive = *szDir = *szFile = *szExt = 0;
|
|
|
|
_tsplitpath(szSearch, szDrive, szDir, szFile, szExt);
|
|
|
|
SafeCpyN(szDirWithDrive, szDrive, ARRAYSIZE(szDirWithDrive));
|
|
|
|
StringCchCat(szDirWithDrive, ARRAYSIZE(szDirWithDrive), szDir);
|
|
|
|
if (lstrlen(szDirWithDrive) == 0) {
|
|
//
|
|
// Only the file name is there, check in the current drive
|
|
//
|
|
*szDirWithDrive = 0;
|
|
|
|
dwReturn = GetCurrentDirectory(MAX_PATH, szDirWithDrive);
|
|
|
|
if (dwReturn > 0 && dwReturn < ARRAYSIZE(szDirWithDrive)) {
|
|
|
|
pchFirstSlash = _tcschr(szDirWithDrive, TEXT('\\'));
|
|
|
|
if (pchFirstSlash) {
|
|
//
|
|
// We will now get only the present drive in szDirWithDrive
|
|
//
|
|
*(++pchFirstSlash) = 0;
|
|
}
|
|
} else {
|
|
//
|
|
// Error condition.
|
|
//
|
|
Dbg(dlError, "[SearchThread]: Could not execute GetCurrentDirectory properly");
|
|
goto End;
|
|
}
|
|
}
|
|
|
|
StringCchCat(szFile, ARRAYSIZE(szFile), szExt);
|
|
|
|
if (lstrlen(szFile) == 0) {
|
|
SafeCpyN(szFile, TEXT("*.EXE"), ARRAYSIZE(szFile));
|
|
}
|
|
|
|
if (!SetCurrentDirectory(szDirWithDrive)) {
|
|
|
|
MSGF(g_hdlgSearchDB,
|
|
g_szAppName,
|
|
MB_ICONINFORMATION,
|
|
TEXT("\'%s\'-%s"),
|
|
szDirWithDrive,
|
|
GetString(IDS_PATHERROR));
|
|
|
|
return 0;
|
|
}
|
|
|
|
SearchDirectory(szDirWithDrive, szFile);
|
|
|
|
End:
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
Search(
|
|
IN HWND hDlg,
|
|
IN LPCTSTR szSearch
|
|
)
|
|
/*++
|
|
|
|
Search
|
|
|
|
Desc: Creates the thread that will do the actual search
|
|
|
|
Params:
|
|
IN HWND hDlg: The search dialog
|
|
IN LPCTSTR szSearch: The files to search
|
|
|
|
Return:
|
|
void
|
|
|
|
--*/
|
|
{
|
|
DWORD dwID;
|
|
|
|
Animate_Play(GetDlgItem(hDlg, IDC_ANIMATE), 0, -1, -1);
|
|
|
|
g_hSearchThread = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START)SearchThread, (PVOID)szSearch, 0, (unsigned int*)&dwID);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PopulateFromExes(
|
|
IN LPTSTR szPath,
|
|
IN TAGREF tagref
|
|
)
|
|
/*++
|
|
|
|
PopulateFromExes
|
|
|
|
Desc: For the file with path szPath, checks if it needs to be added to the results
|
|
list view and if yes, then calls SendNotifyMessage() to add this to the results
|
|
list view
|
|
|
|
Params:
|
|
IN LPTSTR szPath: The path of the file found
|
|
IN TAGREF tagref: TAGREF for the entry. The TAGREF incorporates the TAGID and a
|
|
constant that tells us which PDB the TAGID is from.
|
|
|
|
Return:
|
|
|
|
--*/
|
|
{
|
|
BOOL bEntryHasAppHelp = FALSE;
|
|
BOOL bEntryHasShims = FALSE;
|
|
BOOL bEntryHasPatches = FALSE;
|
|
BOOL bEntryHasFlags = FALSE;
|
|
BOOL bEntryHasLayers = FALSE;
|
|
TAGID ID; //TAGID for the entry
|
|
PDB pDB; //The database pdb
|
|
BOOL bOk = TRUE;
|
|
|
|
PMATCHEDENTRY pmEntry = new MATCHEDENTRY;
|
|
|
|
if (pmEntry == NULL) {
|
|
MEM_ERR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the database pdb and the tag id for this tagref of the entry. We need
|
|
// these so that we can get the properties of this entry from the database in which
|
|
// it resides
|
|
//
|
|
if (!SdbTagRefToTagID(g_hSDB, tagref, &pDB, &ID)) {
|
|
|
|
bOk = FALSE;
|
|
assert(FALSE);
|
|
goto End;
|
|
}
|
|
|
|
//
|
|
// Find out how this entry has been fixed. Get its app-name as well
|
|
//
|
|
if (pDB == NULL || !LookUpEntryProperties(pDB,
|
|
ID,
|
|
&bEntryHasLayers,
|
|
&bEntryHasShims,
|
|
&bEntryHasPatches,
|
|
&bEntryHasFlags,
|
|
&bEntryHasAppHelp,
|
|
pmEntry->strAppName)) {
|
|
assert(FALSE);
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
pmEntry->tiExe = ID;
|
|
pmEntry->strPath = szPath;
|
|
|
|
switch (tagref & TAGREF_STRIP_PDB) {
|
|
case PDB_MAIN:
|
|
|
|
pmEntry->strDatabase = CSTRING(IDS_GLOBAL);
|
|
break;
|
|
|
|
case PDB_TEST:
|
|
|
|
pmEntry->strDatabase = CSTRING(IDS_TEST);
|
|
break;
|
|
|
|
case PDB_LOCAL:
|
|
|
|
pmEntry->strDatabase = CSTRING(IDS_LOCAL);
|
|
break;
|
|
|
|
default:
|
|
|
|
pmEntry->strDatabase = CSTRING(IDS_LOCAL);
|
|
break;
|
|
|
|
}
|
|
|
|
if (!GetDbGuid(pmEntry->szGuid, ARRAYSIZE(pmEntry->szGuid), pDB)) {
|
|
|
|
assert(FALSE);
|
|
bOk = FALSE;
|
|
goto End;
|
|
}
|
|
|
|
|
|
BOOL bShow = FALSE;
|
|
|
|
if (bEntryHasAppHelp && s_bAppHelp) {
|
|
pmEntry->strAction.Strcat(CSTRING(IDS_APPHELPS));
|
|
bShow = TRUE;
|
|
}
|
|
|
|
if ((bEntryHasShims || bEntryHasFlags || bEntryHasPatches) && s_bShims) {
|
|
pmEntry->strAction.Strcat(CSTRING(IDS_FIXES));
|
|
bShow = TRUE;
|
|
}
|
|
|
|
if (bEntryHasLayers && s_bLayers) {
|
|
pmEntry->strAction.Strcat(CSTRING(IDS_MODES));
|
|
bShow = TRUE;
|
|
}
|
|
|
|
int nLength = pmEntry->strAction.Length();
|
|
|
|
if (nLength) {
|
|
pmEntry->strAction.SetChar(nLength - 1, TEXT('\0'));
|
|
}
|
|
|
|
|
|
if (bShow) {
|
|
SendNotifyMessage(g_hSearchDlg, WM_USER_NEWMATCH, 0, (LPARAM)pmEntry);
|
|
}
|
|
|
|
//
|
|
// NOTE: the strings of pmEntry that are not used later are freed by the handler
|
|
// of WM_USER_NEWMATCH, only the szGuid is retained
|
|
// after the handler of WM_USER_NEWMATCH ends.
|
|
// This is required so that we can double click on the list item.
|
|
//
|
|
// The pmEntry data-structure is deleted when the window gets destroyed
|
|
//
|
|
|
|
End:
|
|
if (bOk == FALSE && pmEntry) {
|
|
delete pmEntry;
|
|
}
|
|
|
|
return bOk;
|
|
}
|
|
|
|
void
|
|
SearchDirectory(
|
|
IN LPTSTR pszDir,
|
|
IN LPTSTR szExtension
|
|
)
|
|
/*++
|
|
|
|
SearchDirectory
|
|
|
|
Desc: Searches a directory recursively for fixed files with a specified extension.
|
|
Wild cards are allowed
|
|
|
|
Params:
|
|
IN LPTSTR pszDir: The directory to search in. This may or may not have a ending \
|
|
|
|
IN LPTSTR szExtension: The extensions to look for
|
|
|
|
Return:
|
|
void
|
|
|
|
|
|
Note: If pszDir is a drive should have a \ at the end
|
|
|
|
--*/
|
|
{
|
|
HANDLE hFile;
|
|
WIN32_FIND_DATA Data;
|
|
TCHAR szCurrentDir[MAX_PATH_BUFFSIZE];
|
|
BOOL bAbort = FALSE;
|
|
TCHAR* pszString = NULL;
|
|
INT iLength = 0;
|
|
DWORD dwReturn = 0;
|
|
|
|
*szCurrentDir = 0;
|
|
|
|
dwReturn = GetCurrentDirectory(ARRAYSIZE(szCurrentDir), szCurrentDir);
|
|
|
|
if (dwReturn == 0 || dwReturn >= ARRAYSIZE(szCurrentDir)) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "SearchDirectory GetCurrentDirectory Failed");
|
|
return;
|
|
}
|
|
|
|
if (!SetCurrentDirectory(pszDir)) {
|
|
//
|
|
// We do not prompt here, because we might have encountered a directory that we
|
|
// do not have rights to access. Typically, network paths.
|
|
//
|
|
return;
|
|
}
|
|
|
|
iLength = lstrlen(pszDir) + 1;
|
|
|
|
pszString = new TCHAR[iLength];
|
|
|
|
if (pszString == NULL) {
|
|
MEM_ERR;
|
|
return;
|
|
}
|
|
|
|
SafeCpyN(pszString, pszDir, iLength);
|
|
|
|
SendNotifyMessage(g_hSearchDlg, WM_USER_NEWFILE, 0, (LPARAM)pszString);
|
|
|
|
hFile = FindFirstFile(szExtension, &Data);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
CSTRING szStr;
|
|
|
|
szStr.Sprintf(TEXT("%s"), pszDir);
|
|
|
|
if (*pszDir && TEXT('\\') != pszDir[lstrlen(pszDir) - 1]) {
|
|
szStr.Strcat(TEXT("\\"));
|
|
}
|
|
|
|
szStr.Strcat(Data.cFileName);
|
|
|
|
SDBQUERYRESULT Res;
|
|
|
|
ZeroMemory(&Res, sizeof(SDBQUERYRESULT));
|
|
|
|
//
|
|
// Determine if this file is affected in any way.
|
|
//
|
|
if ((Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
|
|
if (SdbGetMatchingExe(g_hSDB,
|
|
(LPCTSTR)szStr,
|
|
NULL,
|
|
NULL,
|
|
SDBGMEF_IGNORE_ENVIRONMENT,
|
|
&Res)) {
|
|
|
|
//
|
|
// At the moment we only look for exe entires. i.e. to say, we
|
|
// do not catch programs fixed using the compat UI or the tab
|
|
// we only show programs that have been fixed by installing some
|
|
// custom database
|
|
//
|
|
for (int nExeLoop = 0; nExeLoop < SDB_MAX_EXES; ++nExeLoop) {
|
|
|
|
if (Res.atrExes[nExeLoop]) {
|
|
PopulateFromExes(szStr, Res.atrExes[nExeLoop]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close any local databases that might have been opened by SdbGetMatchingExe(...)
|
|
//
|
|
SdbReleaseMatchingExe(g_hSDB, Res.atrExes[0]);
|
|
}
|
|
|
|
bAbort = g_bAbort;
|
|
|
|
} while (FindNextFile(hFile, &Data) && !bAbort);
|
|
|
|
FindClose(hFile);
|
|
}
|
|
|
|
//
|
|
// Now go through the sub-directories.
|
|
//
|
|
hFile = FindFirstFile(TEXT("*.*"), &Data);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
|
|
if (Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
BOOL bForbidden = FALSE;
|
|
|
|
if (TEXT('.') == Data.cFileName[0]) {
|
|
bForbidden = TRUE;
|
|
}
|
|
|
|
if (!bForbidden) {
|
|
|
|
TCHAR szPath[MAX_PATH * 2];
|
|
|
|
SafeCpyN(szPath, pszDir, MAX_PATH);
|
|
|
|
ADD_PATH_SEPARATOR(szPath, ARRAYSIZE(szPath));
|
|
|
|
StringCchCat(szPath, ARRAYSIZE(szPath), Data.cFileName);
|
|
|
|
SearchDirectory(szPath, szExtension);
|
|
}
|
|
}
|
|
|
|
bAbort = g_bAbort;
|
|
|
|
} while (FindNextFile(hFile, &Data) && !bAbort);
|
|
|
|
FindClose(hFile);
|
|
}
|
|
|
|
SetCurrentDirectory(szCurrentDir);
|
|
}
|
|
|
|
void
|
|
CSearch::Begin(
|
|
void
|
|
)
|
|
/*++
|
|
CSearch::Begin
|
|
|
|
Desc: Begins the search
|
|
|
|
--*/
|
|
{
|
|
if (g_hSDB == NULL) {
|
|
g_hSDB = SdbInitDatabase(0, NULL);
|
|
}
|
|
|
|
g_pSearch = this;
|
|
|
|
InitializeCriticalSection(&g_CritSect);
|
|
|
|
HWND hwnd = CreateDialog(g_hInstance,
|
|
MAKEINTRESOURCE(IDD_SEARCH),
|
|
GetDesktopWindow(),
|
|
SearchDialog);
|
|
|
|
ShowWindow(hwnd, SW_NORMAL);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
GotoEntry(
|
|
IN PMATCHEDENTRY pmMatched
|
|
)
|
|
/*++
|
|
GotoEntry
|
|
|
|
Desc: Selects the entry with tagid of pmMatched->tiExe in the entry tree.
|
|
|
|
Params:
|
|
IN PMATCHEDENTRY pmMatched: Contains information about the entry that we want to
|
|
show in the contents pane(RHS) and the database pane(LHS) in the main window
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
if (g_bSomeWizardActive) {
|
|
|
|
//
|
|
// We do not want that the focus should go to some other database, because
|
|
// some wizard is active, which believes that he is modal.
|
|
//
|
|
MessageBox(g_hdlgSearchDB, GetString(IDS_SOMEWIZARDACTIVE), g_szAppName, MB_ICONINFORMATION);
|
|
return;
|
|
|
|
}
|
|
|
|
if (pmMatched == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
BOOL bMainSDB = FALSE;
|
|
WCHAR wszShimDB[MAX_PATH];
|
|
|
|
*wszShimDB = 0;
|
|
|
|
if (pmMatched == NULL) {
|
|
return;
|
|
}
|
|
|
|
PDATABASE pDatabase = NULL;
|
|
|
|
if (lstrcmp(GlobalDataBase.szGUID, pmMatched->szGuid) == 0) {
|
|
|
|
//
|
|
// This is the global database
|
|
//
|
|
pDatabase = &GlobalDataBase;
|
|
bMainSDB = TRUE;
|
|
|
|
if (!g_bMainAppExpanded) {
|
|
|
|
SetStatus(GetDlgItem(g_hSearchDlg, IDC_STATUSBAR), IDS_LOADINGMAIN);
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
INT iResult = ShowMainEntries(g_hdlgSearchDB);
|
|
|
|
if (iResult == -1) {
|
|
SetStatus(GetDlgItem(g_hdlgSearchDB, IDC_STATUSBAR), CSTRING(IDS_LOADINGMAIN));
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
return;
|
|
} else {
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
|
|
SetStatus(GetDlgItem(g_hSearchDlg, IDC_STATUSBAR), TEXT(""));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have to now search for the database in the installed databases list
|
|
//
|
|
PDATABASE pDatabaseInstalled = InstalledDataBaseList.pDataBaseHead;
|
|
|
|
while (pDatabaseInstalled) {
|
|
|
|
if (lstrcmpi(pmMatched->szGuid, pDatabaseInstalled->szGUID) == 0) {
|
|
pDatabase = pDatabaseInstalled;
|
|
break;
|
|
}
|
|
|
|
pDatabaseInstalled = pDatabaseInstalled->pNext;
|
|
}
|
|
|
|
if (pDatabaseInstalled == NULL) {
|
|
//
|
|
// We might come here if the database was uninstalled after we populated
|
|
// the search results
|
|
//
|
|
MessageBox(g_hSearchDlg, GetString(IDS_NOLONGEREXISTS), g_szAppName, MB_ICONWARNING);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now for this database, search the particular entry
|
|
//
|
|
PDBENTRY pApp = pDatabase->pEntries, pEntry;
|
|
|
|
pEntry = pApp;
|
|
|
|
while (pApp) {
|
|
|
|
pEntry = pApp;
|
|
|
|
while (pEntry) {
|
|
if (pEntry->tiExe == pmMatched->tiExe) {
|
|
goto EndLoop;
|
|
}
|
|
|
|
pEntry = pEntry->pSameAppExe;
|
|
}
|
|
|
|
pApp = pApp->pNext;
|
|
}
|
|
|
|
if (pApp == NULL) {
|
|
|
|
MessageBox(g_hSearchDlg, GetString(IDS_NOLONGEREXISTS), g_szAppName, MB_ICONWARNING);
|
|
return;
|
|
}
|
|
|
|
EndLoop:
|
|
|
|
//
|
|
// Select the app in the DB tree
|
|
//
|
|
HTREEITEM hItemEntry = DBTree.FindChild(pDatabase->hItemAllApps, (LPARAM)pApp);
|
|
assert(hItemEntry);
|
|
|
|
TreeView_SelectItem(DBTree.m_hLibraryTree, hItemEntry);
|
|
|
|
//
|
|
// Now select the entry within the app in the entry tree
|
|
//
|
|
hItemEntry = CTree::FindChild(g_hwndEntryTree, TVI_ROOT, (LPARAM)pEntry);
|
|
assert(hItemEntry);
|
|
|
|
if (hItemEntry) {
|
|
TreeView_SelectItem(g_hwndEntryTree, hItemEntry);
|
|
SetFocus(g_hwndEntryTree);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
HandleSearchListNotification(
|
|
IN HWND hdlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
HandleSearchListNotification
|
|
|
|
Desc: Handles the notification messages for the Search List
|
|
|
|
Params:
|
|
IN HWND hdlg: The search dialog
|
|
IN LPARAM lParam: The LPARAM of WM_NOTIFY
|
|
|
|
Return:
|
|
TRUE: If the message was handled by this routine.
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
HWND hwndList = GetDlgItem(hdlg, IDC_LIST);
|
|
|
|
switch (pnm->code) {
|
|
|
|
case NM_DBLCLK:
|
|
|
|
SendMessage(hdlg, WM_COMMAND, (WPARAM)ID_VIEWCONTENTS, 0);
|
|
break;
|
|
|
|
case LVN_COLUMNCLICK:
|
|
{
|
|
LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)lParam;
|
|
COLSORT colSort;
|
|
|
|
colSort.hwndList = hwndList;
|
|
colSort.iCol = pnmlv->iSubItem;
|
|
colSort.lSortColMask = s_lColumnSort;
|
|
|
|
ListView_SortItemsEx(hwndList, CompareItemsEx, &colSort);
|
|
|
|
if ((s_lColumnSort & 1L << colSort.iCol) == 0) {
|
|
//
|
|
// Was in ascending order
|
|
//
|
|
s_lColumnSort |= (1L << colSort.iCol);
|
|
} else {
|
|
s_lColumnSort &= (~(1L << colSort.iCol));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ClearResults(
|
|
IN HWND hdlg,
|
|
IN BOOL bClearSearchPath
|
|
)
|
|
/*++
|
|
ClearResults
|
|
|
|
Desc: Clears the contents of the list view and the text box
|
|
|
|
Params:
|
|
IN HWND hdlg: The search dialog
|
|
IN BOOL bClearSearchPath: Do we wish to clear the contents of the text field also
|
|
--*/
|
|
{
|
|
HWND hwndList = GetDlgItem(hdlg, IDC_LIST);
|
|
INT iCount = ListView_GetItemCount(hwndList);
|
|
LVITEM lvi;
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
//
|
|
// Free the lParam for the list view.
|
|
//
|
|
CleanUpListView(hdlg);
|
|
|
|
|
|
SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
|
|
ListView_DeleteAllItems(hwndList);
|
|
SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
|
|
|
|
InvalidateRect(hwndList, NULL, TRUE);
|
|
UpdateWindow(hwndList);
|
|
|
|
if (bClearSearchPath) {
|
|
SetDlgItemText(hdlg, IDC_PATH, TEXT(""));
|
|
}
|
|
}
|
|
|
|
void
|
|
SaveResults(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
|
|
SaveResults
|
|
|
|
Desc: Saves the search results in a tab separated file
|
|
|
|
Params:
|
|
IN HWND hdlg: The search dialog
|
|
|
|
--*/
|
|
{
|
|
CSTRING strFileName;
|
|
TCHAR szTitle[256], szFilter[128], szExt[8];
|
|
|
|
*szTitle = *szFilter = *szExt = 0;
|
|
|
|
BOOL bResult = GetFileName(hdlg,
|
|
GetString(IDS_SAVE_RESULTS_TITLE, szTitle, ARRAYSIZE(szTitle)),
|
|
GetString(IDS_SAVE_RESULTS_FILTER, szFilter, ARRAYSIZE(szFilter)),
|
|
TEXT(""),
|
|
GetString(IDS_SAVE_RESULTS_EXT, szExt, ARRAYSIZE(szExt)),
|
|
OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT,
|
|
FALSE,
|
|
strFileName,
|
|
TRUE);
|
|
|
|
if (bResult) {
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
SaveListViewToFile(GetDlgItem(hdlg, IDC_LIST), TOT_COLS, strFileName.pszString, NULL);
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
|
|
void
|
|
CleanUpListView(
|
|
IN HWND hdlg
|
|
)
|
|
/*++
|
|
|
|
CleanUpListView
|
|
|
|
Desc: Frees the structures associated with the lParam of the list view
|
|
|
|
Params:
|
|
IN HWND hdlg: The search dialog
|
|
|
|
******************************************************************************
|
|
Warn: This method should not be directky called. Call ClearResults instead
|
|
******************************************************************************
|
|
--*/
|
|
{
|
|
HWND hwndList = GetDlgItem(hdlg, IDC_LIST);
|
|
INT iCount = ListView_GetItemCount(hwndList);
|
|
LVITEM lvi;
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
//
|
|
// Free the lParam for the list view.
|
|
//
|
|
for (INT iIndex = 0; iIndex < iCount; ++iIndex) {
|
|
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = iIndex;
|
|
lvi.iSubItem = 0;
|
|
|
|
if (ListView_GetItem(hwndList, &lvi) && lvi.lParam) {
|
|
delete (PMATCHEDENTRY)lvi.lParam;
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
OnSearchInitDialog(
|
|
IN HWND hDlg,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
OnSearchInitDialog
|
|
|
|
Desc: Handles WM_INITDIALOG for the search dialog box
|
|
|
|
Params:
|
|
IN HWND hDlg: The search dialog box
|
|
IN LPARAM lParam: The lParam for WM_INITDIALOG
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
//
|
|
// Limit the length of the path text field
|
|
//
|
|
SendMessage(GetDlgItem(hDlg, IDC_PATH),
|
|
EM_LIMITTEXT,
|
|
(WPARAM)MAX_PATH - 1,
|
|
(LPARAM)0);
|
|
|
|
g_hdlgSearchDB = hDlg;
|
|
|
|
s_lColumnSort = 0;
|
|
|
|
Animate_OpenEx(GetDlgItem(hDlg, IDC_ANIMATE),
|
|
g_hInstance,
|
|
MAKEINTRESOURCE(IDA_SEARCH));
|
|
|
|
//
|
|
// Set all the buttons
|
|
//
|
|
CheckDlgButton(hDlg, IDC_CHKLAY, BST_CHECKED);
|
|
CheckDlgButton(hDlg, IDC_CHKSHI, BST_CHECKED);
|
|
CheckDlgButton(hDlg, IDC_CHKAPP, BST_CHECKED);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_STOP), FALSE);
|
|
|
|
g_pSearch->m_hStatusBar = GetDlgItem(hDlg, IDC_STATUSBAR);
|
|
|
|
CSearch* pPresentSearch = (CSearch*)lParam;
|
|
|
|
g_hSearchDlg = hDlg;
|
|
|
|
g_hwndSearchList = GetDlgItem(hDlg, IDC_LIST);
|
|
|
|
ListView_SetExtendedListViewStyleEx(g_hwndSearchList,
|
|
0,
|
|
LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT);
|
|
|
|
|
|
//
|
|
// Add the columns that we are going to show in the list view
|
|
//
|
|
|
|
|
|
//
|
|
// Name of the fixed program file
|
|
//
|
|
InsertColumnIntoListView(g_hwndSearchList,
|
|
GetString(IDS_AFFECTED_FILE),
|
|
SEARCH_COL_AFFECTEDFILE,
|
|
20);
|
|
|
|
//
|
|
// Path of the fixed program file
|
|
//
|
|
InsertColumnIntoListView(g_hwndSearchList,
|
|
GetString(IDS_PATH),
|
|
SEARCH_COL_PATH,
|
|
30);
|
|
|
|
//
|
|
// App-Name of the fixed program file
|
|
//
|
|
InsertColumnIntoListView(g_hwndSearchList,
|
|
GetString(IDS_APP),
|
|
SEARCH_COL_APP,
|
|
20);
|
|
|
|
//
|
|
// Action type. This column will show a concatenated string specifying
|
|
// whether fixes, layers and/or apphelp is used for this entry
|
|
//
|
|
InsertColumnIntoListView(g_hwndSearchList,
|
|
GetString(IDS_ACTION),
|
|
SEARCH_COL_ACTION,
|
|
15);
|
|
|
|
//
|
|
// The database type of the database where the entry resides. One of Global or Local
|
|
//
|
|
InsertColumnIntoListView(g_hwndSearchList,
|
|
GetString(IDS_DATABASE),
|
|
SEARCH_COL_DBTYPE,
|
|
15);
|
|
|
|
ListView_SetColumnWidth(g_hwndSearchList, TOT_COLS - 1, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
RECT r;
|
|
|
|
GetWindowRect(hDlg, &r);
|
|
|
|
g_cWidthSrch = r.right - r.left;
|
|
g_cHeightSrch = r.bottom - r.top;
|
|
|
|
SHAutoComplete(GetDlgItem(hDlg, IDC_PATH), AUTOCOMPLETE);
|
|
|
|
if (*s_szPrevPath) {
|
|
|
|
//
|
|
// The user has invoked the search dialog previously, let us
|
|
// now show the directory/path that he searched for previously
|
|
//
|
|
SetDlgItemText(hDlg, IDC_PATH, s_szPrevPath);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is the first time that the user is using this search option.
|
|
// Default to the programs folder
|
|
//
|
|
LPITEMIDLIST lpIDL = NULL;
|
|
|
|
if (SUCCEEDED(SHGetFolderLocation(NULL,
|
|
CSIDL_PROGRAM_FILES,
|
|
NULL,
|
|
0,
|
|
&lpIDL))) {
|
|
|
|
if (lpIDL == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (SHGetPathFromIDList(lpIDL, s_szPath)) {
|
|
|
|
ADD_PATH_SEPARATOR(s_szPath, ARRAYSIZE(s_szPath));
|
|
|
|
StringCchCat(s_szPath, ARRAYSIZE(s_szPath), TEXT("*.exe"));
|
|
SetDlgItemText(hDlg, IDC_PATH, s_szPath);
|
|
|
|
//
|
|
// Free the pidl
|
|
//
|
|
LPMALLOC lpMalloc = NULL;
|
|
|
|
if (SUCCEEDED(SHGetMalloc(&lpMalloc)) && lpMalloc) {
|
|
lpMalloc->Free(lpIDL);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
DoSearch(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
DoSearch
|
|
|
|
Desc: Handle the pressing of the search button.
|
|
|
|
Params:
|
|
IN HWND hDlg: The search dialog box
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
if (hDlg == NULL) {
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
DWORD dwReturn = 0;
|
|
HWND hwndList = GetDlgItem(hDlg, IDC_LIST);
|
|
|
|
if (GetFocus() == hwndList
|
|
&& ListView_GetNextItem(hwndList, -1, LVNI_SELECTED) != -1) {
|
|
|
|
//
|
|
// We will get this message when we press enter in the list box,
|
|
// as IDC_SEARCH is the default button.
|
|
// So in this case we have to pretend as the user double clicked in the list
|
|
// view
|
|
//
|
|
SendNotifyMessage(hDlg, WM_COMMAND, (WPARAM)ID_VIEWCONTENTS , 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We need to get rid of the drop down list for the AUTOCOMPLETE text field.
|
|
//
|
|
SetFocus(GetDlgItem(hDlg, IDC_SEARCH));
|
|
|
|
SendMessage(GetDlgItem(hDlg, IDC_SEARCH),
|
|
WM_NEXTDLGCTL,
|
|
(WPARAM)TRUE,
|
|
(LPARAM)GetDlgItem(hDlg, IDC_SEARCH));
|
|
|
|
FlushCache();
|
|
|
|
GetCheckStatus(hDlg);
|
|
|
|
*s_szPath = 0;
|
|
*s_szPrevPath = 0;
|
|
|
|
GetDlgItemText(hDlg, IDC_PATH, s_szPath, ARRAYSIZE(s_szPath));
|
|
CSTRING::Trim(s_szPath);
|
|
|
|
SafeCpyN(s_szPrevPath, s_szPath, ARRAYSIZE(s_szPrevPath));
|
|
|
|
g_nIndex = 0;
|
|
|
|
//
|
|
// Clear the list view but do not remove the contents of the text field
|
|
//
|
|
ClearResults(hDlg, FALSE);
|
|
|
|
SetTimer(hDlg, 0, 100, NULL);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_STOP), TRUE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_SEARCH), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_NEWSEARCH), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_SAVE), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_CAPTION), FALSE);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_CHKAPP), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_CHKLAY), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_CHKSHI), FALSE);
|
|
|
|
g_bAbort = FALSE;
|
|
|
|
*g_szPresentDir = 0;
|
|
|
|
dwReturn = GetCurrentDirectory(ARRAYSIZE(g_szPresentDir), g_szPresentDir);
|
|
|
|
if (dwReturn == 0 || dwReturn >= ARRAYSIZE(g_szPresentDir)) {
|
|
assert(FALSE);
|
|
Dbg(dlError, "DoSearch GetCurrentDirectory failed");
|
|
}
|
|
|
|
Search(hDlg, s_szPath);
|
|
}
|
|
|
|
BOOL
|
|
AddNewResult(
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
AddNewResult
|
|
|
|
Desc: We found a new file that matches out search criteria, lets now add this
|
|
to the list view. This is the handler for WM_USER_NEWMATCH
|
|
|
|
Params:
|
|
IN LPARAM lParam: The lParam that comes with WM_USER_NEWMATCH. This is
|
|
a pointer to a MATCHEDENTRY
|
|
|
|
Notes: Please note that this routine will also free some members of MATCHEDENTRY that
|
|
we do not need except to populate the list view
|
|
|
|
Return:
|
|
TRUE: If we added the result fields in the list view
|
|
FALSE: Otherwise
|
|
--*/
|
|
{
|
|
PMATCHEDENTRY pmEntry = (PMATCHEDENTRY)lParam;
|
|
CSTRING strExeName;
|
|
int iImage;
|
|
HICON hIcon;
|
|
HIMAGELIST himl;
|
|
HIMAGELIST himlSm;
|
|
LVITEM lvi;
|
|
|
|
if (pmEntry == NULL) {
|
|
assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
EnterCriticalSection(&g_CritSect);
|
|
|
|
strExeName = pmEntry->strPath;
|
|
|
|
strExeName.ShortFilename();
|
|
|
|
himl = ListView_GetImageList(g_hwndSearchList, LVSIL_NORMAL);
|
|
|
|
if (!himl) {
|
|
|
|
himl = ImageList_Create(16, 15, ILC_COLOR32 | ILC_MASK, 10, 1);
|
|
|
|
if (!himl) {
|
|
return FALSE;
|
|
}
|
|
|
|
hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION));
|
|
|
|
ImageList_AddIcon(himl, hIcon);
|
|
ListView_SetImageList(g_hwndSearchList, himl, LVSIL_NORMAL);
|
|
}
|
|
|
|
himlSm = ListView_GetImageList(g_hwndSearchList, LVSIL_SMALL);
|
|
|
|
if (!himlSm) {
|
|
|
|
himlSm = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_MASK, 0, 0);
|
|
|
|
if (!himlSm) {
|
|
return FALSE;
|
|
}
|
|
|
|
hIcon = LoadIcon(0, MAKEINTRESOURCE(IDI_APPLICATION));
|
|
|
|
ImageList_AddIcon(himlSm, hIcon);
|
|
ListView_SetImageList(g_hwndSearchList, himlSm, LVSIL_SMALL);
|
|
}
|
|
|
|
//
|
|
// Get the icon for the file
|
|
//
|
|
hIcon = ExtractIcon(g_hInstance, pmEntry->strPath, 0);
|
|
|
|
if (!hIcon) {
|
|
iImage = 0;
|
|
} else {
|
|
|
|
iImage = ImageList_AddIcon(himl, hIcon);
|
|
|
|
if (iImage == -1) {
|
|
iImage = 0;
|
|
}
|
|
|
|
int iImageSm = ImageList_AddIcon(himlSm, hIcon);
|
|
|
|
assert(iImage == iImageSm);
|
|
DestroyIcon(hIcon);
|
|
}
|
|
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
|
|
lvi.pszText = strExeName;
|
|
lvi.iItem = g_nIndex;
|
|
lvi.iSubItem = SEARCH_COL_AFFECTEDFILE;
|
|
lvi.iImage = iImage;
|
|
lvi.lParam = (LPARAM)pmEntry;
|
|
|
|
INT iIndex = ListView_InsertItem(g_hwndSearchList, &lvi);
|
|
|
|
//
|
|
// Set the various result fields in the list view
|
|
//
|
|
ListView_SetItemText(g_hwndSearchList, iIndex, SEARCH_COL_PATH, pmEntry->strPath);
|
|
ListView_SetItemText(g_hwndSearchList, iIndex, SEARCH_COL_APP, pmEntry->strAppName);
|
|
ListView_SetItemText(g_hwndSearchList, iIndex, SEARCH_COL_ACTION, pmEntry->strAction);
|
|
ListView_SetItemText(g_hwndSearchList, iIndex, SEARCH_COL_DBTYPE, pmEntry->strDatabase);
|
|
|
|
//
|
|
// Remove the strings that are no longer going to be used.
|
|
// Keep the dbguid, this will be used for matching when we double click.
|
|
//
|
|
pmEntry->strAction.Release();
|
|
pmEntry->strDatabase.Release();
|
|
pmEntry->strPath.Release();
|
|
|
|
//
|
|
// Increment the index where we want to put in the next result
|
|
//
|
|
g_nIndex++;
|
|
|
|
LeaveCriticalSection(&g_CritSect);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
OnBrowse(
|
|
IN HWND hDlg
|
|
)
|
|
/*++
|
|
|
|
OnBrowse
|
|
|
|
Desc: Handles the pressing of the browse button
|
|
|
|
Params:
|
|
IN HWND hdlg: The handle to the search dialog
|
|
|
|
Return:
|
|
void
|
|
--*/
|
|
{
|
|
|
|
BROWSEINFO brInfo;
|
|
TCHAR szDir[MAX_PATH * 2] = TEXT("");
|
|
|
|
brInfo.hwndOwner = g_hwndSearchList;
|
|
brInfo.pidlRoot = NULL;
|
|
brInfo.pszDisplayName = szDir;
|
|
brInfo.lpszTitle = GetString(IDS_SELECTDIR);
|
|
brInfo.ulFlags = BIF_STATUSTEXT | BIF_RETURNONLYFSDIRS;
|
|
brInfo.lpfn = NULL;
|
|
brInfo.lParam = NULL;
|
|
|
|
LPITEMIDLIST lpIDL = SHBrowseForFolder(&brInfo);
|
|
|
|
*szDir = 0;
|
|
|
|
if (lpIDL == NULL) {
|
|
//
|
|
// The user pressed cancel
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the actual path from the pidl and free it
|
|
//
|
|
if (SHGetPathFromIDList(lpIDL, szDir)) {
|
|
|
|
ADD_PATH_SEPARATOR(szDir, ARRAYSIZE(szDir));
|
|
|
|
StringCchCat(szDir, ARRAYSIZE(szDir), TEXT("*.exe"));
|
|
SetDlgItemText(hDlg, IDC_PATH, szDir);
|
|
|
|
//
|
|
// Free the pidl
|
|
//
|
|
LPMALLOC lpMalloc;
|
|
|
|
if (SUCCEEDED(SHGetMalloc(&lpMalloc))) {
|
|
lpMalloc->Free(lpIDL);
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
|
|
} else {
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
ShowContextMenu(
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
ShowContextMenu
|
|
|
|
Desc: Shows the context menu. Handles WM_CONTEXTMENU
|
|
|
|
Params:
|
|
IN WPARAM wParam: The wParam that comes with WM_CONTEXTMENU.
|
|
|
|
|
|
Return:
|
|
--*/
|
|
{
|
|
HWND hWnd = (HWND)wParam;
|
|
|
|
if (hWnd == g_hwndSearchList) {
|
|
|
|
int iSelection = ListView_GetSelectionMark(g_hwndSearchList);
|
|
|
|
if (iSelection == -1) {
|
|
return;
|
|
}
|
|
|
|
LVITEM lvi = {0};
|
|
PMATCHEDENTRY pmMatched = NULL;
|
|
|
|
lvi.iItem = iSelection;
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_PARAM;
|
|
|
|
if (!ListView_GetItem(g_hwndSearchList, &lvi)) {
|
|
return;
|
|
}
|
|
|
|
pmMatched = (PMATCHEDENTRY)lvi.lParam;
|
|
|
|
if (pmMatched == NULL) {
|
|
assert(FALSE);
|
|
return;
|
|
}
|
|
|
|
UINT uX = LOWORD(lParam);
|
|
UINT uY = HIWORD(lParam);
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_CONTEXT));
|
|
HMENU hContext;
|
|
|
|
//
|
|
// Get the context menu for search
|
|
//
|
|
hContext = GetSubMenu(hMenu, 3);
|
|
|
|
if (hContext == NULL) {
|
|
goto End;
|
|
}
|
|
|
|
TrackPopupMenuEx(hContext,
|
|
TPM_LEFTALIGN | TPM_TOPALIGN,
|
|
uX,
|
|
uY,
|
|
g_hSearchDlg,
|
|
NULL);
|
|
|
|
End:
|
|
if (hMenu) {
|
|
DestroyMenu(hMenu);
|
|
hMenu = NULL;
|
|
}
|
|
}
|
|
}
|