MONKEY.C
/******************************************************************************\ 
*       This is a part of the Microsoft Source Code Samples.  
*       Copyright 1993 - 1998 Microsoft Corporation. 
*       All rights reserved.  
*       This source code is only intended as a supplement to  
*       Microsoft Development Tools and/or WinHelp documentation. 
*       See these sources for detailed information regarding the  
*       Microsoft samples programs. 
\******************************************************************************/ 
 
/*************************************************************************\ 
* 
*  PROGRAM: Monkey: the Registry Monkey Utility. 
*  PURPOSE: To demonstrate Registry API. 
*  COMMENTS: 
* 
\*************************************************************************/ 
 
 
#define STRICT 
#include <windows.h> 
#include <string.h> 
#define _MBCS 
#include <mbstring.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include "monkey.h" 
 
 
HANDLE hInst; 
HWND   hDlg; 
 
HANDLE hHeap; 
 
 
/*************************************************************************\ 
* 
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) 
* 
*  PURPOSE: Creates the dialogbox. 
* 
*  COMMENTS: 
* 
\*************************************************************************/ 
 
int APIENTRY WinMain (HINSTANCE hInstance, 
                      HINSTANCE hPrevInstance, 
                      LPSTR     lpCmdLine, 
                      int       nCmdShow) 
 
 
{ 
  DWORD retCode; 
 
  UNREFERENCED_PARAMETER( nCmdShow ); 
  UNREFERENCED_PARAMETER( lpCmdLine ); 
  UNREFERENCED_PARAMETER( hPrevInstance ); 
 
  hInst   = hInstance; 
  hHeap   = HeapCreate (0, 0, 0); 
 
  retCode = DialogBox ((HANDLE)hInst, (LPCSTR)"MonkeyDlg", 
                        NULL, (DLGPROC)MonkeyDlgProc); 
 
  HeapDestroy (hHeap); 
  return  (retCode); 
 
} 
 
/************************************************************************\ 
* 
*  FUNCTION: MonkeyDlgProc(); 
* 
*  PURPOSE:  Handle the Monkey dialog box messages. 
* 
*  MESSAGES: 
* 
*    WM_INITDIALOG  - Posts WM_GETFIRSTKEY message. 
* 
*    WM_GETFIRSTKEY - Puts the first 4 pre-defined keys in the listbox. 
* 
*    IDL_LISTBOX    - Trapped when an item in the left hand listbox 
*                     has been double clicked.  It posts a IDB_NEXT message. 
* 
*    IDL_LISTBOX2   - Trapped when an item in the right hand listbox has 
*                     been double clicked.  It basically calls DisplayKeyData, 
*                     which fills the Value edit fields with the data from 
*                     the current key's specified value information. 
* 
*    IDB_PRINT      - Basically calls PrintTree() which does a recursive 
*                     print of the Registry from the current key to the 
*                     end of it's branches. 
* 
*    IDB_BACK       - Sets the dialog box with the information from the 
*                     previously selected key (one closer to the root of 
*                     the registry, the parent of the current key). 
* 
*    IDB_NEXT       - Sets the dialog box with the information on the 
*                     selected key child. 
* 
*    IDR_FULL       - Sets a global variable used to determine if the 
*                     user wants to print full Registry information 
*                     or only information from keys that have value 
*                     associated with it.  Variable is set to TRUE. 
* 
*    IDR_TRIMMED    - Same as above, only the variable is set to FALSE. 
* 
\************************************************************************/ 
 
