/******************************************************************************* * * (C) COPYRIGHT MICROSOFT CORP., 1993-1994 * * TITLE: REGFIND.C * * VERSION: 4.0 * * AUTHOR: Tracy Sharpe * * DATE: 14 Jul 1994 * * Find routines for the Registry Editor. * *******************************************************************************/ #include "pch.h" #include "regedit.h" #include "regkey.h" #include "regresid.h" #include "reghelp.h" #include "regvalue.h" #define SIZE_FINDSPEC (max(MAXKEYNAME, MAXVALUENAME_LENGTH)) TCHAR s_FindSpecification[SIZE_FINDSPEC] = { 0 }; #define FIND_EXACT 0x00000001 #define FIND_KEYS 0x00000002 #define FIND_VALUES 0x00000004 #define FIND_DATA 0x00000008 // Initialized value is the default if we don't find last known state in the // registry. DWORD g_FindFlags = FIND_KEYS | FIND_VALUES | FIND_DATA; // Global needed to monitor the find abort dialog status. BOOL s_fContinueFind; // // Reference data for the RegFind dialog. // typedef struct _REGFINDDATA { UINT LookForCount; } REGFINDDATA; REGFINDDATA s_RegFindData; // // Association between the items of the RegFind dialog and the find flags. // typedef struct _DLGITEMFINDFLAGASSOC { int DlgItem; DWORD Flag; } DLGITEMFINDFLAGASSOC; const DLGITEMFINDFLAGASSOC s_DlgItemFindFlagAssoc[] = { IDC_WHOLEWORDONLY, FIND_EXACT, IDC_FORKEYS, FIND_KEYS, IDC_FORVALUES, FIND_VALUES, IDC_FORDATA, FIND_DATA }; const DWORD s_RegFindHelpIDs[] = { IDC_FINDWHAT, IDH_FIND_SEARCHTEXT, IDC_GROUPBOX, IDH_REGEDIT_LOOK, IDC_FORKEYS, IDH_REGEDIT_LOOK, IDC_FORVALUES, IDH_REGEDIT_LOOK, IDC_FORDATA, IDH_REGEDIT_LOOK, IDC_WHOLEWORDONLY, IDH_FIND_WHOLE, IDOK, IDH_FIND_NEXT_BUTTON, 0, 0 }; BOOL PASCAL FindCompare( LPTSTR lpString ); INT_PTR PASCAL RegFindDlgProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam ); BOOL PASCAL RegFind_OnInitDialog( HWND hWnd, HWND hFocusWnd, LPARAM lParam ); VOID PASCAL RegFind_OnCommand( HWND hWnd, int DlgItem, HWND hControlWnd, UINT NotificationCode ); BOOL PASCAL RegFindAbortProc( HWND hRegFindAbortWnd ); INT_PTR CALLBACK RegFindAbortDlgProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam ); /******************************************************************************* * * RegEdit_OnCommandFindNext * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ VOID PASCAL RegEdit_OnCommandFindNext( HWND hWnd, BOOL fForceDialog ) { UINT uErrorStringID; BOOL fError = FALSE; BOOL fSearchedToEnd; HWND hFocusWnd; LV_ITEM LVItem; TCHAR ValueName[MAXVALUENAME_LENGTH]; DWORD Type; DWORD cbValueData; TV_ITEM TVItem; TCHAR KeyName[MAXKEYNAME]; HWND hRegFindAbortWnd; HTREEITEM hTempTreeItem; UINT ExpandCounter; HKEY hRootKey; HKEY hKey; DWORD EnumIndex; DWORD cbValueName; BOOL fFoundMatch; TCHAR BestValueName[MAXVALUENAME_LENGTH]; LV_FINDINFO LVFindInfo; fSearchedToEnd = FALSE; hFocusWnd = NULL; hRegFindAbortWnd = NULL; // // Check if we're to show the find dialog. This is either due to the user // explicitly choosing the "Find" menu item or causing a "Find Next" with // the search specification being uninitialized. // if (fForceDialog || s_FindSpecification[0] == 0) { if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_REGFIND), hWnd, RegFindDlgProc) != IDOK) return; } RegEdit_SetWaitCursor(TRUE); // // Check if we're trying to finding either value names or data. If so, // then the next match might be part of the current ValueList. // if (g_FindFlags & (FIND_VALUES | FIND_DATA)) { LVItem.iItem = ListView_GetNextItem(g_RegEditData.hValueListWnd, -1, LVNI_FOCUSED); LVItem.iSubItem = 0; LVItem.mask = LVIF_TEXT; LVItem.pszText = ValueName; LVItem.cchTextMax = sizeof(ValueName)/sizeof(TCHAR); // // Walk over all of the rest of the value names attempting to find a // match. // while ((LVItem.iItem = ListView_GetNextItem(g_RegEditData.hValueListWnd, LVItem.iItem, LVNI_ALL)) != -1) { ListView_GetItem(g_RegEditData.hValueListWnd, &LVItem); // // Check if this value name meets our search specification. We'll // assume that this value name still exists. // if ((g_FindFlags & FIND_VALUES) && FindCompare(ValueName)) goto SelectListItem; // // Check if this value data meets our search specification. We'll // have to go back to the registry to determine this. // if (g_FindFlags & FIND_DATA) { if ((RegEdit_QueryValueEx(g_RegEditData.hCurrentSelectionKey, ValueName, NULL, &Type, NULL, &cbValueData) == ERROR_SUCCESS) && IsRegStringType(Type)) { // Allocate storage space PBYTE pbDataValue = (PBYTE)LocalAlloc(LPTR, cbValueData+ExtraAllocLen(Type)); if (pbDataValue) { BOOL fSuccess = FALSE; if (RegEdit_QueryValueEx(g_RegEditData.hCurrentSelectionKey, ValueName, NULL, &Type, pbDataValue, &cbValueData) == ERROR_SUCCESS) { if (Type == REG_MULTI_SZ) { EDITVALUEPARAM evp; evp.pValueData = pbDataValue; evp.cbValueData = cbValueData; if (ValueList_MultiStringToString(&evp)) { pbDataValue = evp.pValueData; } } fSuccess = FindCompare((PTSTR)pbDataValue); } LocalFree(pbDataValue); if (fSuccess) { goto SelectListItem; } } else { fError = TRUE; uErrorStringID = IDS_NOMEMORY; goto DismissRegFindAbortWnd; } } } } } // // Searching the registry (especially with this code!) is a lengthy // operation, so we must provide a way for the user to cancel the // operation. // s_fContinueFind = TRUE; if ((hRegFindAbortWnd = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_REGFINDABORT), hWnd, RegFindAbortDlgProc)) != NULL) { EnableWindow(hWnd, FALSE); // // Major hack: The following code sequence relies heavily on the // TreeView to maintain the state of the find process. Even though I'm // inserting and deleting non-visible tree items, the TreeView // currently flickers despite this. // // So, we set this internal flag and turn off the TreeView's redraw // flag. Whenever we get a WM_PAINT message for our main window, we // temporarily "let" it redraw itself then and only then. That way, // the user can move the modeless abort dialog or switch away and back // and still have the TreeView look normal. // // Yes, it's difficult at this time to fix the TreeView's paint logic. // g_RegEditData.fProcessingFind = TRUE; SetWindowRedraw(g_RegEditData.hKeyTreeWnd, FALSE); } // // Either the user wasn't trying to find value names or data or else no // matches were found. This means that we must move on to the next branch // of the registry. // // We first walk into the children of the current branch, then the // siblings, and finally pop back through the parent. // // We use the information already in the KeyTree pane as much as possible. // ExpandCounter = 0; fFoundMatch = FALSE; BestValueName[0] = '\0'; TVItem.mask = TVIF_TEXT | TVIF_STATE | TVIF_CHILDREN; TVItem.pszText = KeyName; TVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR); TVItem.hItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd); TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem); while (TRUE) { // // Check if we should cancel the find operation. If so, restore our // initial state and exit. // if (!RegFindAbortProc(hRegFindAbortWnd)) { if (ExpandCounter) { hTempTreeItem = TVItem.hItem; do { hTempTreeItem = TreeView_GetParent(g_RegEditData.hKeyTreeWnd, hTempTreeItem); } while (--ExpandCounter); TreeView_Expand(g_RegEditData.hKeyTreeWnd, hTempTreeItem, TVE_COLLAPSE | TVE_COLLAPSERESET); } goto DismissRegFindAbortWnd; } // // Does this branch have any children? This would have been determined // when the tree item was built by the routine KeyTree_ExpandBranch. // if (TVItem.cChildren) { // // The branch may have children, but it may not have been expanded // yet. // if ((hTempTreeItem = TreeView_GetChild(g_RegEditData.hKeyTreeWnd, TVItem.hItem)) == NULL) { if (!KeyTree_ExpandBranch(g_RegEditData.hKeyTreeWnd, TVItem.hItem)) goto SkipToSibling; if ((hTempTreeItem = TreeView_GetChild(g_RegEditData.hKeyTreeWnd, TVItem.hItem)) == NULL) goto SkipToSibling; ExpandCounter++; } TVItem.hItem = hTempTreeItem; } // // The branch doesn't have any children, so we'll move on to the next // sibling of the current branch. If none exists, then try finding // the next sibling of the parent branch, and so on. // else { SkipToSibling: while (TRUE) { if ((hTempTreeItem = TreeView_GetNextSibling(g_RegEditData.hKeyTreeWnd, TVItem.hItem)) != NULL) { TVItem.hItem = hTempTreeItem; break; } // // If no more parents exist, then we've finished searching the // tree. We're outta here! // if ((TVItem.hItem = TreeView_GetParent(g_RegEditData.hKeyTreeWnd, TVItem.hItem)) == NULL) { fSearchedToEnd = TRUE; goto DismissRegFindAbortWnd; } if (ExpandCounter) { ExpandCounter--; TreeView_Expand(g_RegEditData.hKeyTreeWnd, TVItem.hItem, TVE_COLLAPSE | TVE_COLLAPSERESET); } } } // // If we made it this far, then we're at the next branch of the // registry to evaluate. // TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem); // // Check if we're trying to find keys. // if (g_FindFlags & FIND_KEYS) { if (FindCompare(KeyName)) goto SelectTreeItem; } // // Check if we're trying to find value names or data. // if (g_FindFlags & (FIND_VALUES | FIND_DATA)) { // // Try to open the registry at the new current branch. // hRootKey = KeyTree_BuildKeyPath(g_RegEditData.hKeyTreeWnd, TVItem.hItem, KeyName, BKP_TOSUBKEY); if(hRootKey && RegOpenKeyEx(hRootKey,KeyName,0,KEY_QUERY_VALUE,&hKey) == ERROR_SUCCESS) { // // Here's the simple case-- we're trying to find an exact match // for a value name. We can just use the registry API to do // this for us! // if ((g_FindFlags & (FIND_VALUES | FIND_DATA | FIND_EXACT)) == (FIND_VALUES | FIND_EXACT)) { if (RegEdit_QueryValueEx(hKey, s_FindSpecification, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { lstrcpy(BestValueName, s_FindSpecification); fFoundMatch = TRUE; } } // // Bummer... we need to walk through all of the registry // value/data pairs for this key to try to find a match. Even // worse, we have to look at _all_ of the entries, not just the // first hit... we must display the first alphabetically // matching entry! // else { EnumIndex = 0; while (TRUE) { cbValueName = sizeof(ValueName)/sizeof(TCHAR); if (RegEnumValue(hKey, EnumIndex++, ValueName, &cbValueName, NULL, &Type, NULL, &cbValueData) == ERROR_SUCCESS) { PBYTE pbValueData = (g_FindFlags & FIND_DATA) ? (PBYTE)LocalAlloc(LPTR, cbValueData+ExtraAllocLen(Type)) : NULL; if (pbValueData || !(g_FindFlags & FIND_DATA)) { if (RegEdit_QueryValueEx(hKey, ValueName, NULL, &Type, pbValueData, &cbValueData) == ERROR_SUCCESS) { if (pbValueData && (Type == REG_MULTI_SZ)) { EDITVALUEPARAM evp; evp.pValueData = pbValueData; evp.cbValueData = cbValueData; if (ValueList_MultiStringToString(&evp)) { pbValueData = evp.pValueData; } } if (((g_FindFlags & FIND_VALUES) && FindCompare(ValueName)) || ((g_FindFlags & FIND_DATA) && IsRegStringType(Type) && FindCompare((PTSTR)pbValueData))) { // // We've got to check if we've found a "better" // value name to display-- one that's at the top of // the sorted list. // if (fFoundMatch) { if (lstrcmpi(BestValueName, ValueName) > 0) lstrcpy(BestValueName, ValueName); } else { lstrcpy(BestValueName, ValueName); fFoundMatch = TRUE; } } } if (pbValueData) { LocalFree(pbValueData); } } else { fError = TRUE; uErrorStringID = IDS_NOMEMORY; goto DismissRegFindAbortWnd; } } else { break; } } } RegCloseKey(hKey); if (fFoundMatch) goto SelectTreeItem; } } } SelectTreeItem: TreeView_EnsureVisible(g_RegEditData.hKeyTreeWnd, TVItem.hItem); TreeView_SelectItem(g_RegEditData.hKeyTreeWnd, TVItem.hItem); if (!fFoundMatch) hFocusWnd = g_RegEditData.hKeyTreeWnd; else { // // Right now, the TreeView_SelectItem above will cause the ValueListWnd // to update, but only after a short time delay. We want the list // immediately updated, so force the timer to go off now. // RegEdit_OnSelChangedTimer(hWnd); if (BestValueName[0] == 0) LVItem.iItem = 0; else { LVFindInfo.flags = LVFI_STRING; LVFindInfo.psz = BestValueName; LVItem.iItem = ListView_FindItem(g_RegEditData.hValueListWnd, -1, &LVFindInfo); } SelectListItem: ListView_SetItemState(g_RegEditData.hValueListWnd, -1, 0, LVIS_SELECTED | LVIS_FOCUSED); ListView_SetItemState(g_RegEditData.hValueListWnd, LVItem.iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); ListView_EnsureVisible(g_RegEditData.hValueListWnd, LVItem.iItem, FALSE); hFocusWnd = g_RegEditData.hValueListWnd; } DismissRegFindAbortWnd: RegEdit_SetWaitCursor(FALSE); if (hRegFindAbortWnd != NULL) { g_RegEditData.fProcessingFind = FALSE; SetWindowRedraw(g_RegEditData.hKeyTreeWnd, TRUE); EnableWindow(hWnd, TRUE); DestroyWindow(hRegFindAbortWnd); } if (hFocusWnd != NULL) SetFocus(hFocusWnd); if (fError) { InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(uErrorStringID), MAKEINTRESOURCE(IDS_REGEDIT), MB_ICONERROR | MB_OK); } if (fSearchedToEnd) InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(IDS_SEARCHEDTOEND), MAKEINTRESOURCE(IDS_REGEDIT), MB_ICONINFORMATION | MB_OK); } /******************************************************************************* * * FindCompare * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ BOOL PASCAL FindCompare( LPTSTR lpString ) { if (g_FindFlags & FIND_EXACT) return lstrcmpi(lpString, s_FindSpecification) == 0; else return StrStrI(lpString, s_FindSpecification) != NULL; } /******************************************************************************* * * RegFindDlgProc * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ INT_PTR PASCAL RegFindDlgProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam ) { switch (Message) { HANDLE_MSG(hWnd, WM_INITDIALOG, RegFind_OnInitDialog); HANDLE_MSG(hWnd, WM_COMMAND, RegFind_OnCommand); case WM_HELP: WinHelp(((LPHELPINFO) lParam)-> hItemHandle, g_pHelpFileName, HELP_WM_HELP, (ULONG_PTR) s_RegFindHelpIDs); break; case WM_CONTEXTMENU: WinHelp((HWND) wParam, g_pHelpFileName, HELP_CONTEXTMENU, (ULONG_PTR) s_RegFindHelpIDs); break; default: return FALSE; } return TRUE; } /******************************************************************************* * * RegFind_OnInitDialog * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ BOOL PASCAL RegFind_OnInitDialog( HWND hWnd, HWND hFocusWnd, LPARAM lParam ) { UINT Counter; int DlgItem; // // Initialize the "Find What" edit control. // SendDlgItemMessage(hWnd, IDC_FINDWHAT, EM_SETLIMITTEXT, SIZE_FINDSPEC, 0); SetDlgItemText(hWnd, IDC_FINDWHAT, s_FindSpecification); // // Initialize the checkboxes based on the state of the global find flags. // s_RegFindData.LookForCount = 0; for (Counter = 0; Counter < sizeof(s_DlgItemFindFlagAssoc) / sizeof(DLGITEMFINDFLAGASSOC); Counter++) { if (g_FindFlags & s_DlgItemFindFlagAssoc[Counter].Flag) { DlgItem = s_DlgItemFindFlagAssoc[Counter].DlgItem; CheckDlgButton(hWnd, DlgItem, TRUE); if (DlgItem >= IDC_FORKEYS && DlgItem <= IDC_FORDATA) s_RegFindData.LookForCount++; } } return TRUE; UNREFERENCED_PARAMETER(hFocusWnd); UNREFERENCED_PARAMETER(lParam); } /******************************************************************************* * * RegFind_OnCommand * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/ VOID PASCAL RegFind_OnCommand( HWND hWnd, int DlgItem, HWND hControlWnd, UINT NotificationCode ) { UINT Counter; if (DlgItem >= IDC_FORKEYS && DlgItem <= IDC_FORDATA) { if (NotificationCode == BN_CLICKED) { IsDlgButtonChecked(hWnd, DlgItem) ? s_RegFindData.LookForCount++ : s_RegFindData.LookForCount--; goto EnableFindNextButton; } } else { switch (DlgItem) { case IDC_FINDWHAT: if (NotificationCode == EN_CHANGE) { EnableFindNextButton: EnableWindow(GetDlgItem(hWnd, IDOK), s_RegFindData.LookForCount > 0 && SendDlgItemMessage(hWnd, IDC_FINDWHAT, WM_GETTEXTLENGTH, 0, 0) != 0); } break; case IDOK: GetDlgItemText(hWnd, IDC_FINDWHAT, s_FindSpecification, sizeof(s_FindSpecification)/sizeof(TCHAR)); for (Counter = 0; Counter < sizeof(s_DlgItemFindFlagAssoc) / sizeof(DLGITEMFINDFLAGASSOC); Counter++) { if (IsDlgButtonChecked(hWnd, s_DlgItemFindFlagAssoc[Counter].DlgItem)) g_FindFlags |= s_DlgItemFindFlagAssoc[Counter].Flag; else g_FindFlags &= ~s_DlgItemFindFlagAssoc[Counter].Flag; } // FALL THROUGH case IDCANCEL: EndDialog(hWnd, DlgItem); break; } } } /******************************************************************************* * * RegFindAbortProc * * DESCRIPTION: * * PARAMETERS: * (returns), TRUE to continue the find, else FALSE to cancel. * *******************************************************************************/ BOOL PASCAL RegFindAbortProc( HWND hRegFindAbortWnd ) { while (s_fContinueFind && MessagePump(hRegFindAbortWnd)) ; return s_fContinueFind; } /******************************************************************************* * * RegAbortDlgProc * * DESCRIPTION: * Callback procedure for the RegAbort dialog box. * * PARAMETERS: * hWnd, handle of RegAbort window. * Message, * wParam, * lParam, * (returns), * *******************************************************************************/ INT_PTR CALLBACK RegFindAbortDlgProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam ) { switch (Message) { case WM_INITDIALOG: break; case WM_CLOSE: case WM_COMMAND: s_fContinueFind = FALSE; break; default: return FALSE; } return TRUE; }