/******************************************************************************* * * (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, 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 = sizeof(KeyName)/sizeof(TCHAR); 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, 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, HKEY hKey) { HKEY hNewKey; UINT uNewKeyNameID = 1; while (uNewKeyNameID < MAX_KEYNAME_TEMPLATE_ID) { wsprintf(pszKeyName, 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 = sizeof(KeyName)/sizeof(TCHAR); 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, 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 = sizeof(KeyName)/sizeof(TCHAR); 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, 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. // lstrcpy(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 = sizeof(SourceKeyName)/sizeof(TCHAR); 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, 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 = sizeof(KeyName)/sizeof(TCHAR); 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 = sizeof(KeyName)/sizeof(TCHAR); 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, 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, 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, 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. * 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, 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 = sizeof(SubKeyName)/sizeof(TCHAR); while (TRUE) { TreeView_GetItem(hTreeViewWnd, &TVItem); if (TVItem.lParam != 0 && !(ToFlags & BKP_TOSYMBOLICROOT)) break; if (*lpKeyPath != '\0') { lstrcat(SubKeyName, TEXT("\\")); lstrcat(SubKeyName, lpKeyPath); } lstrcpy(lpKeyPath, 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, 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; }