int APIENTRY MonkeyDlgProc (HWND hDlg, WORD wMsg, LONG wParam, LONG lParam) 
  { 
   ULONG    KeyClassLength = 256; 
   ULONG    KeyNameLength = 256; 
   DWORD    indexLB; 
   CHAR     *putNullAt; 
   CHAR     lpBuffer1[128]; 
   CHAR     lpBuffer2[128]; 
 
   static   CHAR     RegPath[MAX_PATH]  = ""; 
   static   CHAR     NameLBSelect[256]  = ""; 
   static   HKEY     hKeyRoot; 
   static   DWORD    RegLevel; 
   static   BOOL     FullBranches = TRUE; 
 
   static   HANDLE   hFile = INVALID_HANDLE_VALUE; 
   static   HANDLE   hBootIni; 
 
  UNREFERENCED_PARAMETER( lParam ); 
 
  switch (wMsg) 
    { 
      case WM_INITDIALOG: 
          // Post a message to get the first 4 pre-defined keys, and set 
          // Full Branches to be the print default. 
        PostMessage (hDlg, WM_GETFIRSTKEY, 0, 0); 
        CheckDlgButton (hDlg, IDR_FULL, TRUE); 
        return (0); 
 
      case WM_GETFIRSTKEY: 
          // Initialize by putting the first 4 predefined keys of the 
          // registry in the list box. 
 
        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX), 
                     LB_ADDSTRING, 0, (LONG)"HKEY_LOCAL_MACHINE"); 
 
        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX), 
                     LB_ADDSTRING, 0, (LONG)"HKEY_CURRENT_USER"); 
 
        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX), 
                     LB_ADDSTRING, 0, (LONG)"HKEY_USERS"); 
 
        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX), 
                     LB_ADDSTRING, 0, (LONG)"HKEY_CLASSES_ROOT"); 
 
        hKeyRoot = 0;                   // Initialize hKeyRoot. 
        return (0); 
 
      case WM_SYSCOMMAND: 
        if (wParam == SC_CLOSE) 
          { 
          EndDialog (hDlg, TRUE); 
          if (hFile != INVALID_HANDLE_VALUE) 
            CloseHandle (hFile); 
          return (TRUE); 
          } 
        break; 
 
      case WM_COMMAND: 
 
        switch (LOWORD(wParam)) 
          { 
            case IDR_FULL: 
                // If Full Branches pressed, set global var to TRUE. 
              FullBranches = TRUE; 
              return (0); 
 
            case IDR_TRIMMED: 
                // If Trimmed Branches pressed, set global var to FALSE. 
              FullBranches = FALSE; 
              return (0); 
 
            case IDL_LISTBOX: 
                 // If double click in left hand listbox, clear Value 
                 // edit fields, and execute Next functionality. 
               if ( HIWORD (wParam) == LBN_DBLCLK) 
                { 
                 SetDlgItemText (hDlg, IDE_VALUE1, ""); 
                 SetDlgItemText (hDlg, IDE_VALUE2, ""); 
                 PostMessage (hDlg, WM_COMMAND, IDB_NEXT, 0); 
                } 
               return (0); 
 
            case IDL_LISTBOX2: 
                 // If double click right hand listbox, clear Value edit 
                 // fields, then display the key's data. 
               if ( HIWORD (wParam) == LBN_DBLCLK) 
                { 
                 SetDlgItemText (hDlg, IDE_VALUE1, ""); 
                 SetDlgItemText (hDlg, IDE_VALUE2, ""); 
                 DisplayKeyData (hDlg, RegPath, hKeyRoot); 
                } 
               return (0); 
 
 
            case IDB_NEXT: 
                 // Get the index of the cursor selection 
                 // in the list box. 
               indexLB = SendMessage (GetDlgItem (hDlg, IDL_LISTBOX), 
                                      LB_GETCURSEL, 0, 0); 
 
                 // If nothing is selected, flag user and return, otherwise 
                 // process the selected key. 
                                       // LB_ERR indicates nothing selected. 
               if (indexLB == LB_ERR) 
                 { 
                 LoadString(hInst, IDS_SELECTMSG, lpBuffer1, sizeof(lpBuffer1)); 
                 LoadString(hInst, IDS_NAME, lpBuffer2, sizeof(lpBuffer2)); 
                 MessageBox (hDlg, lpBuffer1, lpBuffer2, MB_OK); 
                 return (0); 
                 } 
 
                 // If listbox item 0 is pressed, user wants to move 
                 // back up.  Execute the Back functionality. 
               if (indexLB == 0 && hKeyRoot) 
                { 
                PostMessage (hDlg, WM_COMMAND, IDB_BACK, 0); 
                return (0); 
                } 
 
 
                                       // Get text from selection in LB. 
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX), 
                            LB_GETTEXT, indexLB, (LPARAM)NameLBSelect); 
 
                                       // Put name of chosen item in Name field. 
               SetDlgItemText (hDlg, IDE_NAME, NameLBSelect); 
 
                                       // Then clear ListBox entries. 
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX), 
                            LB_RESETCONTENT, 0, 0); 
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2), 
                            LB_RESETCONTENT, 0, 0); 
 
               EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot); 
 
               return (0); 
 
 
 
            case IDB_BACK: 
 
                 // For this case (hRootKey = 0)you're at the top level already. 
                 // Tell the user, then return 
               if (!hKeyRoot) 
                 { 
                 LoadString(hInst, IDS_TOPLEVEL, lpBuffer1, sizeof(lpBuffer1)); 
                 LoadString(hInst, IDS_NAME, lpBuffer2, sizeof(lpBuffer2)); 
                 MessageBox (hDlg, lpBuffer1, lpBuffer2, MB_OK); 
                 return (0); 
                 } 
 
                 //For all remaining cases, clear the listboxes. 
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX), 
                            LB_RESETCONTENT, 0, 0); 
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2), 
                            LB_RESETCONTENT, 0, 0); 
 
 
 
                 // If hRootKey has a value, but the pathname is blank, 
                 // then you must be 1 level deep, reset to level 0 by 
                 // posting WM_GETFIRSTKEY. 
               if (strcmp (RegPath, "") == 0) 
                 { 
                 SetDlgItemText (hDlg, IDE_NAME, ""); 
                 PostMessage (hDlg, WM_GETFIRSTKEY, 0, 0); 
                 return (0); 
                 } 
 
 
                 // Two cases left.  One in which the path has only one 
                 // key name in it, and no back slash character (meaning 
                 // strrchr() will return NULL); and one the other case 
                 // where there are more than one key name in the path ( 
                 // and at least one back slash for strrchr().  If this 
                 // is the first case, we want to fakeout EnumerateLevel 
                 // into thinking we just picked one of the pre-defined keys, 
                 // and then re-enumerate it's child keys. 
               if ((putNullAt = _mbsrchr (RegPath, '\\')) == NULL) 
                 { 
                 RegPath[0] = '\0'; 
 
                 switch ((DWORD)hKeyRoot) 
                   { 
                   case (DWORD)HKEY_LOCAL_MACHINE: 
                     strcpy (NameLBSelect, "HKEY_LOCAL_MACHINE"); 
                     break; 
 
                   case (DWORD)HKEY_USERS: 
                     strcpy (NameLBSelect, "HKEY_USERS"); 
                     break; 
 
                   case (DWORD)HKEY_CURRENT_USER: 
                     strcpy (NameLBSelect, "HKEY_CURRENT_USER"); 
                     break; 
 
                   case (DWORD)HKEY_CLASSES_ROOT: 
                     strcpy (NameLBSelect, "HKEY_CLASSES_ROOT"); 
                     break; 
                   } 
                 SetDlgItemText (hDlg, IDE_NAME, NameLBSelect); 
                 hKeyRoot = 0; 
                 EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot); 
                 } 
               else 
                 { 
                   // In the final case, we can just trim the last key 
                   // name off the path, and re-enumerate the level. 
                 *putNullAt = '\0'; 
                 putNullAt = _mbsrchr (RegPath, '\\'); 
 
                 if (putNullAt) 
                   { 
                   strcpy (NameLBSelect, putNullAt+1); 
                   *putNullAt = '\0'; 
                   } 
                 else 
                   { 
                   strcpy (NameLBSelect, RegPath); 
                   *RegPath = '\0'; 
                   } 
                 SetDlgItemText (hDlg, IDE_NAME, NameLBSelect); 
                 EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot); 
                 } 
               return (0); 
 
            default: 
               return (0); 
 
          } 
 
     } 
    return (FALSE); 
 
  } 
 
 
 
 
