|  | /* | 
|  | * Regedit listviews | 
|  | * | 
|  | * Copyright (C) 2002 Robert Dickenson <robd@reactos.org> | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <windows.h> | 
|  | #include <windowsx.h> | 
|  | #include <commctrl.h> | 
|  | #include <stdlib.h> | 
|  | #include <tchar.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "main.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(regedit); | 
|  |  | 
|  | static INT Image_String; | 
|  | static INT Image_Binary; | 
|  |  | 
|  | typedef struct tagLINE_INFO | 
|  | { | 
|  | DWORD dwValType; | 
|  | LPTSTR name; | 
|  | void* val; | 
|  | size_t val_len; | 
|  | } LINE_INFO; | 
|  |  | 
|  | /******************************************************************************* | 
|  | * Global and Local Variables: | 
|  | */ | 
|  |  | 
|  | static WNDPROC g_orgListWndProc; | 
|  | static DWORD g_columnToSort = ~0UL; | 
|  | static BOOL  g_invertSort = FALSE; | 
|  | static LPTSTR g_valueName; | 
|  | static LPTSTR g_currentPath; | 
|  | static HKEY g_currentRootKey; | 
|  |  | 
|  | #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1) | 
|  | static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 }; | 
|  | static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT }; | 
|  |  | 
|  | static LPTSTR get_item_text(HWND hwndLV, int item) | 
|  | { | 
|  | LPTSTR newStr, curStr; | 
|  | unsigned int maxLen = 128; | 
|  |  | 
|  | curStr = HeapAlloc(GetProcessHeap(), 0, maxLen); | 
|  | if (!curStr) return NULL; | 
|  | if (item == 0) return NULL; /* first item is ALWAYS a default */ | 
|  | do { | 
|  | ListView_GetItemText(hwndLV, item, 0, curStr, maxLen); | 
|  | if (_tcslen(curStr) < maxLen - 1) return curStr; | 
|  | newStr = HeapReAlloc(GetProcessHeap(), 0, curStr, maxLen * 2); | 
|  | if (!newStr) break; | 
|  | curStr = newStr; | 
|  | maxLen *= 2; | 
|  | } while (TRUE); | 
|  | HeapFree(GetProcessHeap(), 0, curStr); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | LPCTSTR GetValueName(HWND hwndLV) | 
|  | { | 
|  | INT item; | 
|  |  | 
|  | if (g_valueName && g_valueName != LPSTR_TEXTCALLBACK) | 
|  | HeapFree(GetProcessHeap(), 0,  g_valueName); | 
|  | g_valueName = NULL; | 
|  |  | 
|  | item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED); | 
|  | if (item == -1) return NULL; | 
|  |  | 
|  | g_valueName = get_item_text(hwndLV, item); | 
|  |  | 
|  | return g_valueName; | 
|  | } | 
|  |  | 
|  | /* convert '\0' separated string list into ',' separated string list */ | 
|  | static void MakeMULTISZDisplayable(LPTSTR multi) | 
|  | { | 
|  | do | 
|  | { | 
|  | for (; *multi; multi++) | 
|  | ; | 
|  | if (*(multi+1)) | 
|  | { | 
|  | *multi = ','; | 
|  | multi++; | 
|  | } | 
|  | } while (*multi); | 
|  | } | 
|  |  | 
|  | /******************************************************************************* | 
|  | * Local module support methods | 
|  | */ | 
|  | static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, | 
|  | void* ValBuf, DWORD dwCount, BOOL bHighlight) | 
|  | { | 
|  | LINE_INFO* linfo; | 
|  | LVITEM item; | 
|  | int index; | 
|  |  | 
|  | linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount); | 
|  | linfo->dwValType = dwValType; | 
|  | linfo->val_len = dwCount; | 
|  | memcpy(&linfo[1], ValBuf, dwCount); | 
|  |  | 
|  | if (Name) | 
|  | linfo->name = _tcsdup(Name); | 
|  | else | 
|  | linfo->name = NULL; | 
|  |  | 
|  | item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE; | 
|  | item.iItem = ListView_GetItemCount(hwndLV);/*idx;  */ | 
|  | item.iSubItem = 0; | 
|  | item.state = 0; | 
|  | item.stateMask = LVIS_FOCUSED | LVIS_SELECTED; | 
|  | item.pszText = Name ? Name : LPSTR_TEXTCALLBACK; | 
|  | item.cchTextMax = Name ? _tcslen(item.pszText) : 0; | 
|  | if (bHighlight) { | 
|  | item.stateMask = item.state = LVIS_FOCUSED | LVIS_SELECTED; | 
|  | } | 
|  | switch (dwValType) | 
|  | { | 
|  | case REG_SZ: | 
|  | case REG_EXPAND_SZ: | 
|  | case REG_MULTI_SZ: | 
|  | item.iImage = Image_String; | 
|  | break; | 
|  | default: | 
|  | item.iImage = Image_Binary; | 
|  | break; | 
|  | } | 
|  | item.lParam = (LPARAM)linfo; | 
|  |  | 
|  | #if (_WIN32_IE >= 0x0300) | 
|  | item.iIndent = 0; | 
|  | #endif | 
|  |  | 
|  | index = ListView_InsertItem(hwndLV, &item); | 
|  | if (index != -1) { | 
|  | /*        LPTSTR pszText = NULL; */ | 
|  | LPTSTR pszText = _T("(cannot display value)"); | 
|  | switch (dwValType) { | 
|  | case REG_SZ: | 
|  | case REG_EXPAND_SZ: | 
|  | if (ValBuf) { | 
|  | ListView_SetItemText(hwndLV, index, 2, ValBuf); | 
|  | } else { | 
|  | ListView_SetItemText(hwndLV, index, 2, "(not set)"); | 
|  | } | 
|  | break; | 
|  | case REG_DWORD: { | 
|  | TCHAR buf[64]; | 
|  | wsprintf(buf, _T("0x%08X (%d)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf); | 
|  | ListView_SetItemText(hwndLV, index, 2, buf); | 
|  | } | 
|  | /*            lpsRes = convertHexToDWORDStr(lpbData, dwLen); */ | 
|  | break; | 
|  | case REG_BINARY: { | 
|  | unsigned int i; | 
|  | LPBYTE pData = (LPBYTE)ValBuf; | 
|  | LPTSTR strBinary = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(TCHAR) * 3 + 1); | 
|  | for (i = 0; i < dwCount; i++) | 
|  | wsprintf( strBinary + i*3, _T("%02X "), pData[i] ); | 
|  | strBinary[dwCount * 3] = 0; | 
|  | ListView_SetItemText(hwndLV, index, 2, strBinary); | 
|  | HeapFree(GetProcessHeap(), 0, strBinary); | 
|  | } | 
|  | break; | 
|  | case REG_MULTI_SZ: | 
|  | MakeMULTISZDisplayable(ValBuf); | 
|  | ListView_SetItemText(hwndLV, index, 2, ValBuf); | 
|  | break; | 
|  | default: | 
|  | /*            lpsRes = convertHexToHexCSV(lpbData, dwLen); */ | 
|  | ListView_SetItemText(hwndLV, index, 2, pszText); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static BOOL InitListViewImageList(HWND hWndListView) | 
|  | { | 
|  | HIMAGELIST himl; | 
|  | HICON hicon; | 
|  | INT cx = GetSystemMetrics(SM_CXSMICON); | 
|  | INT cy = GetSystemMetrics(SM_CYSMICON); | 
|  |  | 
|  | himl = ImageList_Create(cx, cy, ILC_MASK, 0, 2); | 
|  | if (!himl) | 
|  | return FALSE; | 
|  |  | 
|  | hicon = LoadImage(hInst, MAKEINTRESOURCE(IDI_STRING), | 
|  | IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR); | 
|  | Image_String = ImageList_AddIcon(himl, hicon); | 
|  |  | 
|  | hicon = LoadImage(hInst, MAKEINTRESOURCE(IDI_BIN), | 
|  | IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR); | 
|  | Image_Binary = ImageList_AddIcon(himl, hicon); | 
|  |  | 
|  | ListView_SetImageList(hWndListView, himl, LVSIL_SMALL); | 
|  |  | 
|  | /* fail if some of the icons failed to load */ | 
|  | if (ImageList_GetImageCount(himl) < 2) | 
|  | return FALSE; | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CreateListColumns(HWND hWndListView) | 
|  | { | 
|  | TCHAR szText[50]; | 
|  | int index; | 
|  | LV_COLUMN lvC; | 
|  |  | 
|  | /* Create columns. */ | 
|  | lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; | 
|  | lvC.pszText = szText; | 
|  |  | 
|  | /* Load the column labels from the resource file. */ | 
|  | for (index = 0; index < MAX_LIST_COLUMNS; index++) { | 
|  | lvC.iSubItem = index; | 
|  | lvC.cx = default_column_widths[index]; | 
|  | lvC.fmt = column_alignment[index]; | 
|  | LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR)); | 
|  | if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message.  */ | 
|  |  | 
|  | static void OnGetDispInfo(NMLVDISPINFO* plvdi) | 
|  | { | 
|  | static TCHAR buffer[200]; | 
|  |  | 
|  | plvdi->item.pszText = NULL; | 
|  | plvdi->item.cchTextMax = 0; | 
|  |  | 
|  | switch (plvdi->item.iSubItem) { | 
|  | case 0: | 
|  | plvdi->item.pszText = (LPSTR)g_pszDefaultValueName; | 
|  | break; | 
|  | case 1: | 
|  | switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) { | 
|  | case REG_SZ: | 
|  | plvdi->item.pszText = _T("REG_SZ"); | 
|  | break; | 
|  | case REG_EXPAND_SZ: | 
|  | plvdi->item.pszText = _T("REG_EXPAND_SZ"); | 
|  | break; | 
|  | case REG_BINARY: | 
|  | plvdi->item.pszText = _T("REG_BINARY"); | 
|  | break; | 
|  | case REG_DWORD: | 
|  | plvdi->item.pszText = _T("REG_DWORD"); | 
|  | break; | 
|  | case REG_DWORD_BIG_ENDIAN: | 
|  | plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN"); | 
|  | break; | 
|  | case REG_MULTI_SZ: | 
|  | plvdi->item.pszText = _T("REG_MULTI_SZ"); | 
|  | break; | 
|  | case REG_LINK: | 
|  | plvdi->item.pszText = _T("REG_LINK"); | 
|  | break; | 
|  | case REG_RESOURCE_LIST: | 
|  | plvdi->item.pszText = _T("REG_RESOURCE_LIST"); | 
|  | break; | 
|  | case REG_NONE: | 
|  | plvdi->item.pszText = _T("REG_NONE"); | 
|  | break; | 
|  | default: | 
|  | wsprintf(buffer, _T("unknown(%d)"), plvdi->item.lParam); | 
|  | plvdi->item.pszText = buffer; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case 2: | 
|  | plvdi->item.pszText = _T("(value not set)"); | 
|  | break; | 
|  | case 3: | 
|  | plvdi->item.pszText = _T(""); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) | 
|  | { | 
|  | LINE_INFO*l, *r; | 
|  | l = (LINE_INFO*)lParam1; | 
|  | r = (LINE_INFO*)lParam2; | 
|  | if (!l->name) return -1; | 
|  | if (!r->name) return +1; | 
|  |  | 
|  | if (g_columnToSort == ~0UL) | 
|  | g_columnToSort = 0; | 
|  |  | 
|  | if (g_columnToSort == 1 && l->dwValType != r->dwValType) | 
|  | return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType; | 
|  | if (g_columnToSort == 2) { | 
|  | /* FIXME: Sort on value */ | 
|  | } | 
|  | return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name); | 
|  | } | 
|  |  | 
|  | HWND StartValueRename(HWND hwndLV) | 
|  | { | 
|  | int item; | 
|  |  | 
|  | item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED | LVNI_SELECTED); | 
|  | if (item < 1) { /* cannot rename default key */ | 
|  | MessageBeep(MB_ICONHAND); | 
|  | return 0; | 
|  | } | 
|  | return ListView_EditLabel(hwndLV, item); | 
|  | } | 
|  |  | 
|  | static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | switch (LOWORD(wParam)) { | 
|  | /*    case ID_FILE_OPEN: */ | 
|  | /*        break; */ | 
|  | default: | 
|  | return FALSE; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | switch (message) { | 
|  | case WM_COMMAND: | 
|  | if (!_CmdWndProc(hWnd, message, wParam, lParam)) { | 
|  | return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam); | 
|  | } | 
|  | break; | 
|  | case WM_NOTIFY_REFLECT: | 
|  | switch (((LPNMHDR)lParam)->code) { | 
|  |  | 
|  | case LVN_BEGINLABELEDIT: | 
|  | if (!((NMLVDISPINFO *)lParam)->item.iItem) | 
|  | return 1; | 
|  | return 0; | 
|  | case LVN_GETDISPINFO: | 
|  | OnGetDispInfo((NMLVDISPINFO*)lParam); | 
|  | break; | 
|  | case LVN_COLUMNCLICK: | 
|  | if (g_columnToSort == ((LPNMLISTVIEW)lParam)->iSubItem) | 
|  | g_invertSort = !g_invertSort; | 
|  | else { | 
|  | g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem; | 
|  | g_invertSort = FALSE; | 
|  | } | 
|  |  | 
|  | ListView_SortItems(hWnd, CompareFunc, hWnd); | 
|  | break; | 
|  | case LVN_ENDLABELEDIT: { | 
|  | LPNMLVDISPINFO dispInfo = (LPNMLVDISPINFO)lParam; | 
|  | LPTSTR valueName = get_item_text(hWnd, dispInfo->item.iItem); | 
|  | LONG ret; | 
|  | if (!valueName) return -1; /* cannot rename a default value */ | 
|  | ret = RenameValue(hWnd, g_currentRootKey, g_currentPath, valueName, dispInfo->item.pszText); | 
|  | if (ret) | 
|  | RefreshListView(hWnd, g_currentRootKey, g_currentPath, dispInfo->item.pszText); | 
|  | HeapFree(GetProcessHeap(), 0, valueName); | 
|  | return 0; | 
|  | } | 
|  | case NM_RETURN: { | 
|  | int cnt = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED | LVNI_SELECTED); | 
|  | if (cnt != -1) | 
|  | SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0); | 
|  | } | 
|  | break; | 
|  | case NM_DBLCLK: { | 
|  | NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam; | 
|  | LVHITTESTINFO info; | 
|  |  | 
|  | /* if (nmitem->hdr.hwndFrom != hWnd) break; unnecessary because of WM_NOTIFY_REFLECT */ | 
|  | /*            if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;  */ | 
|  | /*            if (nmitem->hdr.code != ???) break;  */ | 
|  | #ifdef _MSC_VER | 
|  | switch (nmitem->uKeyFlags) { | 
|  | case LVKF_ALT:     /*  The ALT key is pressed.   */ | 
|  | /* properties dialog box ? */ | 
|  | break; | 
|  | case LVKF_CONTROL: /*  The CTRL key is pressed. */ | 
|  | /* run dialog box for providing parameters... */ | 
|  | break; | 
|  | case LVKF_SHIFT:   /*  The SHIFT key is pressed.    */ | 
|  | break; | 
|  | } | 
|  | #endif | 
|  | info.pt.x = nmitem->ptAction.x; | 
|  | info.pt.y = nmitem->ptAction.y; | 
|  | if (ListView_HitTest(hWnd, &info) != -1) { | 
|  | ListView_SetItemState(hWnd, -1, 0, LVIS_FOCUSED|LVIS_SELECTED); | 
|  | ListView_SetItemState(hWnd, info.iItem, LVIS_FOCUSED|LVIS_SELECTED, | 
|  | LVIS_FOCUSED|LVIS_SELECTED); | 
|  | SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return 0; /* shouldn't call default ! */ | 
|  | } | 
|  | break; | 
|  | case WM_CONTEXTMENU: { | 
|  | int cnt = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); | 
|  | TrackPopupMenu(GetSubMenu(hPopupMenus, cnt == -1 ? PM_NEW : PM_MODIFYVALUE), | 
|  | TPM_RIGHTBUTTON, (short)LOWORD(lParam), (short)HIWORD(lParam), | 
|  | 0, hFrameWnd, NULL); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam); | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | HWND CreateListView(HWND hwndParent, int id) | 
|  | { | 
|  | RECT rcClient; | 
|  | HWND hwndLV; | 
|  |  | 
|  | /* Get the dimensions of the parent window's client area, and create the list view control.  */ | 
|  | GetClientRect(hwndParent, &rcClient); | 
|  | hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"), | 
|  | WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS, | 
|  | 0, 0, rcClient.right, rcClient.bottom, | 
|  | hwndParent, (HMENU)id, hInst, NULL); | 
|  | if (!hwndLV) return NULL; | 
|  | ListView_SetExtendedListViewStyle(hwndLV,  LVS_EX_FULLROWSELECT); | 
|  |  | 
|  | /* Initialize the image list */ | 
|  | if (!InitListViewImageList(hwndLV)) goto fail; | 
|  | if (!CreateListColumns(hwndLV)) goto fail; | 
|  | g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc); | 
|  | return hwndLV; | 
|  | fail: | 
|  | DestroyWindow(hwndLV); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | BOOL RefreshListView(HWND hwndLV, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR highlightValue) | 
|  | { | 
|  | BOOL result = FALSE; | 
|  | DWORD max_sub_key_len; | 
|  | DWORD max_val_name_len, valNameLen; | 
|  | DWORD max_val_size, valSize; | 
|  | DWORD val_count, index, valType; | 
|  | TCHAR* valName = 0; | 
|  | BYTE* valBuf = 0; | 
|  | HKEY hKey = 0; | 
|  | LONG errCode; | 
|  | INT count, i; | 
|  | LVITEM item; | 
|  |  | 
|  | if (!hwndLV) return FALSE; | 
|  |  | 
|  | SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0); | 
|  |  | 
|  | errCode = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ, &hKey); | 
|  | if (errCode != ERROR_SUCCESS) goto done; | 
|  |  | 
|  | count = ListView_GetItemCount(hwndLV); | 
|  | for (i = 0; i < count; i++) { | 
|  | item.mask = LVIF_PARAM; | 
|  | item.iItem = i; | 
|  | ListView_GetItem(hwndLV, &item); | 
|  | free(((LINE_INFO*)item.lParam)->name); | 
|  | HeapFree(GetProcessHeap(), 0, (void*)item.lParam); | 
|  | } | 
|  | g_columnToSort = ~0UL; | 
|  | ListView_DeleteAllItems(hwndLV); | 
|  |  | 
|  | /* get size information and resize the buffers if necessary */ | 
|  | errCode = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL, | 
|  | &val_count, &max_val_name_len, &max_val_size, NULL, NULL); | 
|  | if (errCode != ERROR_SUCCESS) goto done; | 
|  |  | 
|  | /* account for the terminator char */ | 
|  | max_val_name_len++; | 
|  | max_val_size++; | 
|  |  | 
|  | valName = HeapAlloc(GetProcessHeap(), 0, max_val_name_len * sizeof(TCHAR)); | 
|  | valBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size); | 
|  | if (RegQueryValueEx(hKey, NULL, NULL, &valType, valBuf, &valSize) == ERROR_FILE_NOT_FOUND) { | 
|  | AddEntryToList(hwndLV, NULL, REG_SZ, NULL, 0, !highlightValue); | 
|  | } | 
|  | /*dwValSize = max_val_size; */ | 
|  | for(index = 0; index < val_count; index++) { | 
|  | BOOL bSelected = (valName == highlightValue); /* NOT a bug, we check for double NULL here */ | 
|  | valNameLen = max_val_name_len; | 
|  | valSize = max_val_size; | 
|  | valType = 0; | 
|  | errCode = RegEnumValue(hKey, index, valName, &valNameLen, NULL, &valType, valBuf, &valSize); | 
|  | if (errCode != ERROR_SUCCESS) goto done; | 
|  | valBuf[valSize] = 0; | 
|  | if (valName && highlightValue && !_tcscmp(valName, highlightValue)) | 
|  | bSelected = TRUE; | 
|  | AddEntryToList(hwndLV, valName[0] ? valName : NULL, valType, valBuf, valSize, bSelected); | 
|  | } | 
|  | ListView_SortItems(hwndLV, CompareFunc, hwndLV); | 
|  |  | 
|  | g_currentRootKey = hKeyRoot; | 
|  | if (keyPath != g_currentPath) { | 
|  | HeapFree(GetProcessHeap(), 0, g_currentPath); | 
|  | g_currentPath = HeapAlloc(GetProcessHeap(), 0, (lstrlen(keyPath) + 1) * sizeof(TCHAR)); | 
|  | if (!g_currentPath) goto done; | 
|  | lstrcpy(g_currentPath, keyPath); | 
|  | } | 
|  |  | 
|  | result = TRUE; | 
|  |  | 
|  | done: | 
|  | HeapFree(GetProcessHeap(), 0, valBuf); | 
|  | HeapFree(GetProcessHeap(), 0, valName); | 
|  | SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0); | 
|  | if (hKey) RegCloseKey(hKey); | 
|  |  | 
|  | return result; | 
|  | } |