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.
2003 lines
53 KiB
2003 lines
53 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 1993-1994
|
|
*
|
|
* TITLE: REGKEY.C
|
|
*
|
|
* VERSION: 4.01
|
|
*
|
|
* AUTHOR: Tracy Sharpe
|
|
*
|
|
* DATE: 05 Mar 1994
|
|
*
|
|
* KeyTreeWnd TreeView routines for the Registry Editor.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "pch.h"
|
|
#include "regedit.h"
|
|
#include "regkey.h"
|
|
#include "regvalue.h"
|
|
#include "regresid.h"
|
|
|
|
#define MAX_KEYNAME_TEMPLATE_ID 100
|
|
|
|
#define SELCHANGE_TIMER_ID 1
|
|
|
|
#define REFRESH_DPA_GROW 16
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeDelete(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
);
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeRename(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
);
|
|
|
|
int
|
|
WINAPI
|
|
DPACompareKeyNames(
|
|
LPVOID lpString1,
|
|
LPVOID lpString2,
|
|
LPARAM lParam
|
|
);
|
|
|
|
HTREEITEM
|
|
PASCAL
|
|
KeyTree_InsertItem(
|
|
HWND hKeyTreeWnd,
|
|
HTREEITEM hParent,
|
|
HTREEITEM hInsertAfter,
|
|
LPCTSTR lpText,
|
|
UINT fHasKids,
|
|
LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
PASCAL
|
|
DoesKeyHaveKids(
|
|
HKEY hKey,
|
|
LPTSTR lpKeyName
|
|
);
|
|
|
|
VOID
|
|
PASCAL
|
|
KeyTree_EditLabel(
|
|
HWND hKeyTreeWnd,
|
|
HTREEITEM hTreeItem
|
|
);
|
|
|
|
BOOL
|
|
PASCAL
|
|
KeyTree_CanDeleteOrRenameItem(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
);
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnNewKey
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnNewKey(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
TCHAR KeyName[MAXKEYNAME*2];
|
|
UINT cchKeyName = 0;
|
|
HKEY hRootKey;
|
|
HKEY hKey;
|
|
UINT ErrorStringID;
|
|
BOOL fNewKeyIsOnlyChild;
|
|
UINT NewKeyNameID;
|
|
HKEY hNewKey;
|
|
HTREEITEM hNewTreeItem;
|
|
TV_ITEM TVItem;
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( g_RegEditData.hKeyTreeWnd,
|
|
hTreeItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOSUBKEY);
|
|
cchKeyName = lstrlen(KeyName);
|
|
|
|
if(RegOpenKeyEx(hRootKey,KeyName,0,KEY_CREATE_SUB_KEY,&hKey) != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Get the text of the selected tree item so that we can display
|
|
// a more meaningful error message.
|
|
//
|
|
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.hItem = hTreeItem;
|
|
TVItem.pszText = (LPTSTR) KeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(KeyName);
|
|
|
|
TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
|
|
ErrorStringID = IDS_NEWKEYPARENTOPENFAILED;
|
|
goto error_ShowDialog;
|
|
|
|
}
|
|
|
|
TVItem.mask = TVIF_STATE | TVIF_CHILDREN;
|
|
TVItem.hItem = hTreeItem;
|
|
TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
|
|
fNewKeyIsOnlyChild = FALSE;
|
|
|
|
if (TVItem.cChildren == FALSE) {
|
|
|
|
//
|
|
// The selected key doesn't have any subkeys, so we can't do an expand
|
|
// on it just yet. We'll just set a flag and later tag it with a
|
|
// plus/minus icon and expand it.
|
|
//
|
|
|
|
fNewKeyIsOnlyChild = TRUE;
|
|
|
|
}
|
|
|
|
else if (!(TVItem.state & TVIS_EXPANDED)) {
|
|
|
|
//
|
|
// The selected key isn't expanded. Do it now so that we can do an
|
|
// in-place edit and don't reenumerate the "New Key #xxx" after we do
|
|
// the RegCreateKey.
|
|
//
|
|
|
|
TreeView_Expand(g_RegEditData.hKeyTreeWnd, hTreeItem, TVE_EXPAND);
|
|
|
|
}
|
|
|
|
if (RegEdit_GetTemporaryKeyName(hWnd, KeyName, ARRAYSIZE(KeyName), hKey))
|
|
{
|
|
if((cchKeyName + lstrlen(KeyName) + 1) < MAXKEYNAME)
|
|
{
|
|
if (RegCreateKey(hKey, KeyName, &hNewKey) != ERROR_SUCCESS)
|
|
{
|
|
ErrorStringID = IDS_NEWKEYCANNOTCREATE;
|
|
goto error_CloseKey;
|
|
|
|
}
|
|
|
|
RegCloseKey(hNewKey);
|
|
|
|
if (fNewKeyIsOnlyChild)
|
|
{
|
|
TVItem.mask = TVIF_CHILDREN;
|
|
TVItem.cChildren = TRUE;
|
|
TreeView_SetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
|
|
TreeView_Expand(g_RegEditData.hKeyTreeWnd, hTreeItem, TVE_EXPAND);
|
|
|
|
// WARNING: It is possible for our new item _not_ to be the only child
|
|
// if our view is out of date!
|
|
hNewTreeItem = TreeView_GetChild(g_RegEditData.hKeyTreeWnd, hTreeItem);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
hNewTreeItem = KeyTree_InsertItem(g_RegEditData.hKeyTreeWnd, hTreeItem,
|
|
TVI_LAST, KeyName, FALSE, 0);
|
|
|
|
}
|
|
|
|
TreeView_SelectItem(g_RegEditData.hKeyTreeWnd, hNewTreeItem);
|
|
KeyTree_EditLabel(g_RegEditData.hKeyTreeWnd, hNewTreeItem);
|
|
}
|
|
else
|
|
{
|
|
ErrorStringID = IDS_RENAMEKEYTOOLONG;
|
|
goto error_CloseKey;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorStringID = IDS_NEWKEYNOUNIQUE;
|
|
goto error_CloseKey;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
return;
|
|
|
|
error_CloseKey:
|
|
RegCloseKey(hKey);
|
|
|
|
// FEATURE: For any errors that may crop up, we may need to turn off the
|
|
// child flag.
|
|
|
|
error_ShowDialog:
|
|
InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(ErrorStringID),
|
|
MAKEINTRESOURCE(IDS_NEWKEYERRORTITLE), MB_ICONERROR | MB_OK,
|
|
(LPTSTR) KeyName);
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// RegEdit_GetTemporaryKeyName
|
|
//
|
|
// DESCRIPTION: Loop through the registry trying to find a valid temporary name
|
|
// until the user renames the key.
|
|
//
|
|
// PARAMETERS: HWND hWnd - handle to window
|
|
// PTSTR pszKeyName
|
|
//
|
|
// RETURN: True, if unique name is found
|
|
//------------------------------------------------------------------------------
|
|
BOOL RegEdit_GetTemporaryKeyName(HWND hWnd, PTSTR pszKeyName, DWORD cchKeyNameMax, HKEY hKey)
|
|
{
|
|
HKEY hNewKey;
|
|
UINT uNewKeyNameID = 1;
|
|
|
|
while (uNewKeyNameID < MAX_KEYNAME_TEMPLATE_ID)
|
|
{
|
|
StringCchPrintf(pszKeyName, cchKeyNameMax, g_RegEditData.pNewKeyTemplate, uNewKeyNameID);
|
|
|
|
if(RegOpenKeyEx(hKey, pszKeyName, 0, 0, &hNewKey) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
break;
|
|
}
|
|
RegCloseKey(hNewKey);
|
|
|
|
uNewKeyNameID++;
|
|
}
|
|
|
|
if (uNewKeyNameID == MAX_KEYNAME_TEMPLATE_ID)
|
|
{
|
|
InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(IDS_NEWKEYNOUNIQUE),
|
|
MAKEINTRESOURCE(IDS_NEWKEYERRORTITLE), MB_ICONERROR | MB_OK, pszKeyName);
|
|
}
|
|
|
|
return (uNewKeyNameID != MAX_KEYNAME_TEMPLATE_ID);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeItemExpanding
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
* lpNMTreeView, TreeView notification data.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
LRESULT
|
|
PASCAL
|
|
RegEdit_OnKeyTreeItemExpanding(
|
|
HWND hWnd,
|
|
LPNM_TREEVIEW lpNMTreeView
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
HTREEITEM hExpandingTreeItem;
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
TV_ITEM TVItem;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
hExpandingTreeItem = lpNMTreeView-> itemNew.hItem;
|
|
|
|
//
|
|
// Check if we're to expand the given tree item for the first time. If so,
|
|
// delve into the registry to get all of the key's subkeys.
|
|
//
|
|
|
|
if (lpNMTreeView-> action & TVE_EXPAND && !(lpNMTreeView-> itemNew.state &
|
|
TVIS_EXPANDEDONCE)) {
|
|
|
|
if (TreeView_GetChild(hKeyTreeWnd, hExpandingTreeItem) != NULL)
|
|
return FALSE;
|
|
|
|
RegEdit_SetWaitCursor(TRUE);
|
|
|
|
if (!KeyTree_ExpandBranch(hKeyTreeWnd, hExpandingTreeItem)) {
|
|
|
|
//
|
|
// Get the text of the selected tree item so that we can display
|
|
// a more meaningful error message.
|
|
//
|
|
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.hItem = hExpandingTreeItem;
|
|
TVItem.pszText = (LPTSTR) KeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(KeyName);
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
InternalMessageBox(g_hInstance, hWnd,
|
|
MAKEINTRESOURCE(IDS_OPENKEYCANNOTOPEN),
|
|
MAKEINTRESOURCE(IDS_OPENKEYERRORTITLE), MB_ICONERROR | MB_OK,
|
|
(LPTSTR) KeyName);
|
|
|
|
}
|
|
|
|
RegEdit_SetWaitCursor(FALSE);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeSelChanged
|
|
*
|
|
* DESCRIPTION:
|
|
* Depending on how the user has selected the new item in the KeyTreeWnd,
|
|
* we call to the real worker routine, RegEdit_KeyTreeSelChanged, or delay
|
|
* the call for several milliseconds.
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
* lpNMTreeView, TreeView notification data.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeSelChanged(
|
|
HWND hWnd,
|
|
LPNM_TREEVIEW lpNMTreeView
|
|
)
|
|
{
|
|
|
|
UINT TimerDelay;
|
|
|
|
//
|
|
// We delay the actual update of the selection and thus of the
|
|
// ValueListWnd for several milliseconds. This avoids unnecessary flashing
|
|
// as the user scrolls through the tree. (This behavior is directly taken
|
|
// from the Explorer.)
|
|
//
|
|
|
|
switch (g_RegEditData.SelChangeTimerState) {
|
|
|
|
case SCTS_TIMERSET:
|
|
KillTimer(hWnd, SELCHANGE_TIMER_ID);
|
|
// FALL THROUGH
|
|
|
|
case SCTS_TIMERCLEAR:
|
|
#ifdef WINNT
|
|
//
|
|
// This behavior is extremely annoying so I am changing it.
|
|
//
|
|
TimerDelay = 1;
|
|
#else
|
|
TimerDelay = (lpNMTreeView != NULL && lpNMTreeView-> action ==
|
|
TVC_BYMOUSE) ? (1) : (GetDoubleClickTime() * 3 / 2);
|
|
#endif
|
|
SetTimer(hWnd, SELCHANGE_TIMER_ID, TimerDelay, NULL);
|
|
g_RegEditData.SelChangeTimerState = SCTS_TIMERSET;
|
|
break;
|
|
|
|
//
|
|
// We want to punt the first selection change notification that comes
|
|
// through.
|
|
//
|
|
|
|
case SCTS_INITIALIZING:
|
|
RegEdit_KeyTreeSelChanged(hWnd);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnSelChangedTimer
|
|
*
|
|
* DESCRIPTION:
|
|
* Called several milliseconds after a keyboard operation has selected a new
|
|
* item in the KeyTreeWnd. Act as if a new selection has just been made in
|
|
* the KeyTreeWnd.
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnSelChangedTimer(
|
|
HWND hWnd
|
|
)
|
|
{
|
|
|
|
KillTimer(hWnd, SELCHANGE_TIMER_ID);
|
|
g_RegEditData.SelChangeTimerState = SCTS_TIMERCLEAR;
|
|
|
|
RegEdit_KeyTreeSelChanged(hWnd);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_KeyTreeSelChanged
|
|
*
|
|
* DESCRIPTION:
|
|
* Called after a new item has been selected in the KeyTreeWnd. Opens a
|
|
* registry key to the new branch and notifies the ValueListWnd to update
|
|
* itself.
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_KeyTreeSelChanged(
|
|
HWND hWnd
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
HTREEITEM hSelectedTreeItem;
|
|
RECT ItemRect;
|
|
RECT ClientRect;
|
|
RECT FromRect;
|
|
RECT ToRect;
|
|
HKEY hRootKey;
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
TV_ITEM TVItem;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
hSelectedTreeItem = TreeView_GetSelection(hKeyTreeWnd);
|
|
|
|
if (g_RegEditData.SelChangeTimerState != SCTS_INITIALIZING) {
|
|
|
|
//
|
|
// Draw an animation that shows the "expansion" of the newly selected
|
|
// tree item to the ListView.
|
|
//
|
|
|
|
TreeView_GetItemRect(hKeyTreeWnd, hSelectedTreeItem, &ItemRect, TRUE);
|
|
GetClientRect(hKeyTreeWnd, &ClientRect);
|
|
IntersectRect(&FromRect, &ClientRect, &ItemRect);
|
|
MapWindowPoints(hKeyTreeWnd, hWnd, (LPPOINT) &FromRect, 2);
|
|
|
|
GetWindowRect(g_RegEditData.hValueListWnd, &ToRect);
|
|
MapWindowPoints(NULL, hWnd, (LPPOINT) &ToRect, 2);
|
|
|
|
DrawAnimatedRects(hWnd, IDANI_OPEN, &FromRect, &ToRect);
|
|
|
|
}
|
|
|
|
//
|
|
// Close the previously selected item's key handle, if appropriate.
|
|
//
|
|
|
|
if (g_RegEditData.hCurrentSelectionKey != NULL) {
|
|
|
|
RegCloseKey(g_RegEditData.hCurrentSelectionKey);
|
|
g_RegEditData.hCurrentSelectionKey = NULL;
|
|
|
|
}
|
|
|
|
RegEdit_UpdateStatusBar();
|
|
|
|
//
|
|
// Simple case-- we're changing to one of the top-level labels, such as
|
|
// "My Computer" or a network computer name. Right now, nothing is
|
|
// displayed in the ListView, so just empty it and return.
|
|
//
|
|
|
|
if (TreeView_GetParent(hKeyTreeWnd, hSelectedTreeItem) != NULL) {
|
|
|
|
//
|
|
// Build a registry path to the selected tree item and open a registry
|
|
// key.
|
|
//
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
hSelectedTreeItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOSUBKEY);
|
|
|
|
if(RegOpenKeyEx(hRootKey,KeyName, 0, MAXIMUM_ALLOWED,
|
|
&g_RegEditData.hCurrentSelectionKey) != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Get the text of the selected tree item so that we can display
|
|
// a more meaningful error message.
|
|
//
|
|
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.hItem = hSelectedTreeItem;
|
|
TVItem.pszText = (LPTSTR) KeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(KeyName);
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
InternalMessageBox(g_hInstance, hWnd,
|
|
MAKEINTRESOURCE(IDS_OPENKEYCANNOTOPEN),
|
|
MAKEINTRESOURCE(IDS_OPENKEYERRORTITLE), MB_ICONERROR | MB_OK,
|
|
(LPTSTR) KeyName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RegEdit_OnValueListRefresh(hWnd);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeBeginLabelEdit
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
* lpTVDispInfo,
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
PASCAL
|
|
RegEdit_OnKeyTreeBeginLabelEdit(
|
|
HWND hWnd,
|
|
TV_DISPINFO FAR* lpTVDispInfo
|
|
)
|
|
{
|
|
|
|
//
|
|
// B#7933: We don't want the user to hurt themselves by making it too easy
|
|
// to rename keys and values. Only allow renames via the menus.
|
|
//
|
|
|
|
//
|
|
// We don't get any information on the source of this editing action, so
|
|
// we must maintain a flag that tells us whether or not this is "good".
|
|
//
|
|
|
|
if (!g_RegEditData.fAllowLabelEdits)
|
|
return TRUE;
|
|
|
|
//
|
|
// All other labels are fair game. We need to disable our keyboard
|
|
// accelerators so that the edit control can "see" them.
|
|
//
|
|
|
|
g_fDisableAccelerators = TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeEndLabelEdit
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
* lpTVDispInfo,
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
PASCAL
|
|
RegEdit_OnKeyTreeEndLabelEdit(
|
|
HWND hWnd,
|
|
TV_DISPINFO FAR* lpTVDispInfo
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
HKEY hRootKey;
|
|
TCHAR SourceKeyName[MAXKEYNAME*2];
|
|
TCHAR DestinationKeyName[MAXKEYNAME];
|
|
HKEY hSourceKey;
|
|
HKEY hDestinationKey;
|
|
LPTSTR lpEndOfParentKey;
|
|
UINT ErrorStringID;
|
|
TV_ITEM TVItem;
|
|
|
|
//
|
|
// We can reenable our keyboard accelerators now that the edit control no
|
|
// longer needs to "see" them.
|
|
//
|
|
|
|
g_fDisableAccelerators = FALSE;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
//
|
|
// Check to see if the user cancelled the edit. If so, we don't care so
|
|
// just return.
|
|
//
|
|
|
|
if (lpTVDispInfo-> item.pszText == NULL)
|
|
return FALSE;
|
|
|
|
//
|
|
// Attempt to open the key to be renamed. This may or may not be the same
|
|
// key that is already open.
|
|
//
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
lpTVDispInfo-> item.hItem,
|
|
SourceKeyName,
|
|
ARRAYSIZE(SourceKeyName),
|
|
BKP_TOSUBKEY);
|
|
|
|
if (lstrlen(SourceKeyName) >= MAXKEYNAME) {
|
|
ErrorStringID = IDS_RENAMEKEYTOOLONG;
|
|
goto error_ShowDialog;
|
|
|
|
}
|
|
|
|
if(RegOpenKeyEx(hRootKey,SourceKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSourceKey) != ERROR_SUCCESS)
|
|
{
|
|
ErrorStringID = IDS_RENAMEKEYOTHERERROR;
|
|
goto error_ShowDialog;
|
|
}
|
|
|
|
//
|
|
// Take the full path name of the key (relative to a predefined root key)
|
|
// and replace the old key name with the new. Make sure that this key
|
|
// doesn't exceed our internal buffers.
|
|
//
|
|
|
|
StringCchCopy(DestinationKeyName, ARRAYSIZE(DestinationKeyName), SourceKeyName);
|
|
|
|
if ((lpEndOfParentKey = StrRChr(DestinationKeyName, NULL, TEXT('\\'))) != NULL)
|
|
lpEndOfParentKey++;
|
|
|
|
else
|
|
lpEndOfParentKey = DestinationKeyName;
|
|
|
|
*lpEndOfParentKey = 0;
|
|
|
|
if (lstrlen(DestinationKeyName) + lstrlen(lpTVDispInfo->item.pszText) >= MAXKEYNAME) {
|
|
ErrorStringID = IDS_RENAMEKEYTOOLONG;
|
|
goto error_CloseSourceKey;
|
|
}
|
|
|
|
lstrcpy(lpEndOfParentKey, lpTVDispInfo->item.pszText);
|
|
|
|
//
|
|
// Make sure there are no backslashes in the name.
|
|
//
|
|
|
|
if (StrChr(lpEndOfParentKey, TEXT('\\')) != NULL)
|
|
{
|
|
ErrorStringID = IDS_RENAMEKEYBADCHARS;
|
|
goto error_CloseSourceKey;
|
|
}
|
|
|
|
//
|
|
// Make sure there the name isn't empty
|
|
//
|
|
|
|
if (DestinationKeyName[0] == 0) {
|
|
ErrorStringID = IDS_RENAMEKEYEMPTY;
|
|
goto error_CloseSourceKey;
|
|
}
|
|
|
|
//
|
|
// Make sure that the destination doesn't already exist.
|
|
//
|
|
if(RegOpenKeyEx(hRootKey, DestinationKeyName, 0, KEY_QUERY_VALUE, &hDestinationKey) == ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hDestinationKey);
|
|
|
|
ErrorStringID = IDS_RENAMEKEYEXISTS;
|
|
goto error_CloseSourceKey;
|
|
}
|
|
|
|
//
|
|
// Create the destination key and do the copy.
|
|
//
|
|
|
|
if (RegCreateKey(hRootKey, DestinationKeyName, &hDestinationKey) != ERROR_SUCCESS)
|
|
{
|
|
ErrorStringID = IDS_RENAMEKEYOTHERERROR;
|
|
goto error_CloseSourceKey;
|
|
}
|
|
|
|
// FEATURE: Check this return (when it gets one!)
|
|
if (!CopyRegistry(hSourceKey, hDestinationKey))
|
|
{
|
|
RegCloseKey(hDestinationKey);
|
|
RegCloseKey(hSourceKey);
|
|
|
|
ErrorStringID = IDS_RENAMEKEYOTHERERROR;
|
|
goto error_ShowDialog;
|
|
}
|
|
|
|
RegCloseKey(hSourceKey);
|
|
|
|
//
|
|
// Check to see if we're renaming the currently selected key. If so, toss
|
|
// our cached key handle and change to our source key.
|
|
//
|
|
|
|
if (TreeView_GetSelection(hKeyTreeWnd) == lpTVDispInfo-> item.hItem) {
|
|
|
|
RegCloseKey(g_RegEditData.hCurrentSelectionKey);
|
|
|
|
g_RegEditData.hCurrentSelectionKey = hDestinationKey;
|
|
|
|
//
|
|
// We can't just call RegEdit_UpdateStatusBar here... the tree item
|
|
// won't be updated until we return TRUE from this message. So we must
|
|
// post a message to tell ourselves to do the update later on.
|
|
//
|
|
|
|
PostMessage(hWnd, REM_UPDATESTATUSBAR, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
RegCloseKey(hDestinationKey);
|
|
|
|
if (RegDeleteKeyRecursive(hRootKey, SourceKeyName) != ERROR_SUCCESS) {
|
|
|
|
ErrorStringID = IDS_RENAMEKEYOTHERERROR;
|
|
goto error_ShowDialog;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
error_CloseSourceKey:
|
|
RegCloseKey(hSourceKey);
|
|
|
|
error_ShowDialog:
|
|
TVItem.hItem = lpTVDispInfo-> item.hItem;
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.pszText = SourceKeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(SourceKeyName);
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(ErrorStringID),
|
|
MAKEINTRESOURCE(IDS_RENAMEKEYERRORTITLE), MB_ICONERROR | MB_OK,
|
|
(LPTSTR) SourceKeyName);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeCommand
|
|
*
|
|
* DESCRIPTION:
|
|
* Handles the selection of a menu item by the user intended for the
|
|
* KeyTree child window.
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of RegEdit window.
|
|
* MenuCommand, identifier of menu command.
|
|
* hTreeItem,
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeCommand(
|
|
HWND hWnd,
|
|
int MenuCommand,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
//
|
|
// Assume that the we mean the current selection if we're to dispatch a
|
|
// command that requires a tree item. This is necessary because the tree
|
|
// control will let you activate the context menu of one tree item while
|
|
// another one is really the selected tree item.
|
|
//
|
|
|
|
if (hTreeItem == NULL)
|
|
hTreeItem = TreeView_GetSelection(hKeyTreeWnd);
|
|
|
|
switch (MenuCommand) {
|
|
|
|
case ID_CONTEXTMENU:
|
|
RegEdit_OnKeyTreeContextMenu(hWnd, TRUE);
|
|
break;
|
|
|
|
case ID_TOGGLE:
|
|
TreeView_Expand(hKeyTreeWnd, hTreeItem, TVE_TOGGLE);
|
|
break;
|
|
|
|
case ID_DELETE:
|
|
RegEdit_OnKeyTreeDelete(hWnd, hTreeItem);
|
|
break;
|
|
|
|
case ID_RENAME:
|
|
RegEdit_OnKeyTreeRename(hWnd, hTreeItem);
|
|
break;
|
|
|
|
case ID_DISCONNECT:
|
|
RegEdit_OnKeyTreeDisconnect(hWnd, hTreeItem);
|
|
break;
|
|
|
|
case ID_COPYKEYNAME:
|
|
RegEdit_OnCopyKeyName(hWnd, hTreeItem);
|
|
break;
|
|
|
|
case ID_NEWKEY:
|
|
RegEdit_OnNewKey(hWnd, hTreeItem);
|
|
break;
|
|
|
|
case ID_NEWSTRINGVALUE:
|
|
case ID_NEWBINARYVALUE:
|
|
if (hTreeItem != TreeView_GetSelection(hKeyTreeWnd)) {
|
|
|
|
//
|
|
// Force the selection to occur now, so that we're dealing
|
|
// with the right open key.
|
|
//
|
|
|
|
TreeView_SelectItem(hKeyTreeWnd, hTreeItem);
|
|
RegEdit_OnSelChangedTimer(hWnd);
|
|
|
|
}
|
|
// FALL THROUGH
|
|
|
|
default:
|
|
//
|
|
// Check to see if this menu command should be handled by the main
|
|
// window's command handler.
|
|
//
|
|
|
|
if (MenuCommand >= ID_FIRSTMAINMENUITEM && MenuCommand <=
|
|
ID_LASTMAINMENUITEM)
|
|
RegEdit_OnCommand(hWnd, MenuCommand, NULL, 0);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeContextMenu
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeContextMenu(
|
|
HWND hWnd,
|
|
BOOL fByAccelerator
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
DWORD MessagePos;
|
|
POINT MessagePoint;
|
|
TV_HITTESTINFO TVHitTestInfo;
|
|
UINT MenuID;
|
|
HMENU hContextMenu;
|
|
HMENU hContextPopupMenu;
|
|
TV_ITEM TVItem;
|
|
int MenuCommand;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
//
|
|
// If fByAcclerator is TRUE, then the user hit Shift-F10 to bring up the
|
|
// context menu. Following the Cabinet's convention, this menu is
|
|
// placed at (0,0) of the KeyTree client area.
|
|
//
|
|
|
|
if (fByAccelerator) {
|
|
|
|
MessagePoint.x = 0;
|
|
MessagePoint.y = 0;
|
|
|
|
ClientToScreen(hKeyTreeWnd, &MessagePoint);
|
|
|
|
TVItem.hItem = TreeView_GetSelection(hKeyTreeWnd);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MessagePos = GetMessagePos();
|
|
|
|
MessagePoint.x = GET_X_LPARAM(MessagePos);
|
|
MessagePoint.y = GET_Y_LPARAM(MessagePos);
|
|
|
|
TVHitTestInfo.pt = MessagePoint;
|
|
ScreenToClient(hKeyTreeWnd, &TVHitTestInfo.pt);
|
|
TVItem.hItem = TreeView_HitTest(hKeyTreeWnd, &TVHitTestInfo);
|
|
|
|
}
|
|
|
|
//
|
|
// Determine which context menu to use and load it up.
|
|
//
|
|
|
|
if (TVItem.hItem == NULL)
|
|
{
|
|
return; // No context menu for now
|
|
}
|
|
else
|
|
{
|
|
// Select the item to be dragged
|
|
TreeView_Select(g_RegEditData.hKeyTreeWnd, TVItem.hItem, TVGN_CARET);
|
|
|
|
if (TreeView_GetParent(hKeyTreeWnd, TVItem.hItem) == NULL)
|
|
MenuID = IDM_COMPUTER_CONTEXT;
|
|
|
|
else
|
|
MenuID = IDM_KEY_CONTEXT;
|
|
|
|
}
|
|
|
|
if ((hContextMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(MenuID))) == NULL)
|
|
return;
|
|
|
|
hContextPopupMenu = GetSubMenu(hContextMenu, 0);
|
|
|
|
TVItem.mask = TVIF_STATE | TVIF_CHILDREN;
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
if (TVItem.state & TVIS_EXPANDED)
|
|
ModifyMenu(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND | MF_STRING,
|
|
ID_TOGGLE, g_RegEditData.pCollapse);
|
|
|
|
if (MenuID == IDM_COMPUTER_CONTEXT) {
|
|
|
|
if (g_RegEditData.fHaveNetwork) {
|
|
|
|
if (TreeView_GetPrevSibling(hKeyTreeWnd, TVItem.hItem) == NULL)
|
|
EnableMenuItem(hContextPopupMenu, ID_DISCONNECT, MF_GRAYED |
|
|
MF_BYCOMMAND);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DeleteMenu(hContextPopupMenu, ID_DISCONNECT, MF_BYCOMMAND);
|
|
DeleteMenu(hContextPopupMenu, ID_NETSEPARATOR, MF_BYCOMMAND);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RegEdit_SetKeyTreeEditMenuItems(hContextPopupMenu, TVItem.hItem);
|
|
|
|
if (TVItem.cChildren == 0)
|
|
EnableMenuItem(hContextPopupMenu, ID_TOGGLE, MF_GRAYED |
|
|
MF_BYCOMMAND);
|
|
|
|
}
|
|
|
|
SetMenuDefaultItem(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND);
|
|
|
|
MenuCommand = TrackPopupMenuEx(hContextPopupMenu, TPM_RETURNCMD |
|
|
TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_TOPALIGN, MessagePoint.x,
|
|
MessagePoint.y, hWnd, NULL);
|
|
|
|
DestroyMenu(hContextMenu);
|
|
|
|
RegEdit_OnKeyTreeCommand(hWnd, MenuCommand, TVItem.hItem);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_SetKeyTreeEditMenuItems
|
|
*
|
|
* DESCRIPTION:
|
|
* Shared routine between the main menu and the context menu to setup the
|
|
* edit menu items.
|
|
*
|
|
* PARAMETERS:
|
|
* hPopupMenu, handle of popup menu to modify.
|
|
* hTreeItem, handle of selected tree item.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_SetKeyTreeEditMenuItems(
|
|
HMENU hPopupMenu,
|
|
HTREEITEM hSelectedTreeItem
|
|
)
|
|
{
|
|
|
|
UINT EnableFlags;
|
|
|
|
EnableFlags = KeyTree_CanDeleteOrRenameItem(g_RegEditData.hKeyTreeWnd,
|
|
hSelectedTreeItem) ? (MF_ENABLED | MF_BYCOMMAND) :
|
|
(MF_GRAYED | MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(hPopupMenu, ID_DELETE, EnableFlags);
|
|
EnableMenuItem(hPopupMenu, ID_RENAME, EnableFlags);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeDelete
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeDelete(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
HKEY hRootKey;
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
HTREEITEM hParentTreeItem;
|
|
TV_ITEM TVItem;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
if (!KeyTree_CanDeleteOrRenameItem(hKeyTreeWnd, hTreeItem))
|
|
return;
|
|
|
|
if (InternalMessageBox(g_hInstance, hWnd,
|
|
MAKEINTRESOURCE(IDS_CONFIRMDELKEYTEXT),
|
|
MAKEINTRESOURCE(IDS_CONFIRMDELKEYTITLE), MB_ICONWARNING | MB_YESNO) !=
|
|
IDYES)
|
|
return;
|
|
|
|
if (hTreeItem == TreeView_GetSelection(hKeyTreeWnd)) {
|
|
|
|
if (g_RegEditData.hCurrentSelectionKey != NULL) {
|
|
|
|
RegCloseKey(g_RegEditData.hCurrentSelectionKey);
|
|
g_RegEditData.hCurrentSelectionKey = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
hTreeItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOSUBKEY);
|
|
|
|
if (RegDeleteKeyRecursive(hRootKey, KeyName) == ERROR_SUCCESS) {
|
|
|
|
SetWindowRedraw(hKeyTreeWnd, FALSE);
|
|
|
|
hParentTreeItem = TreeView_GetParent(hKeyTreeWnd, hTreeItem);
|
|
|
|
TreeView_DeleteItem(hKeyTreeWnd, hTreeItem);
|
|
|
|
//
|
|
// See if the key that we just deleted was the last child of its
|
|
// parent. If so, remove the expand/collapse button.
|
|
//
|
|
|
|
if (TreeView_GetChild(hKeyTreeWnd, hParentTreeItem) == NULL) {
|
|
|
|
TVItem.mask = TVIF_CHILDREN | TVIF_STATE;
|
|
TVItem.hItem = hParentTreeItem;
|
|
TVItem.cChildren = 0;
|
|
TVItem.state = 0;
|
|
TVItem.stateMask = TVIS_EXPANDED | TVIS_EXPANDEDONCE;
|
|
TreeView_SetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure we can see the selected tree item now since it may be
|
|
// currently off-screen.
|
|
//
|
|
|
|
TreeView_EnsureVisible(hKeyTreeWnd, TreeView_GetSelection(hKeyTreeWnd));
|
|
|
|
SetWindowRedraw(hKeyTreeWnd, TRUE);
|
|
|
|
UpdateWindow(hKeyTreeWnd);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
TVItem.hItem = hTreeItem;
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.pszText = KeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(KeyName);
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
InternalMessageBox(g_hInstance, hWnd,
|
|
MAKEINTRESOURCE(IDS_DELETEKEYDELETEFAILED),
|
|
MAKEINTRESOURCE(IDS_DELETEKEYERRORTITLE), MB_ICONERROR | MB_OK,
|
|
KeyName);
|
|
|
|
//
|
|
// Need to refresh the tree at this point, as some subkeys may have
|
|
// been deleted successfully even if we didn't have sufficient
|
|
// permissions to delete all of them.
|
|
//
|
|
RegEdit_OnKeyTreeRefresh(hWnd);
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeRename
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeRename(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
if (KeyTree_CanDeleteOrRenameItem(g_RegEditData.hKeyTreeWnd, hTreeItem))
|
|
KeyTree_EditLabel(g_RegEditData.hKeyTreeWnd, hTreeItem);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeRefresh
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeRefresh(
|
|
HWND hWnd
|
|
)
|
|
{
|
|
|
|
HDPA hDPA;
|
|
HWND hKeyTreeWnd;
|
|
HTREEITEM hPrevSelectedTreeItem;
|
|
TV_ITEM EnumTVItem;
|
|
TV_ITEM CurrentTVItem;
|
|
HKEY hRootKey;
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
int MaximumSubKeyLength;
|
|
int Index;
|
|
HKEY hEnumKey;
|
|
int CompareResult;
|
|
LPTSTR lpDPAKeyName;
|
|
HTREEITEM hTempTreeItem;
|
|
|
|
if ((hDPA = DPA_CreateEx(REFRESH_DPA_GROW, GetProcessHeap())) == NULL)
|
|
return;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
RegEdit_SetWaitCursor(TRUE);
|
|
SetWindowRedraw(hKeyTreeWnd, FALSE);
|
|
|
|
hPrevSelectedTreeItem = TreeView_GetSelection(hKeyTreeWnd);
|
|
|
|
EnumTVItem.mask = TVIF_TEXT;
|
|
EnumTVItem.pszText = KeyName;
|
|
EnumTVItem.cchTextMax = ARRAYSIZE(KeyName);
|
|
|
|
CurrentTVItem.mask = TVIF_STATE | TVIF_CHILDREN;
|
|
CurrentTVItem.stateMask = 0;
|
|
CurrentTVItem.hItem = TreeView_GetRoot(hKeyTreeWnd);
|
|
|
|
while (TRUE) {
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &CurrentTVItem);
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
CurrentTVItem.hItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOSUBKEY);
|
|
|
|
if (CurrentTVItem.state & TVIS_EXPANDED) {
|
|
|
|
//
|
|
// If this isn't a top-level label (and it won't be if hRootKey is
|
|
// not NULL), then compare the actual contents of the registry
|
|
// against what we're showing.
|
|
//
|
|
if(hRootKey && RegOpenKeyEx(hRootKey,KeyName,0,KEY_ENUMERATE_SUB_KEYS,&hEnumKey) ==
|
|
ERROR_SUCCESS) {
|
|
|
|
//
|
|
// As a result of adding new keys and renaming existing ones,
|
|
// the children of this item may be out of order. For the
|
|
// following algorithm to work correctly, we must now sort
|
|
// these keys.
|
|
//
|
|
|
|
TreeView_SortChildren(hKeyTreeWnd, CurrentTVItem.hItem, FALSE);
|
|
|
|
//
|
|
// Build a sorted dynamic array of strings that represent the
|
|
// keys actually in the registry at this time.
|
|
//
|
|
|
|
MaximumSubKeyLength = MAXKEYNAME - (lstrlen(KeyName) + 1);
|
|
Index = 0;
|
|
|
|
while (RegEnumKey(hEnumKey, Index, KeyName,
|
|
MaximumSubKeyLength) == ERROR_SUCCESS) {
|
|
|
|
lpDPAKeyName = NULL;
|
|
Str_SetPtr(&lpDPAKeyName, KeyName);
|
|
DPA_InsertPtr(hDPA, Index++, lpDPAKeyName);
|
|
|
|
}
|
|
|
|
RegCloseKey(hEnumKey);
|
|
DPA_Sort(hDPA, DPACompareKeyNames, 0);
|
|
|
|
//
|
|
// Does this key have subkeys anymore? If not, then we need
|
|
// to reset it's child flag and remove all of it's children.
|
|
//
|
|
|
|
if (Index == 0) {
|
|
|
|
DPA_DeleteAllPtrs(hDPA);
|
|
|
|
TreeView_Expand(hKeyTreeWnd, CurrentTVItem.hItem,
|
|
TVE_COLLAPSE | TVE_COLLAPSERESET);
|
|
|
|
CurrentTVItem.cChildren = 0;
|
|
goto SetCurrentTreeItem;
|
|
|
|
}
|
|
|
|
//
|
|
// Merge the keys that we found during our above enumeration
|
|
// with the keys that our key tree lists. Add and remove
|
|
// elements from the tree as appropriate.
|
|
//
|
|
|
|
lpDPAKeyName = DPA_FastGetPtr(hDPA, --Index);
|
|
|
|
EnumTVItem.hItem = TreeView_GetChild(hKeyTreeWnd,
|
|
CurrentTVItem.hItem);
|
|
if (EnumTVItem.hItem)
|
|
TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);
|
|
|
|
while (Index >= 0 && EnumTVItem.hItem != NULL) {
|
|
|
|
CompareResult = lstrcmpi(KeyName, lpDPAKeyName);
|
|
|
|
if (CompareResult == 0) {
|
|
|
|
EnumTVItem.hItem = TreeView_GetNextSibling(hKeyTreeWnd,
|
|
EnumTVItem.hItem);
|
|
if (EnumTVItem.hItem)
|
|
TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);
|
|
|
|
goto GetNextDPAPointer;
|
|
|
|
}
|
|
|
|
else if (CompareResult > 0) {
|
|
|
|
KeyTree_InsertItem(hKeyTreeWnd, CurrentTVItem.hItem,
|
|
TVI_SORT, lpDPAKeyName, DoesKeyHaveKids(hEnumKey,
|
|
lpDPAKeyName), 0);
|
|
|
|
GetNextDPAPointer:
|
|
Str_SetPtr(&lpDPAKeyName, NULL);
|
|
|
|
if (--Index >= 0)
|
|
lpDPAKeyName = DPA_FastGetPtr(hDPA, Index);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
|
|
EnumTVItem.hItem);
|
|
TreeView_DeleteItem(hKeyTreeWnd, EnumTVItem.hItem);
|
|
EnumTVItem.hItem = hTempTreeItem;
|
|
if (EnumTVItem.hItem)
|
|
TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Once we drop to here, we may have extra items in the key
|
|
// tree or in the dynamic array. Process them accordingly.
|
|
//
|
|
|
|
if (Index >= 0) {
|
|
|
|
while (TRUE) {
|
|
|
|
KeyTree_InsertItem(hKeyTreeWnd, CurrentTVItem.hItem,
|
|
TVI_SORT, lpDPAKeyName, DoesKeyHaveKids(hEnumKey,
|
|
lpDPAKeyName), 0);
|
|
|
|
Str_SetPtr(&lpDPAKeyName, NULL);
|
|
|
|
if (--Index < 0)
|
|
break;
|
|
|
|
lpDPAKeyName = DPA_FastGetPtr(hDPA, Index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
while (EnumTVItem.hItem != NULL) {
|
|
|
|
hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
|
|
EnumTVItem.hItem);
|
|
TreeView_DeleteItem(hKeyTreeWnd, EnumTVItem.hItem);
|
|
EnumTVItem.hItem = hTempTreeItem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DPA_DeleteAllPtrs(hDPA);
|
|
|
|
}
|
|
|
|
CurrentTVItem.hItem = TreeView_GetChild(hKeyTreeWnd,
|
|
CurrentTVItem.hItem);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// If this isn't a top-level label (and it won't be if hRootKey is
|
|
// not NULL), then re-check if this key has any children.
|
|
//
|
|
|
|
if (hRootKey != NULL) {
|
|
|
|
TreeView_Expand(hKeyTreeWnd, CurrentTVItem.hItem, TVE_COLLAPSE |
|
|
TVE_COLLAPSERESET);
|
|
|
|
CurrentTVItem.cChildren = DoesKeyHaveKids(hRootKey, KeyName);
|
|
|
|
SetCurrentTreeItem:
|
|
TreeView_SetItem(hKeyTreeWnd, &CurrentTVItem);
|
|
|
|
}
|
|
|
|
//
|
|
// Because we're at the "bottom" of the TreeView, we now need to
|
|
// walk to the siblings of this tree item. And if no siblings
|
|
// exist, we walk back to the parent and check again for siblings.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
if ((hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
|
|
CurrentTVItem.hItem)) != NULL) {
|
|
|
|
CurrentTVItem.hItem = hTempTreeItem;
|
|
break;
|
|
|
|
}
|
|
|
|
if ((CurrentTVItem.hItem = TreeView_GetParent(hKeyTreeWnd,
|
|
CurrentTVItem.hItem)) == NULL) {
|
|
|
|
//
|
|
// We've now walked over all of the tree items, so do any
|
|
// cleanup here and exit.
|
|
//
|
|
|
|
DPA_Destroy(hDPA);
|
|
|
|
SetWindowRedraw(hKeyTreeWnd, TRUE);
|
|
|
|
//
|
|
// The selection may have changed as a result of having
|
|
// the focus on an nonexistent key.
|
|
//
|
|
|
|
if (TreeView_GetSelection(hKeyTreeWnd) != hPrevSelectedTreeItem) {
|
|
RegEdit_OnKeyTreeSelChanged(hWnd, NULL);
|
|
} else {
|
|
if (RegEdit_OnValueListRefresh(hWnd) != ERROR_SUCCESS) {
|
|
//
|
|
// Its possible that the registry key was deleted and replaced with
|
|
// an identically-named key. We should just trigger a selection
|
|
// change in this case.
|
|
//
|
|
RegEdit_OnKeyTreeSelChanged(hWnd, NULL);
|
|
}
|
|
}
|
|
|
|
RegEdit_SetWaitCursor(FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* DPACompareKeyNames
|
|
*
|
|
* DESCRIPTION:
|
|
* Callback comparision routine for refresh's DPA_Sort call. Simply returns
|
|
* the result of lstrcmpi.
|
|
*
|
|
* PARAMETERS:
|
|
* lpString1,
|
|
* lpString2,
|
|
* lParam, ignored optional data.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int
|
|
WINAPI
|
|
DPACompareKeyNames(
|
|
LPVOID lpString1,
|
|
LPVOID lpString2,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
|
|
return lstrcmpi((LPTSTR) lpString2, (LPTSTR) lpString1);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnKeyTreeDisconnect
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnKeyTreeDisconnect(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
TV_ITEM TVItem;
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
//
|
|
// Disconnect all of the root registry handles that we've opened.
|
|
//
|
|
|
|
TVItem.mask = TVIF_PARAM;
|
|
TVItem.hItem = TreeView_GetChild(hKeyTreeWnd, hTreeItem);
|
|
|
|
while (TVItem.hItem != NULL) {
|
|
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
RegCloseKey((HKEY) TVItem.lParam);
|
|
|
|
TVItem.hItem = TreeView_GetNextSibling(hKeyTreeWnd, TVItem.hItem);
|
|
|
|
}
|
|
|
|
TreeView_DeleteItem(hKeyTreeWnd, hTreeItem);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_UpdateStatusBar
|
|
*
|
|
* DESCRIPTION:
|
|
* Show the full registry path in the status bar, for lack of anything
|
|
* better to do with it.
|
|
*
|
|
* PARAMETERS:
|
|
* (none).
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_UpdateStatusBar(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
HWND hKeyTreeWnd;
|
|
TCHAR KeyName[MAXKEYNAME*2];
|
|
|
|
hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
|
|
|
|
KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
TreeView_GetSelection(hKeyTreeWnd),
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOCOMPUTER);
|
|
|
|
SetWindowText(g_RegEditData.hStatusBarWnd, KeyName);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RegEdit_OnCopyKeyName
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
RegEdit_OnCopyKeyName(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
TCHAR KeyName[MAXKEYNAME*2];
|
|
UINT KeyNameLength;
|
|
HANDLE hClipboardData;
|
|
LPTSTR lpClipboardData;
|
|
|
|
KeyTree_BuildKeyPath( g_RegEditData.hKeyTreeWnd,
|
|
hTreeItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
BKP_TOSYMBOLICROOT);
|
|
|
|
KeyNameLength = (lstrlen(KeyName) + 1) * sizeof(TCHAR);
|
|
|
|
if (OpenClipboard(hWnd))
|
|
{
|
|
if ((hClipboardData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, KeyNameLength)) != NULL)
|
|
{
|
|
lpClipboardData = (LPTSTR) GlobalLock(hClipboardData);
|
|
|
|
CopyMemory(lpClipboardData, KeyName, KeyNameLength);
|
|
|
|
GlobalUnlock(hClipboardData);
|
|
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_UNICODETEXT, hClipboardData);
|
|
}
|
|
|
|
CloseClipboard();
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* KeyTree_BuildKeyPath
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hTreeViewWnd, handle of KeyTree window.
|
|
* hTreeItem, handle of tree item to begin building from.
|
|
* lpKeyPath, buffer to store path in.
|
|
* cchKeyPathMax, maximum # of characters in key path, includes space for NULL
|
|
* fIncludeSymbolicRootName, TRUE if root key's name should be included
|
|
* (e.g., HKEY_LOCAL_MACHINE), else FALSE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
HKEY
|
|
PASCAL
|
|
KeyTree_BuildKeyPath(
|
|
HWND hTreeViewWnd,
|
|
HTREEITEM hTreeItem,
|
|
LPTSTR lpKeyPath,
|
|
DWORD cchKeyPathMax,
|
|
UINT ToFlags
|
|
)
|
|
{
|
|
|
|
TV_ITEM TVItem;
|
|
TCHAR SubKeyName[MAXKEYNAME*2];
|
|
|
|
*lpKeyPath = '\0';
|
|
|
|
TVItem.mask = TVIF_TEXT | TVIF_PARAM;
|
|
TVItem.hItem = hTreeItem;
|
|
TVItem.pszText = (LPTSTR) SubKeyName;
|
|
TVItem.cchTextMax = ARRAYSIZE(SubKeyName);
|
|
|
|
while (TRUE) {
|
|
|
|
TreeView_GetItem(hTreeViewWnd, &TVItem);
|
|
|
|
if (TVItem.lParam != 0 && !(ToFlags & BKP_TOSYMBOLICROOT))
|
|
break;
|
|
|
|
if (*lpKeyPath != '\0') {
|
|
|
|
StringCchCat(SubKeyName, ARRAYSIZE(SubKeyName), TEXT("\\"));
|
|
StringCchCat(SubKeyName, ARRAYSIZE(SubKeyName), lpKeyPath);
|
|
|
|
}
|
|
|
|
StringCchCopy(lpKeyPath, cchKeyPathMax, SubKeyName);
|
|
|
|
if (TVItem.lParam != 0 && (ToFlags & BKP_TOCOMPUTER) != BKP_TOCOMPUTER)
|
|
break;
|
|
|
|
TVItem.hItem = TreeView_GetParent(hTreeViewWnd, TVItem.hItem);
|
|
|
|
if (TVItem.hItem == NULL) {
|
|
|
|
if ((ToFlags & BKP_TOCOMPUTER) != BKP_TOCOMPUTER)
|
|
*lpKeyPath = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ((HKEY) TVItem.lParam);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* KeyTree_InsertItem
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
HTREEITEM
|
|
PASCAL
|
|
KeyTree_InsertItem(
|
|
HWND hKeyTreeWnd,
|
|
HTREEITEM hParent,
|
|
HTREEITEM hInsertAfter,
|
|
LPCTSTR lpText,
|
|
UINT fHasKids,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
|
|
TV_INSERTSTRUCT TVInsertStruct;
|
|
|
|
TVInsertStruct.hParent = hParent;
|
|
TVInsertStruct.hInsertAfter = hInsertAfter;
|
|
TVInsertStruct.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
|
|
TVIF_PARAM | TVIF_CHILDREN;
|
|
// TVInsertStruct.item.hItem = NULL;
|
|
// TVInsertStruct.item.state = 0;
|
|
// TVInsertStruct.item.stateMask = 0;
|
|
TVInsertStruct.item.pszText = (LPTSTR) lpText;
|
|
// TVInsertStruct.item.cchTextMax = lstrlen(lpText);
|
|
TVInsertStruct.item.iImage = IMAGEINDEX(IDI_FOLDER);
|
|
TVInsertStruct.item.iSelectedImage = IMAGEINDEX(IDI_FOLDEROPEN);
|
|
TVInsertStruct.item.cChildren = fHasKids;
|
|
TVInsertStruct.item.lParam = lParam;
|
|
|
|
return TreeView_InsertItem(hKeyTreeWnd, &TVInsertStruct);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* KeyTree_ExpandBranch
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hTreeViewWnd, handle of KeyTree window.
|
|
* hTreeItem, handle of tree item to edit.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
PASCAL
|
|
KeyTree_ExpandBranch(
|
|
HWND hKeyTreeWnd,
|
|
HTREEITEM hExpandingTreeItem
|
|
)
|
|
{
|
|
|
|
TCHAR KeyName[MAXKEYNAME];
|
|
HKEY hRootKey;
|
|
HKEY hEnumKey;
|
|
int Index;
|
|
int MaximumSubKeyLength;
|
|
|
|
//
|
|
// Nothing special needs to be done with a top-level label such as "My
|
|
// Computer" or a network computer name. It's children are already filled
|
|
// in and are always valid.
|
|
//
|
|
|
|
if (TreeView_GetParent(hKeyTreeWnd, hExpandingTreeItem) == NULL)
|
|
return TRUE;
|
|
|
|
hRootKey = KeyTree_BuildKeyPath( hKeyTreeWnd,
|
|
hExpandingTreeItem,
|
|
KeyName,
|
|
ARRAYSIZE(KeyName),
|
|
FALSE);
|
|
|
|
if(RegOpenKeyEx(hRootKey,KeyName,0,KEY_ENUMERATE_SUB_KEYS,&hEnumKey) != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
MaximumSubKeyLength = MAXKEYNAME - (lstrlen(KeyName) + 1);
|
|
Index = 0;
|
|
|
|
while (RegEnumKey(hEnumKey, Index++, KeyName, MaximumSubKeyLength) ==
|
|
ERROR_SUCCESS) {
|
|
|
|
KeyTree_InsertItem(hKeyTreeWnd, hExpandingTreeItem, TVI_FIRST,
|
|
KeyName, DoesKeyHaveKids(hEnumKey, KeyName), 0);
|
|
|
|
}
|
|
|
|
RegCloseKey(hEnumKey);
|
|
|
|
//
|
|
// Sort the subkeys _after_ inserting all the items. The above insert
|
|
// used to specify TVI_SORT, but on NT, expanding a key with several
|
|
// subkeys (e.g., HKEY_CLASSES_ROOT) would take several seconds!
|
|
//
|
|
|
|
TreeView_SortChildren(hKeyTreeWnd, hExpandingTreeItem, FALSE);
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* DoesKeyHaveKids
|
|
*
|
|
* DESCRIPTION:
|
|
* Checks if the given key path has any subkeys or not.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
PASCAL
|
|
DoesKeyHaveKids(
|
|
HKEY hKey,
|
|
LPTSTR lpKeyName
|
|
)
|
|
{
|
|
|
|
BOOL fHasKids;
|
|
HKEY hCheckChildrenKey;
|
|
DWORD cSubKeys;
|
|
|
|
fHasKids = FALSE;
|
|
|
|
if ( RegOpenKeyEx(hKey, lpKeyName, 0, KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &hCheckChildrenKey)
|
|
== ERROR_SUCCESS )
|
|
{
|
|
if (RegQueryInfoKey(hCheckChildrenKey, NULL, NULL, NULL, &cSubKeys,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
|
|
cSubKeys > 0)
|
|
fHasKids = TRUE;
|
|
|
|
RegCloseKey(hCheckChildrenKey);
|
|
}
|
|
|
|
return fHasKids;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* KeyTree_EditLabel
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hTreeViewWnd, handle of KeyTree window.
|
|
* hTreeItem, handle of tree item to edit.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID
|
|
PASCAL
|
|
KeyTree_EditLabel(
|
|
HWND hKeyTreeWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
g_RegEditData.fAllowLabelEdits = TRUE;
|
|
|
|
TreeView_EditLabel(hKeyTreeWnd, hTreeItem);
|
|
|
|
g_RegEditData.fAllowLabelEdits = FALSE;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* KeyTree_CanDeleteOrRenameItem
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hTreeViewWnd, handle of KeyTree window.
|
|
* hTreeItem, handle of tree item to check.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL
|
|
PASCAL
|
|
KeyTree_CanDeleteOrRenameItem(
|
|
HWND hWnd,
|
|
HTREEITEM hTreeItem
|
|
)
|
|
{
|
|
|
|
TV_ITEM TVItem;
|
|
|
|
//
|
|
// Check if the selected tree item is null. This will occur when viewing
|
|
// the Edit popup from the main menu with no selection made.
|
|
//
|
|
|
|
if (hTreeItem != NULL) {
|
|
|
|
//
|
|
// Check if this tree item has any reference data indicating that it
|
|
// is a predefined root. Predefined roots cannot be renamed or
|
|
// deleted.
|
|
//
|
|
|
|
TVItem.hItem = hTreeItem;
|
|
TVItem.mask = TVIF_PARAM;
|
|
TreeView_GetItem(hWnd, &TVItem);
|
|
|
|
if ((HKEY) TVItem.lParam == NULL) {
|
|
|
|
//
|
|
// Check that this isn't a top-level item such as "My Computer" or
|
|
// a remote registry connection.
|
|
//
|
|
|
|
if (TreeView_GetParent(hWnd, hTreeItem) != NULL)
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// KeyTree_GetRootKey
|
|
//
|
|
// DESCRIPTION: Returns the root key of the item (HKEY_ ...)
|
|
//
|
|
// PARAMETERS: hTreeItem - treeview item
|
|
//------------------------------------------------------------------------------
|
|
HKEY KeyTree_GetRootKey(HTREEITEM hTreeItem)
|
|
{
|
|
TV_ITEM TVItem;
|
|
TVItem.mask = TVIF_PARAM;
|
|
TVItem.hItem = hTreeItem;
|
|
|
|
TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
|
|
while (!TVItem.lParam)
|
|
{
|
|
TVItem.hItem = TreeView_GetParent(g_RegEditData.hKeyTreeWnd, TVItem.hItem);
|
|
TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
}
|
|
|
|
return ((HKEY) TVItem.lParam);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// KeyTree_GetKeyName
|
|
//
|
|
// DESCRIPTION: Returns the TEXT of an item
|
|
//
|
|
// PARAMETERS: hTreeItem - treeview item
|
|
// pszText - pointer to an TCHAR array
|
|
// cchMax - number of characters in the array
|
|
//------------------------------------------------------------------------------
|
|
PTSTR KeyTree_GetKeyName(HTREEITEM hTreeItem, PTSTR pszName, int cchNameMax)
|
|
{
|
|
TV_ITEM TVItem;
|
|
|
|
pszName[0] = TEXT('\0');
|
|
|
|
TVItem.mask = TVIF_TEXT;
|
|
TVItem.hItem = hTreeItem;
|
|
TVItem.pszText = pszName;
|
|
TVItem.cchTextMax = cchNameMax;
|
|
|
|
TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
|
|
|
|
return TVItem.pszText;
|
|
}
|
|
|