/************************************************************************\ 
* 
*  FUNCTION: EnumerateLevel(); 
* 
*  PURPOSE: To get a valid key handle (either to determine if the one sent 
*           to the function was one of the pre-defined, or to open a key 
*           specified by the path), and to pass that key handle along 
*           to QueryKey(). 
* 
*           To enumerate the children of a key, you must have 
*           an open handle to it.  The four top keys of the 
*           Registry are predefined and open for use: 
*           HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, 
*           and HKEY_CLASSES_ROOT.  These 4 can be used for 
*           RegEnumKey as is; but to RegEnumKey on any of the 
*           children of these you must first have an open key 
*           handle to the child. 
* 
*           If hKeyRoot != 0, assume you are lower than the 
*           first level of the Registry and the user is trying 
*           to enumerate one of the children.  First calculate 
*           the name of the child, and then use RegOpenKey to 
*           get an open handle. 
* 
*           If hKeyRoot == 0, assume you are at the top level 
*           of the Registry, and set the hKey to be enumerated 
*           to be one of the 4 predefined values, the specific 
*           one indicated by the ListBox selection. 
* 
\************************************************************************/ 
  VOID EnumerateLevel (HWND hDlg, LPTSTR NameLBSelect, 
                       LPTSTR RegPath, HKEY *hKeyRoot) 
  { 
 
    HKEY hKey; 
    DWORD  retCode; 
    CHAR   Buf[100]; 
    CHAR   lpBuffer[128]; 
 
 
    if (*hKeyRoot) 
      { 
                                       // If RegPath is not NULL, then 
                                       // you have to add a backslash to the 
                                       // path name before appending the next 
                                       // level child name. 
      if (strcmp (RegPath, "") != 0) 
        strcat (RegPath, "\\"); 
 
                                       // Add the next level child name. 
      strcat (RegPath, NameLBSelect); 
 
                                       // Use RegOpenKeyEx() with the new 
                                       // Registry path to get an open handle 
                                       // to the child key you want to 
                                       // enumerate. 
      retCode = RegOpenKeyEx (*hKeyRoot, 
                              RegPath, 
                              0, 
                              KEY_ENUMERATE_SUB_KEYS | 
                              KEY_EXECUTE | 
                              KEY_QUERY_VALUE, 
                              &hKey); 
 
      if (retCode != ERROR_SUCCESS) 
        { 
        if (retCode == ERROR_ACCESS_DENIED) { 
          LoadString(hInst, IDS_CANTOPENKEY, lpBuffer, sizeof(lpBuffer)); 
          wsprintf (Buf, lpBuffer); 
        } 
        else { 
          LoadString(hInst, IDS_OPENKEYERR, lpBuffer, sizeof(lpBuffer)); 
          wsprintf (Buf, lpBuffer, retCode, __LINE__); 
        } 
        MessageBox (hDlg, Buf, "", MB_OK); 
        PostMessage (hDlg, WM_COMMAND, IDB_BACK, 0); 
        return; 
        } 
 
      } 
    else 
      { 
                                       // Set the *hKeyRoot handle based 
                                       // on the text taken from the ListBox. 
 
      if (strcmp (NameLBSelect, "HKEY_CLASSES_ROOT") == 0) 
        *hKeyRoot = HKEY_CLASSES_ROOT; 
 
      if (strcmp (NameLBSelect, "HKEY_USERS") == 0) 
        *hKeyRoot = HKEY_USERS; 
 
      if (strcmp (NameLBSelect, "HKEY_LOCAL_MACHINE") == 0) 
        *hKeyRoot = HKEY_LOCAL_MACHINE; 
 
      if (strcmp (NameLBSelect, "HKEY_CURRENT_USER") == 0) 
        *hKeyRoot = HKEY_CURRENT_USER; 
 
      hKey = *hKeyRoot;     // hKey is used in RegEnumKey(). 
 
      }//end if/else *hKeyRoot 
 
      QueryKey (hDlg, hKey); 
 
 
      RegCloseKey (hKey);   // Close the key handle. 
 
//      rect.top = 0; rect.left = 5; rect.right = 1200; rect.bottom = 25; 
//      hDC = GetDC (hDlg); 
//      FillRect (hDC, &rect, GetStockObject(WHITE_BRUSH)); 
//      TextOut (hDC, 5, 5, RegPath, strlen(RegPath)); 
//      ReleaseDC (hDlg, hDC); 
      SetDlgItemText (hDlg, IDE_TEXTOUT, RegPath); 
 
 
  } 
 
 
