Figure 1 CLEANREG.CPP and CLNREGUI.CPP
//==========================================
// CLEANREG - Matt Pietrek 1996
// Microsoft Systems Journal, September 1996
// FILE: CLEANREG.CPP
//==========================================
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <tchar.h>
#pragma hdrstop
#include "cleanreg.h"
// From CLNREGUI.CPP
void AddItemToUI(LPTSTR pszRegPath, LPTSTR pszValueName, LPTSTR pszFilename);
BOOL g_fBeRealistic = TRUE; // Eliminate unlikely (but possible) filenames
//
// Checks to see if the file exists on a fixed drive, and if not,
// adds it to the user interface
//
void CheckForExistence(LPTSTR pszRegPath, LPTSTR pszValueName, LPTSTR pszFile)
{
// See if the file is on a floppy or CD-ROM drive. Don't tell about, if so
TCHAR szDriveRoot[4];
_tcsncpy( szDriveRoot, pszFile, 3 );
szDriveRoot[3] = 0;
UINT driveType = GetDriveType(szDriveRoot);
if ( (driveType != DRIVE_FIXED) && (driveType != DRIVE_REMOTE) )
return;
// A quick'n'easy way to see if a file exists. Bail out if so.
if ( 0xFFFFFFFF != GetFileAttributes( pszFile ) )
return;
AddItemToUI( pszRegPath, pszValueName, pszFile ); // Not found! Add to UI
}
//
// Given a string as input, this routine does its best to try and extract
// a filename portion in the beginning. A "filename" means that there's
// a ":\" near the beginning. The function handles filenames in quotes.
// If the g_fBeRealistic variable is TRUE, it assumes that characters like
// '/' and '-' aren't part of the filename, although they're technically
// legal.
//
BOOL CheckForFilename( LPTSTR psz, LPTSTR szOutput, UINT cbOutput )
{
if ( lstrlen(psz) <= 2 ) // toss out strings that are too short
return FALSE;
BOOL fBeginQuote = FALSE;
LPTSTR pszStartFilename = psz;
if ( *pszStartFilename == '"' ) // Does the string start with a quote?
{ // If so, adjust accordingly. We'll
fBeginQuote = TRUE; // assume that there must be an end quote
pszStartFilename++; // for it to be a legal filename.
}
LPTSTR pszEndName; // Pointer to one char past the end of the name
if ( fBeginQuote ) // The string started with quotes. Find the other one
{
pszEndName = _tcschr( pszStartFilename, '"' );
if ( !pszEndName )
return FALSE;
pszEndName--; // Strip off spaces at the end
while ( (pszEndName > pszStartFilename) && (*pszEndName == ' ') )
pszEndName--;
pszEndName++;
}
else // String didn't start with a quote
{
// Look for the first non-legal filename character
pszEndName = _tcspbrk( psz, TEXT("<>|/\"") );
if ( pszEndName ) // We found a non-legal character, so the
{ // filename must end before this
// Now strip off any whitespace
pszEndName--;
while ( (pszEndName > pszStartFilename) && (*pszEndName == ' '))
pszEndName--;
pszEndName++;
}
else
pszEndName = psz + lstrlen(psz); // No non-legal chars found
}
// At this point, pszStartFilename points to the beginning of the possible
// filename, and pszEndName points 1 character past the last character
// of the possible filename.
if ( pszEndName - pszStartFilename < 2 ) // Are the start and end far
return FALSE; // enough apart to be legal?
// Check to see if the 2nd and 3rd characters are ":\"
if ( 0 != _tcsnicmp( pszStartFilename+1, TEXT(":\\"), 2 ) )
return FALSE;
if ( g_fBeRealistic ) // If set, toss out unlikely characters
{
LPTSTR pszSearch = _tcspbrk( pszStartFilename, TEXT("^/-%,;") );
// Did we find something, and was it before what we're considering
// the end of the filename to be?
if ( pszSearch && (pszSearch < pszEndName) )
{
// Now strip off any whitespace
pszSearch--;
while ( (pszSearch > pszStartFilename) && (*pszSearch == ' ') )
pszSearch--;
pszEndName = pszSearch+1;
}
}
// Make sure the string we found won't overflow the input buffer
unsigned nameLen = pszEndName - pszStartFilename;
if ( nameLen >= cbOutput )
return FALSE;
// copy the filename string to the input buffer
memcpy( szOutput, pszStartFilename, nameLen * sizeof(TCHAR) );
szOutput[ nameLen ] = 0; // Null terminate it!
return TRUE;
}
//
// Routine that enumerates through all the values in a registry, looking
// for REG_SZ's that begin with filenames. Then, the routine recurses
// to iterate through its child nodes.
//
void ScanRegNode( HKEY hKey, LPTSTR pszRegPath )
{
DWORD regEnumIndex = 0;
while ( TRUE )
{
TCHAR szValueName[ MAX_PATH ];
DWORD cbValueNameSize = SIZEOFARRAY(szValueName);
TCHAR szValue[ MAX_PATH ];
DWORD cbValueSize = SIZEOFARRAY(szValue);
DWORD type;
LONG result;
result = RegEnumValue( hKey, regEnumIndex++,
szValueName, &cbValueNameSize,
0, &type, (LPBYTE)szValue, &cbValueSize );
if ( ERROR_NO_MORE_ITEMS == result )
break;
if ( REG_SZ != type )
continue;
TCHAR szFilename[MAX_PATH];
if ( CheckForFilename( szValue,
szFilename, SIZEOFARRAY(szFilename)) )
CheckForExistence( pszRegPath, szValueName, szFilename );
}
// ================ Now iterate through all the child nodes ===============
regEnumIndex = 0;
while ( TRUE )
{
TCHAR szSubkey[ MAX_PATH ];
DWORD cbSubkeySize = SIZEOFARRAY(szSubkey);
LONG result;
result = RegEnumKeyEx( hKey, regEnumIndex++, szSubkey, &cbSubkeySize,
0, 0, 0, 0 );
if ( ERROR_NO_MORE_ITEMS == result )
break;
if ( 0 != result ) // Some other error? Ignore and continue
continue;
HKEY hSubkey;
result = RegOpenKeyEx( hKey, szSubkey, 0, KEY_READ, &hSubkey );
if ( ERROR_SUCCESS != result )
continue;
// Create a fully qualified subkey name to pass to ScanRegNode
LPTSTR pszRegPathSubkey
= new TCHAR[lstrlen(pszRegPath) + cbSubkeySize + 2];
wsprintf( pszRegPathSubkey, TEXT("%s\\%s"), pszRegPath, szSubkey );
// Recurse to find any child keys
ScanRegNode( hSubkey, pszRegPathSubkey );
delete []pszRegPathSubkey;
RegCloseKey( hSubkey );
}
}
void SearchRegistryForMissingFiles(void)
{
// Start the scan at the top level. Note that we don't do keys that
// are mirrored elsewhere (e.g., HKEY_CLASSES_ROOT), or that we
// shouldn't muck with (HKEY_USERS)
ScanRegNode( HKEY_CURRENT_USER, TEXT("\\HKEY_CURRENT_USER") );
ScanRegNode( HKEY_LOCAL_MACHINE, TEXT("\\HKEY_LOCAL_MACHINE") );
}
//
// Recursive routine to delete a key and all of its subkeys. We can't just
// use RegDeleteKey, since Windows NT doesn't do recursive deletions. (For
// once, Windows 95 did something better than Windows NT!). Based on PSS
// article Q142491
//
LONG RecursiveRegDeleteKey( HKEY hKey, LPTSTR pszSubkey )
{
HKEY hSubkey;
LONG result = RegOpenKeyEx( hKey, pszSubkey, 0,
KEY_ENUMERATE_SUB_KEYS | DELETE, &hSubkey );
while ( ERROR_SUCCESS == result )
{
TCHAR szSubkey[ MAX_PATH ];
DWORD cbSubkeySize = SIZEOFARRAY(szSubkey);
result = RegEnumKeyEx( hSubkey, 0, szSubkey, &cbSubkeySize,
0, 0, 0, 0);
if ( ERROR_NO_MORE_ITEMS == result )
break;
if ( ERROR_SUCCESS != result )
return result;
result = RecursiveRegDeleteKey( hSubkey, szSubkey );
}
RegCloseKey( hSubkey );
result = RegDeleteKey( hKey, pszSubkey );
return result;
}
// Data structures for mapping string representation of root registry keys
// back to their binary values
struct REG_ROOT_NAME
{
LPTSTR pszName;
HKEY hKey;
};
REG_ROOT_NAME RegRootKeys[] =
{
{ TEXT("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT },
{ TEXT("HKEY_CURRENT_USER"), HKEY_CURRENT_USER },
{ TEXT("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE },
{ TEXT("HKEY_USERS"), HKEY_USERS },
{ TEXT("HKEY_PERFORMANCE_DATA"),HKEY_PERFORMANCE_DATA },
{ TEXT("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG },
{ TEXT("HKEY_DYN_DATA"), HKEY_DYN_DATA },
};
//
// Given a registry path, deletes the specified key and everything below it.
// Expects a string in the form \Key1\Key2\Key3\
// A value can be specified via the form \[value: xxx]\
// Thus, the path \Key1\Key2\[value: xxx]\string can be passed
//
BOOL DeleteRegistryPath( LPTSTR _pszRegPath )
{
if ( !_pszRegPath || *_pszRegPath != '\\' )
return FALSE;
// Make a copy of the string so that we can munge it
LPTSTR pszRegPath = _tcsdup( _pszRegPath );
if ( !pszRegPath )
return FALSE;
//
// First, break out the root portion of the path (e.g., "\HKEY_CLASSES\")
//
LPTSTR pszRootKey, pszRootKeyEnd, pszSubkey=0;
pszRootKey = pszRegPath+1;
pszRootKeyEnd = _tcschr( pszRootKey, '\\' );
if ( !pszRootKeyEnd )
{
free( pszRegPath );
return FALSE;
}
else
{
*pszRootKeyEnd = 0;
pszSubkey = pszRootKeyEnd + 1;
}
// Look up the predefined HKEY
HKEY hKeyRoot = 0;
for ( unsigned i = 0; i < SIZEOFARRAY(RegRootKeys); i++ )
if ( 0 == _tcscmp( pszRootKey, RegRootKeys[i].pszName ) )
{
hKeyRoot = RegRootKeys[i].hKey;
break;
}
if ( 0 == hKeyRoot ) // Huh??? Not found. This shouldn't happen
{
free( pszRegPath );
return FALSE;
}
//
// Next, determine if there's a value included in the reg path. If so,
// isolate it and strip it off.
//
LPTSTR pszValue = _tcsstr( pszSubkey, TEXT("\\[value: ") );
if ( pszValue ) // Did we find a "\[value: xxx]" somewhere???
{
*pszValue = 0; // Null terminate pszRegPath before the "\[value: "
pszValue += 9; // Advance the pointer past "\[value: "
// Now strip off the terminating ']'
LPTSTR pszEndValue = _tcschr( pszValue, ']' );
if ( pszEndValue )
*pszEndValue = 0;
// Check for the special case of "<default>", which is the default value
if ( 0 == _tcscmp( pszValue, TEXT("<default>") ) )
pszValue = TEXT("");
}
//
// At this point, we have a predefined root key, a subkey string, and an
// optional value string. If we have a value string, delete the value
// from the registry. Otherwise, delete the key.
//
BOOL fSuccess = FALSE; // Assume the worst...
if ( pszValue ) // We're just deleting a value (RegDeleteValue)
{
HKEY hSubkey;
if ( ERROR_SUCCESS == RegOpenKeyEx( hKeyRoot, pszSubkey,
0, KEY_ALL_ACCESS, &hSubkey ) )
{
if ( ERROR_SUCCESS == RegDeleteValue( hSubkey, pszValue ) )
fSuccess = TRUE;
RegCloseKey( hSubkey );
}
}
else // We're deleting an entire key
{
// Split off the last node, and null terminate the prior nodes
LPTSTR pszSubkey2 = _tcsrchr( pszSubkey, '\\' );
*pszSubkey2++ = 0;
HKEY hSubkey;
if ( ERROR_SUCCESS == RegOpenKeyEx( hKeyRoot, pszSubkey,
0, KEY_ENUMERATE_SUB_KEYS | DELETE,
&hSubkey ) )
{
if ( ERROR_SUCCESS == RecursiveRegDeleteKey( hSubkey, pszSubkey2) )
fSuccess = TRUE;
RegCloseKey( hSubkey );
}
}
free( pszRegPath );
return fSuccess;
}
CLNREGUI.CPP
//==========================================
// CLEANREG - Matt Pietrek 1996
// Microsoft Systems Journal, September 1996
// FILE: CLNREGUI.CPP
//==========================================
#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#pragma hdrstop
#include "clnregui.h"
#include "cleanreg.h"
// Helper function prototypes
void Handle_WM_COMMAND(HWND hDlg, WPARAM wParam, LPARAM lParam);
void Handle_WM_INITDIALOG(HWND hDlg);
void Handle_WM_CLOSE( HWND hDlg );
BOOL Handle_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam );
void Handle_ListBox_Cmd(HWND hDlg, WORD wNotifyCode, WORD wID, HWND hwndCtl);
BOOL Handle_TreeView_Notify( HWND hDlg, int idCtrl, LPNMHDR pnmh );
BOOL CALLBACK CleanRegDlgProc( HWND, UINT, WPARAM, LPARAM );
HTREEITEM TVAppendItem( HWND hWnd, HTREEITEM hTi, LPTSTR psz );
BOOL GetRegPathForHTI(HWND hWnd, HTREEITEM hTi, PTSTR pszPath, unsigned cbPath);
void DeleteRegPathAfterQuerying( HWND hWnd, LPTSTR pszRegPath );
void RefreshViews(void);
HWND g_hWndTree=0; // Global variables (Yes, the horror!) for speed
HWND g_hWndList = 0;
HTREEITEM g_hTiRoot;
BOOL g_fRefreshAfterDelete = TRUE;
TCHAR g_szRootName[] = TEXT("My Registry");
TCHAR g_szCopyright[] = TEXT("CleanReg - Matt Pietrek, 1996 for MSJ");
TCHAR g_szHelpAbout[] =
TEXT("CleanReg searches the registry for filenames that aren't present on ")
TEXT("a fixed or network drive. These files are listed in the top listbox. ")
TEXT("When a file is selected, the bottom treeview expands to show the ")
TEXT("registry path where the filename was found.\r\n\r\n")
TEXT("Nodes in the treeview can be selected. All keys and values below and ")
TEXT("including the node can be deleted via the <delete> key. Use this ")
TEXT("option with care!!!\r\n\r\n")
TEXT("CleanReg is from the September 1996 issue of the Microsoft Systems ")
TEXT("Journal. Please refer to that issue (which will be on the MSDN ")
TEXT("CD-ROM) for all documentation questions.");
int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
PSTR lpszCmdLine, int nCmdShow )
{
InitCommonControls(); // Gotta do this for treeview controls
// Bring up the user interface (A dialog box? What a surprise!)
DialogBox( hInstance, MAKEINTRESOURCE(IDD_DIALOG_CLEANREG),
0, (DLGPROC)CleanRegDlgProc );
return 0;
}
//=================== Start of message handling routines ====================
BOOL CALLBACK CleanRegDlgProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch ( msg )
{
case WM_COMMAND:
Handle_WM_COMMAND( hDlg, wParam, lParam ); return TRUE;
case WM_INITDIALOG:
Handle_WM_INITDIALOG( hDlg ); return TRUE;
case WM_CLOSE:
Handle_WM_CLOSE( hDlg ); break;
case WM_NOTIFY:
return Handle_WM_NOTIFY( hDlg, wParam, lParam );
}
return FALSE;
}
void Handle_WM_COMMAND( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
WORD wNotifyCode = HIWORD(wParam);
WORD wID = LOWORD(wParam);
HWND hwndCtl = (HWND)lParam;
switch ( wID )
{
case IDC_LIST:
Handle_ListBox_Cmd( hDlg, wNotifyCode, wID, hwndCtl ); break;
case IDC_ABOUT:
MessageBox( hDlg, g_szHelpAbout, g_szCopyright, MB_OK ); break;
case IDC_CHECK_SANE:
if ( BN_CLICKED == wNotifyCode )
{
g_fBeRealistic = (BOOL)IsDlgButtonChecked(hDlg,IDC_CHECK_SANE);
RefreshViews(); // Valid file list may have changed, so update
}
case IDC_CHECK_UPDATE:
if ( BN_CLICKED == wNotifyCode )
g_fRefreshAfterDelete =
(BOOL)IsDlgButtonChecked( hDlg,IDC_CHECK_UPDATE );
break;
}
}
void Handle_WM_INITDIALOG(HWND hDlg)
{
g_hWndTree = GetDlgItem( hDlg, IDC_TREE ); // Save control HWND's in
g_hWndList = GetDlgItem( hDlg, IDC_LIST ); // global variables for speed
CheckDlgButton( hDlg, IDC_CHECK_SANE, // Set checkboxes appropriately
g_fBeRealistic ? BST_CHECKED : BST_UNCHECKED );
CheckDlgButton( hDlg, IDC_CHECK_UPDATE,
g_fRefreshAfterDelete ? BST_CHECKED : BST_UNCHECKED );
RefreshViews(); // Fill treeviews with initial values
}
void Handle_WM_CLOSE( HWND hDlg )
{
EndDialog(hDlg, 0);
}
// Tree controls like to use WM_NOTIFY, rather than WM_COMMAND
BOOL Handle_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
int idCtrl = wParam;
LPNMHDR pnmh = (LPNMHDR)lParam;
if ( IDC_TREE == idCtrl )
return Handle_TreeView_Notify( hDlg, idCtrl, pnmh );
return FALSE;
}
void Handle_ListBox_Cmd(HWND hDlg, WORD wNotifyCode, WORD wID, HWND hwndCtl)
{
// If the file listbox selection changes, expand the appropriate tree item
if ( (g_hWndList == hwndCtl) && (wNotifyCode == LBN_SELCHANGE) )
{
// Figure out which listbox item was selected
LRESULT lbIndex = SendMessage( hwndCtl, LB_GETCURSEL, 0, 0 );
if ( LB_ERR == lbIndex )
return;
// Now get the treeview HTREEITEM that we associated with this entry
HTREEITEM hTi=
(HTREEITEM)SendMessage(hwndCtl, LB_GETITEMDATA, (WPARAM)lbIndex, 0);
// Set treeview selection, based on the HTREEITEM we just retrieved
TreeView_Select( g_hWndTree, hTi, TVGN_CARET );
SetFocus( g_hWndTree ); // Set focus to the treeview for
// convenience
}
}
BOOL Handle_TreeView_Notify( HWND hDlg, int idCtrl, LPNMHDR pnmh )
{
if ( TVN_KEYDOWN == pnmh->code ) // We want keystrokes
{
TV_KEYDOWN * ptvkd = (TV_KEYDOWN *)pnmh;
if ( VK_DELETE == ptvkd->wVKey ) // Was it the <delete> key?
{
TCHAR szRegPath[1024]; // Hope this is big enough!
if ( GetRegPathForHTI( g_hWndTree,
TreeView_GetSelection(g_hWndTree),
szRegPath,
sizeof(szRegPath)/sizeof(TCHAR) ) )
{
DeleteRegPathAfterQuerying( hDlg, szRegPath );
}
}
}
return TRUE;
}
//====================== Start of helper routines ===========================
//
// Do a complete update of the UI, based on rescanning the registry
//
void RefreshViews(void)
{
HCURSOR hCursOld = SetCursor( LoadCursor(0, IDC_WAIT) ); // hourglass!
// Clear out the listbox and tell it to not redraw after each addition
SendMessage( g_hWndList, LB_RESETCONTENT, 0, 0 );
SendMessage( g_hWndList, WM_SETREDRAW, FALSE, 0 );
// Clear out the treeview, and add the root treeview node
TreeView_DeleteAllItems( g_hWndTree );
g_hTiRoot = TVAppendItem( g_hWndTree, TVI_ROOT, g_szRootName );
SearchRegistryForMissingFiles(); // Get down to the real work
SendMessage( g_hWndList, WM_SETREDRAW, TRUE, 0 ); // Let listbox redraw
SetCursor( hCursOld );
}
BOOL TVGetItemText( HWND hWnd, HTREEITEM hTi,
LPTSTR pszText, unsigned cbText )
{
TV_ITEM tvi;
tvi.mask = TVIF_TEXT;
tvi.hItem = hTi;
tvi.pszText = pszText;
tvi.cchTextMax = cbText;
return TreeView_GetItem( hWnd, &tvi );
}
// Append a new child node to the specified HTREEITEM
HTREEITEM TVAppendItem( HWND hWnd, HTREEITEM hTi, LPTSTR psz )
{
// A helper function that appends a string to the end of the list
// of subnodes under a specified node (hTi).
TV_INSERTSTRUCT tvis;
memset( &tvis, 0, sizeof(tvis) );
tvis.hParent = hTi;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_TEXT;
tvis.item.pszText = psz;
return TreeView_InsertItem( hWnd, &tvis );
}
//
// Given an HTREEITEM, return a '\' delimited path to it (e.g., "\a\b\c\d")
//
BOOL GetRegPathForHTI(HWND hWnd, HTREEITEM hTi, LPTSTR pszPath, unsigned cbPath)
{
TCHAR szNode[MAX_PATH];
unsigned cbThisNode =0, cbSoFar = 0;
while ( hTi )
{
if ( !TVGetItemText( hWnd, hTi, szNode, sizeof(szNode)/sizeof(TCHAR)))
return FALSE;
cbThisNode = lstrlen(szNode) + 1;
cbSoFar += cbThisNode;
if ( cbSoFar >= cbPath )
return FALSE;
memmove( pszPath + cbThisNode, pszPath, cbSoFar * sizeof(TCHAR) );
pszPath[0] = '\\';
memcpy( pszPath+1, szNode, (cbThisNode-1) * sizeof(TCHAR) );
hTi = TreeView_GetParent( hWnd, hTi );
}
pszPath[cbSoFar] = 0; // Null terminate the string
return TRUE;
}
//
// Given a '\' path (e.g., "\a\b\c\d"), find its HTREEITEM in the treeview.
// If the node doesn't exist, build it! See also GetRegPathForHTI()
//
HTREEITEM GetHTIForRegPath( HWND hWnd, HTREEITEM hTi, LPTSTR pszRegPath )
{
//
// Fill in the szKey variable with the name of the current registry
// node being searched for. The nodes are separated by '\'s. The
// szKey variable is a string representing one registry key node, and
// without any '\'s
//
TCHAR szKey[MAX_PATH];
LPTSTR pszSlash = _tcschr( pszRegPath, '\\' );
if ( pszSlash )
{
unsigned len = pszSlash - pszRegPath;
memcpy( szKey, pszRegPath, len * sizeof(TCHAR) );
szKey[len] = 0;
}
else // No slash found, so it's the last node found. Just copy it.
_tcscpy( szKey, pszRegPath );
//
// Now look through the child nodes of the HTREEITEM that we're currently
// working on. If we find it, but there's still more nodes in pszRegpath,
// recurse to find the next node in the registry path.
//
HTREEITEM hTiScan;
hTiScan = TreeView_GetNextItem( hWnd, hTi, TVGN_CHILD );
while ( hTiScan )
{
TCHAR szScanItemText[MAX_PATH];
if ( TVGetItemText( hWnd, hTiScan, szScanItemText,
sizeof(szScanItemText)/sizeof(TCHAR) ) )
{
if ( 0 == _tcscmp( szKey, szScanItemText ) )
{
if ( pszSlash )
return GetHTIForRegPath( hWnd, hTiScan, pszSlash+1 );
else
return hTiScan;
}
}
hTiScan = TreeView_GetNextItem( hWnd, hTiScan, TVGN_NEXT );
}
//
// If we get here, we didn't find the item, so create it
//
HTREEITEM hTiNew = TVAppendItem( hWnd, hTi, szKey );
if ( !hTiNew )
return 0;
//
// If there's still nodes left in pszRegpath, recurse so that they can
// be created. Otherwise, return the HTREEITEM of the new treeview item.
//
if ( pszSlash )
return GetHTIForRegPath( hWnd, hTiNew, pszSlash+1 );
else
return hTiNew;
}
//
// Called by CheckForExistence in CLEANREG.CPP when a non-existent file is
// found in the registry. Add the filename to the listbox and treeview.
//
void AddItemToUI( LPTSTR pszRegPath, LPTSTR pszValueName, LPTSTR pszFilename )
{
// Get an HTREEITEM for the registry path, making the nodes if necessary
HTREEITEM hTi = GetHTIForRegPath( g_hWndTree,
g_hTiRoot,
pszRegPath+1 );
// Make a new node representing the value portion (in []'s), and
// add it underneath the actual keys. If the value name is empty, then
// it's the default value, so call it "<default>"
TCHAR szValueNode[MAX_PATH*2];
wsprintf( szValueNode, TEXT("[value: %s]"),
*pszValueName ? pszValueName : TEXT("<default>") );
hTi = TVAppendItem( g_hWndTree, hTi, szValueNode );
// now put the filename portion of the string under its value node
hTi = TVAppendItem( g_hWndTree, hTi, pszFilename );
// Add the filename to the listbox, and associate the HTREEITEM of the
// filename as it appears in the treeview.
DWORD lbIndex;
lbIndex = SendMessage( g_hWndList, LB_ADDSTRING, 0, (LPARAM) pszFilename );
SendMessage( g_hWndList, LB_SETITEMDATA, (WPARAM)lbIndex, (LPARAM)hTi );
}
//
// If the user hits the <delete> key on an item in the treeview, this routine
// queries the user to make sure it's OK to delete the registry entry. If
// the user says "OK", it calls DeleteRegistryPath() in CLEANREG.CPP
//
void DeleteRegPathAfterQuerying( HWND hWnd, LPTSTR pszRegPath )
{
if ( *pszRegPath != '\\' )
return;
// Make sure that pszRegPath starts with "My Registry" (or whatever we've
// decided to call the root node in the treeview control).
if ( 0 != _tcsncmp(pszRegPath+1, g_szRootName, SIZEOFARRAY(g_szRootName)-1) )
return;
// Advance past the first node in pszRegPath. It's just there for the
// treeview control, and not part of the actual registry path.
pszRegPath += SIZEOFARRAY(g_szRootName);
if ( IDOK == MessageBox( hWnd, pszRegPath, TEXT("Deleting:"),
MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON2 ) )
{
if ( DeleteRegistryPath( pszRegPath ) )
{
if ( g_fRefreshAfterDelete )
RefreshViews();
}
else
MessageBox( hWnd, TEXT("Error in deleting"), 0, MB_OK );
}
}