/************************************************************************\ 
* 
*  FUNCTION: QueryKey(); 
* 
*  PURPOSE:  To display the key's children (subkeys) and the names of 
*            the Values associated with it.  This function uses RegEnumKey, 
*            RegEnumValue, and RegQueryInfoKey. 
* 
\************************************************************************/ 
VOID QueryKey (HWND hDlg, HANDLE hKey) 
  { 
  CHAR     KeyName[MAX_PATH]; 
  CHAR     ClassName[MAX_PATH] = ""; // Buffer for class name. 
  DWORD    dwcClassLen = MAX_PATH;   // Length of class string. 
  DWORD    dwcSubKeys;               // Number of sub keys. 
  DWORD    dwcMaxSubKey;             // Longest sub key size. 
  DWORD    dwcMaxClass;              // Longest class string. 
  DWORD    dwcValues;                // Number of values for this key. 
  DWORD    dwcMaxValueName;          // Longest Value name. 
  DWORD    dwcMaxValueData;          // Longest Value data. 
  DWORD    dwcSecDesc;               // Security descriptor. 
  FILETIME ftLastWriteTime;          // Last write time. 
 
  DWORD i; 
  DWORD retCode; 
 
  DWORD j; 
  DWORD retValue; 
  CHAR  ValueName[MAX_VALUE_NAME]; 
  DWORD dwcValueName = MAX_VALUE_NAME; 
  CHAR  Buf[80]; 
  CHAR  lpBuffer[80]; 
 
 
  // Get Class name, Value count. 
 
  RegQueryInfoKey (hKey,              // Key handle. 
                   ClassName,         // Buffer for class name. 
                   &dwcClassLen,      // Length of class string. 
                   NULL,              // Reserved. 
                   &dwcSubKeys,       // Number of sub keys. 
                   &dwcMaxSubKey,     // Longest sub key size. 
                   &dwcMaxClass,      // Longest class string. 
                   &dwcValues,        // Number of values for this key. 
                   &dwcMaxValueName,  // Longest Value name. 
                   &dwcMaxValueData,  // Longest Value data. 
                   &dwcSecDesc,       // Security descriptor. 
                   &ftLastWriteTime); // Last write time. 
 
  SetDlgItemText (hDlg, IDE_CLASS, ClassName); 
  SetDlgItemInt  (hDlg, IDE_CVALUES, dwcValues, FALSE); 
 
  SendMessage (GetDlgItem (hDlg, IDL_LISTBOX), 
               LB_ADDSTRING, 0, (LONG)".."); 
 
                                       // Loop until RegEnumKey fails, get 
                                       // the name of each child and enter 
                                       // it into the box. 
 
  // Enumerate the Child Keys. 
 
  SetCursor (LoadCursor (NULL, IDC_WAIT)); 
  for (i=0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++) 
    { 
    retCode = RegEnumKey (hKey, i, 
                          KeyName, MAX_PATH); 
 
    if (retCode == (DWORD)ERROR_SUCCESS) 
      SendMessage (GetDlgItem(hDlg, IDL_LISTBOX), 
                   LB_ADDSTRING, 0, (LONG)KeyName); 
    } 
  SetCursor (LoadCursor (NULL, IDC_ARROW)); 
 
 
  // Enumerate the Key Values 
        SetCursor (LoadCursor (NULL, IDC_WAIT)); 
 
        if (dwcValues) 
          for (j = 0, retValue = ERROR_SUCCESS; j < dwcValues; j++) 
            { 
            dwcValueName = MAX_VALUE_NAME; 
            ValueName[0] = '\0'; 
            retValue = RegEnumValue (hKey, j, ValueName, 
                                     &dwcValueName, 
                                     NULL, 
                                     NULL,               //&dwType, 
                                     NULL,               //&bData, 
                                     NULL);              //&bcData); 
            if (retValue != (DWORD)ERROR_SUCCESS && 
                retValue != ERROR_INSUFFICIENT_BUFFER) 
              { 
              LoadString(hInst, IDS_REGERR, lpBuffer, sizeof(lpBuffer)); 
              wsprintf (Buf, lpBuffer, __LINE__, j, retValue, dwcValueName); 
              LoadString(hInst, IDS_DEBUG, lpBuffer, sizeof(lpBuffer)); 
              MessageBox (hDlg, Buf, lpBuffer, MB_OK); 
              } 
 
            Buf[0] = '\0'; 
            if (!strlen(ValueName)) { 
              LoadString(hInst, IDS_NONAME, lpBuffer, sizeof(lpBuffer)); 
              strcpy (ValueName, lpBuffer); 
            } 
            wsprintf (Buf, "%d) %s ", j, ValueName); 
            SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2), 
                           LB_ADDSTRING, 0, (LONG)Buf); 
 
            }// end for(;;) 
 
        SetCursor (LoadCursor (NULL, IDC_ARROW)); 
 
  } 
 
 
 
/************************************************************************\ 
* 
*  FUNCTION: DisplayKeyData(); 
* 
*  PURPOSE:  To display the keys values and value types to the Value edit 
*            field.  This function is called when the right hand listbox 
*            is double clicked.  The functionality is much like that found 
*            in the function PrintTree, please see it for more details. 
* 
\************************************************************************/ 
 
 
VOID DisplayKeyData (HWND hDlg, CHAR *RegPath, HANDLE hKeyRoot) 
  { 
  HKEY   hKey; 
  DWORD  dwLBIndex; 
  CHAR   Buf[LINE_LEN]; 
  CHAR   ValueName[MAX_VALUE_NAME]; 
  DWORD  cbValueName = MAX_VALUE_NAME; 
  DWORD  dwType; 
  DWORD  retCode; 
  CHAR   lpBuffer[128]; 
  CHAR   lpBuffer2[128]; 
 
  CHAR   ClassName[MAX_PATH]; 
  DWORD  dwcClassLen = MAX_PATH; 
  DWORD  dwcSubKeys; 
  DWORD  dwcMaxSubKey; 
  DWORD  dwcMaxClass; 
  DWORD  dwcValues; 
  DWORD  dwcMaxValueName; 
  DWORD  dwcMaxValueData; 
  DWORD  dwcSecDesc; 
  FILETIME  ftLastWriteTime; 
 
 
  BYTE   *bData; 
  DWORD  cbData; 
 
  CHAR   *outBuf; 
  DWORD  i; 
  DWORD  cStrLen; 
 
  CHAR   *BinaryStrBuf; 
  CHAR   ByteBuf[4]; 
 
  CHAR   *ptr; 
 
  // OPEN THE KEY. 
 
                                // LBIndex should == value index. 
  dwLBIndex = SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2), 
                           LB_GETCURSEL, 0, 0); 
 
  retCode = RegOpenKeyEx (hKeyRoot,    // Key handle at root level. 
                          RegPath,     // Path name of child key. 
                          0,           // Reserved. 
                          KEY_EXECUTE, // Requesting read access. 
                          &hKey);      // Address of key to be returned. 
 
  if (retCode) 
    { 
    LoadString(hInst, IDS_REGOPENERR, lpBuffer, sizeof(lpBuffer)); 
    wsprintf (Buf, lpBuffer, retCode); 
    MessageBox (hDlg, Buf, "DisplayKeyData()", MB_OK); 
    return; 
    } 
 
// ADD A QUERY AND ALLOCATE A BUFFER FOR BDATA. 
 
  retCode = 
  RegQueryInfoKey (hKey,              // Key handle. 
                   ClassName,         // Buffer for class name. 
                   &dwcClassLen,      // Length of class string. 
                   NULL,              // Reserved. 
                   &dwcSubKeys,       // Number of sub keys. 
                   &dwcMaxSubKey,     // Longest sub key size. 
                   &dwcMaxClass,      // Longest class string. 
                   &dwcValues,        // Number of values for this key. 
                   &dwcMaxValueName,  // Longest Value name. 
                   &dwcMaxValueData,  // Longest Value data. 
                   &dwcSecDesc,       // Security descriptor. 
                   &ftLastWriteTime); // Last write time. 
 
   if (retCode) 
    { 
    LoadString(hInst, IDS_REGQUERYERR, lpBuffer, sizeof(lpBuffer)); 
    wsprintf (Buf, lpBuffer, retCode, __LINE__); 
    MessageBox (hDlg, Buf, "", MB_OK); 
    } 
 
   bData = HeapAlloc (hHeap, 0, dwcMaxValueData); 
   cbData = dwcMaxValueData; 
 
 
  // ENUMERATE THE KEY. 
 
  retCode = RegEnumValue (hKey,        // Key handle returned from RegOpenKeyEx. 
                          dwLBIndex,   // Value index, taken from listbox. 
                          ValueName,   // Name of value. 
                          &cbValueName,// Size of value name. 
                          NULL,        // Reserved, dword = NULL. 
                          &dwType,     // Type of data. 
                          bData,       // Data buffer. 
                          &cbData);    // Size of data buffer. 
 
  if (retCode != ERROR_SUCCESS) 
    { 
 
    if (dwType < REG_FULL_RESOURCE_DESCRIPTOR) 
      { 
      LoadString(hInst, IDS_REGENUMERR, lpBuffer, sizeof(lpBuffer)); 
      wsprintf (Buf, lpBuffer, retCode, cbData, __LINE__); 
      MessageBox (hDlg, Buf, "", MB_OK); 
      } 
    } 
 
 
  switch (dwType) 
    { 
//    REG_NONE                    ( 0 )   // No value type 
//    REG_SZ                      ( 1 )   // Unicode nul terminated string 
//    REG_EXPAND_SZ               ( 2 )   // Unicode nul terminated string 
                                            // (with environment variable references) 
//    REG_BINARY                  ( 3 )   // Free form binary 
//    REG_DWORD                   ( 4 )   // 32-bit number 
//    REG_DWORD_LITTLE_ENDIAN     ( 4 )   // 32-bit number (same as REG_DWORD) 
//    REG_DWORD_BIG_ENDIAN        ( 5 )   // 32-bit number 
//    REG_LINK                    ( 6 )   // Symbolic Link (unicode) 
//    REG_MULTI_SZ                ( 7 )   // Multiple Unicode strings 
//    REG_RESOURCE_LIST           ( 8 )   // Resource list in the resource map 
//    REG_FULL_RESOURCE_DESCRIPTOR ( 9 )  // Resource list in the hardware description 
 
    case REG_NONE: 
      LoadString(hInst, IDS_REGNONE, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      break; 
 
    case REG_SZ: 
      LoadString(hInst, IDS_REGSZ, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
 
      outBuf = HeapAlloc (hHeap, 0, cbData + 2); 
      *outBuf = '\0'; 
 
      strcat (outBuf, "\""); 
      strcat (outBuf, bData); 
      strcat (outBuf, "\""); 
 
      SetDlgItemText (hDlg, IDE_VALUE2, outBuf); 
      HeapFree (hHeap, 0, outBuf); 
      break; 
 
    case REG_EXPAND_SZ: 
      LoadString(hInst, IDS_REGEXPSZ, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      outBuf = HeapAlloc (hHeap, 0, cbData + 2); 
      *outBuf = '\0'; 
 
      strcat (outBuf, "\""); 
      strcat (outBuf, bData); 
      strcat (outBuf, "\""); 
 
      SetDlgItemText (hDlg, IDE_VALUE2, outBuf); 
      HeapFree (hHeap, 0, outBuf); 
      break; 
 
    case REG_BINARY: 
      LoadString(hInst, IDS_REGBIN, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      SetCursor (LoadCursor (NULL, IDC_WAIT)); 
 
      BinaryStrBuf = HeapAlloc (hHeap, 0, (3 * cbData) + 1); 
      if (BinaryStrBuf) 
        { 
        *BinaryStrBuf = '\0'; 
        *ByteBuf = '\0'; 
        for (i = 0; i < cbData; i++) 
          { 
          sprintf (ByteBuf, "%02x ", (BYTE)bData[i]); 
          strcat (BinaryStrBuf, ByteBuf); 
          } 
        SetDlgItemText (hDlg, IDE_VALUE2, BinaryStrBuf); 
        } 
      else 
        { 
        LoadString(hInst, IDS_MALLOCERR, lpBuffer, sizeof(lpBuffer)); 
        LoadString(hInst, IDS_DBGTITLE, lpBuffer2, sizeof(lpBuffer2)); 
        MessageBox (hDlg, lpBuffer, lpBuffer2, MB_OK); 
        } 
      SetDlgItemText (hDlg, IDL_LISTBOX2, BinaryStrBuf); 
      HeapFree (hHeap, 0, BinaryStrBuf); 
      SetCursor (LoadCursor (NULL, IDC_ARROW)); 
 
      break; 
 
    case REG_DWORD: 
      LoadString(hInst, IDS_REGDWORD, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      SetDlgItemInt (hDlg, IDE_VALUE2, *(UINT *)bData, FALSE); 
      break; 
 
    case REG_DWORD_BIG_ENDIAN: 
      LoadString(hInst, IDS_REGBIGEND, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      SetDlgItemInt (hDlg, IDE_VALUE2, *(UINT *)bData, TRUE); 
      break; 
 
    case REG_LINK: 
      LoadString(hInst, IDS_REGLINK, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      SetDlgItemText (hDlg, IDE_VALUE2, bData); 
      break; 
 
    case REG_MULTI_SZ: 
      LoadString(hInst, IDS_REGMULTI, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      SetCursor (LoadCursor (NULL, IDC_WAIT)); 
                                       // Count the NULLs in the buffer to 
                                       // find out how many strings there are. 
 
      for (i=0, cStrLen=4; i < cbData; i++) 
        if (!bData[i]) 
          cStrLen+=4;                  // Add room for two quotes and two 
                                       // spaced per string. 
 
      outBuf = HeapAlloc (hHeap, 0, cbData + cStrLen); 
 
      ptr = bData;                     // Set ptr to beginning of buffer. 
      *outBuf = '\0';                  // Initialize output string. 
 
      strcat (outBuf, "{ ");           // Do first bracket. 
      while (*ptr)                     // Loop til you hit 2 NULLs in a row. 
        { 
         strcat (outBuf, "\"");        // Put quotes around each string. 
strcat (outBuf, ptr); 
         strcat (outBuf, "\"  "); 
         ptr += strlen(ptr)+1; 
        } 
      strcat (outBuf, "}");            // Add final bracket. 
      SetDlgItemText (hDlg, IDE_VALUE2, outBuf); 
 
      SetCursor (LoadCursor (NULL, IDC_ARROW)); 
      HeapFree (hHeap, 0, outBuf);                 // free output string. 
      break; 
 
 
    case REG_RESOURCE_LIST:            // CM_RESOURCE_LIST is complex.  Print it 
                                       // as a free formed binary data for now. 
 
      LoadString(hInst, IDS_REGRESLST, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
 
      BinaryStrBuf = HeapAlloc (hHeap, 0, (3 * cbData) + 1); 
      if (BinaryStrBuf) 
        { 
        *BinaryStrBuf = '\0'; 
        *ByteBuf = '\0'; 
        for (i = 0; i < cbData; i++) 
          { 
          sprintf (ByteBuf, "%02x ", (BYTE)bData[i]); 
          strcat (BinaryStrBuf, ByteBuf); 
          } 
        SetDlgItemText (hDlg, IDE_VALUE2, BinaryStrBuf); 
        } 
      else 
        { 
        LoadString(hInst, IDS_MALLOCERR, lpBuffer, sizeof(lpBuffer)); 
        LoadString(hInst, IDS_DBGTITLE, lpBuffer2, sizeof(lpBuffer2)); 
        MessageBox (hDlg, lpBuffer, lpBuffer2, MB_OK); 
        } 
      SetDlgItemText (hDlg, IDL_LISTBOX2, BinaryStrBuf); 
      HeapFree (hHeap, 0, BinaryStrBuf); 
 
      break; 
 
    case REG_FULL_RESOURCE_DESCRIPTOR: 
      LoadString(hInst, IDS_REGFULLDESC, lpBuffer, sizeof(lpBuffer)); 
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer); 
      break; 
 
 
 
    default: 
      LoadString(hInst, IDS_REGUNDEFINE, lpBuffer, sizeof(lpBuffer)); 
      wsprintf (Buf, lpBuffer, dwType); 
      SetDlgItemText (hDlg, IDE_VALUE1, Buf); 
      break; 
 
    } // end switch 
 
 
    HeapFree (hHeap, 0, bData); 
  }