"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/script_registry.cpp" (8 May 2021, 21725 Bytes) of package /windows/misc/AutoHotkey_L-1.1.33.09.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "script_registry.cpp" see the Fossies "Dox" file reference documentation.

    1 ///////////////////////////////////////////////////////////////////////////////
    2 //
    3 // AutoIt
    4 //
    5 // Copyright (C)1999-2007:
    6 //      - Jonathan Bennett <jon@hiddensoft.com>
    7 //      - Others listed at http://www.autoitscript.com/autoit3/docs/credits.htm
    8 //      - Chris Mallett (support@autohotkey.com): adaptation of this file's
    9 //        functions to interface with AutoHotkey.
   10 //
   11 // This program is free software; you can redistribute it and/or modify
   12 // it under the terms of the GNU General Public License as published by
   13 // the Free Software Foundation; either version 2 of the License, or
   14 // (at your option) any later version.
   15 //
   16 ///////////////////////////////////////////////////////////////////////////////
   17 //
   18 // script_registry.cpp
   19 //
   20 // Contains registry handling routines.  Part of script.cpp
   21 //
   22 ///////////////////////////////////////////////////////////////////////////////
   23 
   24 
   25 // Includes
   26 #include "stdafx.h" // pre-compiled headers
   27 #include "script.h"
   28 #include "util.h" // for strlcpy()
   29 #include "globaldata.h"
   30 
   31 
   32 ResultType Line::IniRead(LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey, LPTSTR aDefault)
   33 {
   34     if (!aDefault || !*aDefault)
   35         aDefault = _T("ERROR");  // This mirrors what AutoIt2 does for its default value.
   36     TCHAR   szFileTemp[T_MAX_PATH];
   37     TCHAR   *szFilePart, *cp;
   38     TCHAR   szBuffer[65535];                    // Max ini file size is 65535 under 95
   39     *szBuffer = '\0';
   40     TCHAR   szEmpty[] = _T("");
   41     // Get the fullpathname (ini functions need a full path):
   42     GetFullPathName(aFilespec, _countof(szFileTemp), szFileTemp, &szFilePart);
   43     if (*aKey)
   44     {
   45         // An access violation can occur if the following conditions are met:
   46         //  1) aFilespec specifies a Unicode file.
   47         //  2) aSection is a read-only string, either empty or containing only spaces.
   48         //
   49         // Debugging at the assembly level indicates that in this specific situation,
   50         // it tries to write a zero at the end of aSection (which is already null-
   51         // terminated).
   52         //
   53         // The second condition can ordinarily only be met if Section is omitted,
   54         // since in all other cases aSection is writable.  Although Section was a
   55         // required parameter prior to revision 57, empty or blank section names
   56         // are actually valid.  Simply passing an empty writable buffer appears
   57         // to work around the problem effectively:
   58         if (!*aSection)
   59             aSection = szEmpty;
   60         GetPrivateProfileString(aSection, aKey, aDefault, szBuffer, _countof(szBuffer), szFileTemp);
   61     }
   62     else if (*aSection
   63         ? GetPrivateProfileSection(aSection, szBuffer, _countof(szBuffer), szFileTemp)
   64         : GetPrivateProfileSectionNames(szBuffer, _countof(szBuffer), szFileTemp))
   65     {
   66         // Convert null-terminated array of null-terminated strings to newline-delimited list.
   67         for (cp = szBuffer; ; ++cp)
   68             if (!*cp)
   69             {
   70                 if (!cp[1])
   71                     break;
   72                 *cp = '\n';
   73             }
   74     }
   75     // The above function is supposed to set szBuffer to be aDefault if it can't find the
   76     // file, section, or key.  In other words, it always changes the contents of szBuffer.
   77     return OUTPUT_VAR->Assign(szBuffer); // Avoid using the length the API reported because it might be inaccurate if the data contains any binary zeroes, or if the data is double-terminated, etc.
   78     // Note: ErrorLevel is not changed by this command since the aDefault value is returned
   79     // whenever there's an error.
   80 }
   81 
   82 #ifdef UNICODE
   83 static BOOL IniEncodingFix(LPWSTR aFilespec, LPWSTR aSection)
   84 {
   85     BOOL result = TRUE;
   86     if (!DoesFilePatternExist(aFilespec))
   87     {
   88         HANDLE hFile;
   89         DWORD dwWritten;
   90 
   91         // Create a Unicode file. (UTF-16LE BOM)
   92         hFile = CreateFile(aFilespec, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
   93         if (hFile != INVALID_HANDLE_VALUE)
   94         {
   95             DWORD cc = (DWORD)wcslen(aSection);
   96             DWORD cb = (cc + 1) * sizeof(WCHAR);
   97             
   98             aSection[cc] = ']'; // Temporarily replace the null-terminator.
   99 
  100             // Write a UTF-16LE BOM to identify this as a Unicode file.
  101             // Write [%aSection%] to prevent WritePrivateProfileString from inserting an empty line (for consistency and style).
  102             if (   !WriteFile(hFile, L"\xFEFF[", 4, &dwWritten, NULL) || dwWritten != 4
  103                 || !WriteFile(hFile, aSection, cb, &dwWritten, NULL) || dwWritten != cb   )
  104                 result = FALSE;
  105 
  106             if (!CloseHandle(hFile))
  107                 result = FALSE;
  108 
  109             aSection[cc] = '\0'; // Re-terminate.
  110         }
  111     }
  112     return result;
  113 }
  114 #endif
  115 
  116 ResultType Line::IniWrite(LPTSTR aValue, LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey)
  117 {
  118     TCHAR   szFileTemp[T_MAX_PATH];
  119     TCHAR   *szFilePart;
  120     BOOL    result;
  121     // Get the fullpathname (INI functions need a full path) 
  122     GetFullPathName(aFilespec, _countof(szFileTemp), szFileTemp, &szFilePart);
  123 #ifdef UNICODE
  124     // WritePrivateProfileStringW() always creates INIs using the system codepage.
  125     // IniEncodingFix() checks if the file exists and if it doesn't then it creates
  126     // an empty file with a UTF-16LE BOM.
  127     result = IniEncodingFix(szFileTemp, aSection);
  128     if(result){
  129 #endif
  130         if (*aKey)
  131         {
  132             result = WritePrivateProfileString(aSection, aKey, aValue, szFileTemp);  // Returns zero on failure.
  133         }
  134         else
  135         {
  136             size_t value_len = ArgLength(1);
  137             TCHAR c, *cp, *szBuffer = talloca(value_len + 2); // +2 for double null-terminator.
  138             // Convert newline-delimited list to null-terminated array of null-terminated strings.
  139             for (cp = szBuffer; *aValue; ++cp, ++aValue)
  140             {
  141                 c = *aValue;
  142                 *cp = (c == '\n') ? '\0' : c;
  143             }
  144             *cp = '\0', cp[1] = '\0'; // Double null-terminator.
  145             result = WritePrivateProfileSection(aSection, szBuffer, szFileTemp);
  146         }
  147         WritePrivateProfileString(NULL, NULL, NULL, szFileTemp);    // Flush
  148 #ifdef UNICODE
  149     }
  150 #endif
  151     return SetErrorLevelOrThrowBool(!result);
  152 }
  153 
  154 
  155 
  156 ResultType Line::IniDelete(LPTSTR aFilespec, LPTSTR aSection, LPTSTR aKey)
  157 // Note that aKey can be NULL, in which case the entire section will be deleted.
  158 {
  159     TCHAR   szFileTemp[T_MAX_PATH];
  160     TCHAR   *szFilePart;
  161     // Get the fullpathname (ini functions need a full path) 
  162     GetFullPathName(aFilespec, _countof(szFileTemp), szFileTemp, &szFilePart);
  163     BOOL result = WritePrivateProfileString(aSection, aKey, NULL, szFileTemp);  // Returns zero on failure.
  164     WritePrivateProfileString(NULL, NULL, NULL, szFileTemp);    // Flush
  165     return SetErrorLevelOrThrowBool(!result);
  166 }
  167 
  168 
  169 
  170 ResultType Line::RegRead(HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName)
  171 {
  172     Var &output_var = *OUTPUT_VAR;
  173     output_var.Assign(); // Init.  Tell it not to free the memory by not calling without parameters.
  174 
  175     HKEY    hRegKey;
  176     DWORD   dwRes, dwBuf, dwType;
  177     LONG    result;
  178 
  179     if (!aRootKey)
  180     {
  181         result = ERROR_INVALID_PARAMETER; // Indicate the error.
  182         goto finish;
  183     }
  184 
  185     // Open the registry key
  186     result = RegOpenKeyEx(aRootKey, aRegSubkey, 0, KEY_READ | g->RegView, &hRegKey);
  187     if (result != ERROR_SUCCESS)
  188         goto finish;
  189 
  190     // Read the value and determine the type.  If aValueName is the empty string, the key's default value is used.
  191     result = RegQueryValueEx(hRegKey, aValueName, NULL, &dwType, NULL, NULL);
  192     if (result != ERROR_SUCCESS)
  193     {
  194         RegCloseKey(hRegKey);
  195         goto finish;
  196     }
  197 
  198     LPTSTR contents, cp;
  199 
  200     // The way we read is different depending on the type of the key
  201     switch (dwType)
  202     {
  203         case REG_DWORD:
  204             dwRes = sizeof(dwBuf);
  205             result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, (LPBYTE)&dwBuf, &dwRes);
  206             if (result == ERROR_SUCCESS)
  207                 output_var.Assign((DWORD)dwBuf);
  208             RegCloseKey(hRegKey);
  209             break;
  210 
  211         // Note: The contents of any of these types can be >64K on NT/2k/XP+ (though that is probably rare):
  212         case REG_SZ:
  213         case REG_EXPAND_SZ:
  214         case REG_MULTI_SZ:
  215         {
  216             dwRes = 0; // Retained for backward compatibility because values >64K may cause it to fail on Win95 (unverified, and MSDN implies its value should be ignored for the following call).
  217             // MSDN: If lpData is NULL, and lpcbData is non-NULL, the function returns ERROR_SUCCESS and stores
  218             // the size of the data, in bytes, in the variable pointed to by lpcbData.
  219             result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, NULL, &dwRes); // Find how large the value is.
  220             if (result != ERROR_SUCCESS || !dwRes) // Can't find size (realistically might never happen), or size is zero.
  221             {
  222                 RegCloseKey(hRegKey);
  223                 // Above realistically probably never happens; if it does and result != ERROR_SUCCESS,
  224                 // setting ErrorLevel to indicate the error seems more useful than maintaining backward-
  225                 // compatibility by faking success.
  226                 break;
  227             }
  228             DWORD dwCharLen = dwRes / sizeof(TCHAR);
  229             // Set up the variable to receive the contents, enlarging it if necessary:
  230             // Since dwRes includes the space for the zero terminator (if the MSDN docs
  231             // are accurate), this will enlarge it to be 1 byte larger than we need,
  232             // which leaves room for the final newline character to be inserted after
  233             // the last item.  But add 2 to the requested capacity in case the data isn't
  234             // terminated in the registry, which allows double-NULL to be put in for REG_MULTI_SZ later.
  235             if (output_var.AssignString(NULL, (VarSizeType)(dwCharLen + 2)) != OK)
  236             {
  237                 RegCloseKey(hRegKey);
  238                 return FAIL; // FAIL is only returned when the error is a critical one such as this one.
  239             }
  240 
  241             contents = output_var.Contents(); // This target buf should now be large enough for the result.
  242 
  243             result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, (LPBYTE)contents, &dwRes);
  244             RegCloseKey(hRegKey);
  245 
  246             if (result != ERROR_SUCCESS || !dwRes) // Relies on short-circuit boolean order.
  247             {
  248                 *contents = '\0'; // MSDN says the contents of the buffer is undefined after the call in some cases, so reset it.
  249                 // Above realistically probably never happens; if it does and result != ERROR_SUCCESS,
  250                 // setting ErrorLevel to indicate the error seems more useful than maintaining backward-
  251                 // compatibility by faking success.
  252             }
  253             else
  254             {
  255                 dwCharLen = dwRes / sizeof(TCHAR);
  256                 // See ReadRegString() for more comments about the following:
  257                 // The MSDN docs state that we should ensure that the buffer is NULL-terminated ourselves:
  258                 // "If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, then lpcbData will also
  259                 // include the size of the terminating null character or characters ... If the data has the
  260                 // REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with the
  261                 // proper null-terminating characters. Applications should ensure that the string is properly
  262                 // terminated before using it, otherwise, the application may fail by overwriting a buffer."
  263                 //
  264                 // Double-terminate so that the loop can find out the true end of the buffer.
  265                 // The MSDN docs cited above are a little unclear.  The most likely interpretation is that
  266                 // dwRes contains the true size retrieved.  For example, if dwRes is 1, the first char
  267                 // in the buffer is either a NULL or an actual non-NULL character that was originally
  268                 // stored in the registry incorrectly (i.e. without a terminator).  In either case, do
  269                 // not change the first character, just leave it as is and add a NULL at the 2nd and
  270                 // 3rd character positions to ensure that it is double terminated in every case:
  271                 contents[dwCharLen] = contents[dwCharLen + 1] = '\0';
  272 
  273                 if (dwType == REG_MULTI_SZ) // Convert NULL-delimiters into newline delimiters.
  274                 {
  275                     for (cp = contents;; ++cp)
  276                     {
  277                         if (!*cp)
  278                         {
  279                             // Unlike AutoIt3, it seems best to have a newline character after the
  280                             // last item in the list also.  It usually makes parsing easier:
  281                             *cp = '\n'; // Convert to \n for later storage in the user's variable.
  282                             if (!*(cp + 1)) // Buffer is double terminated, so this is safe.
  283                                 // Double null terminator marks the end of the used portion of the buffer.
  284                                 break;
  285                         }
  286                     }
  287                     // else the buffer is empty (see above notes for explanation).  So don't put any newlines
  288                     // into it at all, since each newline should correspond to an item in the buffer.
  289                 }
  290             }
  291             output_var.SetCharLength((VarSizeType)_tcslen(contents)); // Due to conservative buffer sizes above, length is probably too large by 3. So update to reflect the true length.
  292             if (!output_var.Close()) // Must be called after Assign(NULL, ...) or when Contents() has been altered because it updates the variable's attributes and properly handles VAR_CLIPBOARD.
  293                 return FAIL;
  294             break;
  295         }
  296         case REG_BINARY:
  297         {
  298             // See case REG_SZ for comments.
  299             dwRes = 0;
  300             result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, NULL, &dwRes); // Find how large the value is.
  301             if (result != ERROR_SUCCESS || !dwRes) // Can't find size (realistically might never happen), or size is zero.
  302             {
  303                 RegCloseKey(hRegKey);
  304                 break;
  305             }
  306 
  307             // Set up the variable to receive the contents, enlarging it if necessary.
  308             // AutoIt3: Each byte will turned into 2 digits, plus a final null:
  309             if (output_var.AssignString(NULL, (VarSizeType)(dwRes * 2)) != OK)
  310             {
  311                 RegCloseKey(hRegKey);
  312                 return FAIL;
  313             }
  314             contents = output_var.Contents();
  315             *contents = '\0';
  316             
  317             // Read the binary data into the variable, placed so that the last byte of
  318             // binary data will be overwritten as the hexadecimal conversion completes.
  319             LPBYTE pRegBuffer = (LPBYTE)(contents + dwRes * 2) - dwRes;
  320             result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, pRegBuffer, &dwRes);
  321             RegCloseKey(hRegKey);
  322 
  323             if (result != ERROR_SUCCESS)
  324                 break;
  325 
  326             int j = 0;
  327             DWORD i, n; // i and n must be unsigned to work
  328             TCHAR szHexData[] = _T("0123456789ABCDEF");  // Access to local vars might be faster than static ones.
  329             for (i = 0; i < dwRes; ++i)
  330             {
  331                 n = pRegBuffer[i];              // Get the value and convert to 2 digit hex
  332                 contents[j + 1] = szHexData[n % 16];
  333                 n /= 16;
  334                 contents[j] = szHexData[n % 16];
  335                 j += 2;
  336             }
  337             contents[j] = '\0'; // Terminate
  338             if (!output_var.Close()) // Length() was already set by the earlier call to Assign().
  339                 return FAIL;
  340             break;
  341         }
  342         default:
  343             RegCloseKey(hRegKey);
  344             result = ERROR_UNSUPPORTED_TYPE; // Indicate the error.
  345             break;
  346     }
  347 
  348 finish:
  349     return SetErrorsOrThrow(result != ERROR_SUCCESS, result);
  350 } // RegRead()
  351 
  352 
  353 
  354 ResultType Line::RegWrite(DWORD aValueType, HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName, LPTSTR aValue)
  355 // If aValueName is the empty string, the key's default value is used.
  356 {
  357     HKEY    hRegKey;
  358     DWORD   dwRes, dwBuf;
  359 
  360     TCHAR *buf;
  361     #define SET_REG_BUF buf = aValue;
  362     
  363     LONG result;
  364 
  365     if (!aRootKey || aValueType == REG_NONE || aValueType == REG_SUBKEY) // Can't write to these.
  366     {
  367         result = ERROR_INVALID_PARAMETER; // Indicate the error.
  368         goto finish;
  369     }
  370 
  371     // Open/Create the registry key
  372     // The following works even on root keys (i.e. blank subkey), although values can't be created/written to
  373     // HKCU's root level, perhaps because it's an alias for a subkey inside HKEY_USERS.  Even when RegOpenKeyEx()
  374     // is used on HKCU (which is probably redundant since it's a pre-opened key?), the API can't create values
  375     // there even though RegEdit can.
  376     result = RegCreateKeyEx(aRootKey, aRegSubkey, 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_WRITE | g->RegView, NULL, &hRegKey, &dwRes);
  377     if (result != ERROR_SUCCESS)
  378         goto finish;
  379 
  380     // Write the registry differently depending on type of variable we are writing
  381     switch (aValueType)
  382     {
  383     case REG_SZ:
  384         SET_REG_BUF
  385         result = RegSetValueEx(hRegKey, aValueName, 0, REG_SZ, (CONST BYTE *)buf, (DWORD)(_tcslen(buf)+1) * sizeof(TCHAR));
  386         break;
  387 
  388     case REG_EXPAND_SZ:
  389         SET_REG_BUF
  390         result = RegSetValueEx(hRegKey, aValueName, 0, REG_EXPAND_SZ, (CONST BYTE *)buf, (DWORD)(_tcslen(buf)+1) * sizeof(TCHAR));
  391         break;
  392     
  393     case REG_MULTI_SZ:
  394     {
  395         size_t length = _tcslen(aValue);
  396         // Allocate some temporary memory because aValue might not be a writable string,
  397         // and we would need to write to it to temporarily change the newline delimiters into
  398         // zero-delimiters.  Even if we were to require callers to give us a modifiable string,
  399         // its capacity may be 1 byte too small to handle the double termination that's needed
  400         // (i.e. if the last item in the list happens to not end in a newline):
  401         buf = tmalloc(length + 2);
  402         if (!buf)
  403         {
  404             result = ERROR_OUTOFMEMORY;
  405             break;
  406         }
  407         tcslcpy(buf, aValue, length + 1);
  408         // Double-terminate:
  409         buf[length + 1] = '\0';
  410 
  411         // Remove any final newline the user may have provided since we don't want the length
  412         // to include it when calling RegSetValueEx() -- it would be too large by 1:
  413         if (length > 0 && buf[length - 1] == '\n')
  414             buf[--length] = '\0';
  415 
  416         // Replace the script's delimiter char with the zero-delimiter needed by RegSetValueEx():
  417         for (LPTSTR cp = buf; *cp; ++cp)
  418             if (*cp == '\n')
  419                 *cp = '\0';
  420 
  421         result = RegSetValueEx(hRegKey, aValueName, 0, REG_MULTI_SZ, (CONST BYTE *)buf
  422                                 , (DWORD)(length ? length + 2 : 0) * sizeof(TCHAR));
  423         free(buf);
  424         break;
  425     }
  426 
  427     case REG_DWORD:
  428         if (*aValue)
  429             dwBuf = ATOU(aValue);  // Changed to ATOU() for v1.0.24 so that hex values are supported.
  430         else // Default to 0 when blank.
  431             dwBuf = 0;
  432         result = RegSetValueEx(hRegKey, aValueName, 0, REG_DWORD, (CONST BYTE *)&dwBuf, sizeof(dwBuf));
  433         break;
  434 
  435     case REG_BINARY:
  436     {
  437         int nLen = (int)_tcslen(aValue);
  438 
  439         // String length must be a multiple of 2 
  440         if (nLen % 2)
  441         {
  442             result = ERROR_INVALID_PARAMETER;
  443             break;
  444         }
  445 
  446         int nBytes = nLen / 2;
  447         LPBYTE pRegBuffer = (LPBYTE) malloc(nBytes);
  448         if (!pRegBuffer)
  449         {
  450             result = ERROR_OUTOFMEMORY;
  451             break;
  452         }
  453 
  454         // Really crappy hex conversion
  455         int j = 0, i = 0, nVal, nMult;
  456         while (i < nLen && j < nBytes)
  457         {
  458             nVal = 0;
  459             for (nMult = 16; nMult >= 0; nMult = nMult - 15)
  460             {
  461                 if (aValue[i] >= '0' && aValue[i] <= '9')
  462                     nVal += (aValue[i] - '0') * nMult;
  463                 else if (aValue[i] >= 'A' && aValue[i] <= 'F')
  464                     nVal += (((aValue[i] - 'A'))+10) * nMult;
  465                 else if (aValue[i] >= 'a' && aValue[i] <= 'f')
  466                     nVal += (((aValue[i] - 'a'))+10) * nMult;
  467                 else
  468                 {
  469                     free(pRegBuffer);
  470                     RegCloseKey(hRegKey);
  471                     result = ERROR_INVALID_PARAMETER;
  472                     goto finish;
  473                 }
  474                 ++i;
  475             }
  476             pRegBuffer[j++] = (char)nVal;
  477         }
  478 
  479         result = RegSetValueEx(hRegKey, aValueName, 0, REG_BINARY, pRegBuffer, (DWORD)j);
  480         free(pRegBuffer);
  481         break;
  482     }
  483 
  484     default:
  485         result = ERROR_INVALID_PARAMETER; // Anything other than ERROR_SUCCESS.
  486         break;
  487     } // switch()
  488 
  489     RegCloseKey(hRegKey);
  490     // Additionally, fall through to below:
  491 
  492 finish:
  493     return SetErrorsOrThrow(result != ERROR_SUCCESS, result);
  494 } // RegWrite()
  495 
  496 
  497 
  498 LONG Line::RegRemoveSubkeys(HKEY hRegKey)
  499 {
  500     // Removes all subkeys to the given key.  Will not touch the given key.
  501     TCHAR Name[256];
  502     DWORD dwNameSize;
  503     FILETIME ftLastWrite;
  504     HKEY hSubKey;
  505     LONG result;
  506 
  507     for (;;) 
  508     { // infinite loop 
  509         dwNameSize = _countof(Name)-1;
  510         if (RegEnumKeyEx(hRegKey, 0, Name, &dwNameSize, NULL, NULL, NULL, &ftLastWrite) == ERROR_NO_MORE_ITEMS)
  511             return ERROR_SUCCESS;
  512         result = RegOpenKeyEx(hRegKey, Name, 0, KEY_READ | g->RegView, &hSubKey);
  513         if (result != ERROR_SUCCESS)
  514             break;
  515         result = RegRemoveSubkeys(hSubKey);
  516         RegCloseKey(hSubKey);
  517         if (result != ERROR_SUCCESS)
  518             break;
  519         result = RegDeleteKey(hRegKey, Name);
  520         if (result != ERROR_SUCCESS)
  521             break;
  522     }
  523     return result;
  524 }
  525 
  526 
  527 
  528 ResultType Line::RegDelete(HKEY aRootKey, LPTSTR aRegSubkey, LPTSTR aValueName)
  529 {
  530     LONG result;
  531 
  532     // Fix for v1.0.48: Don't remove the entire key if it's a root key!  According to MSDN,
  533     // the root key would be opened by RegOpenKeyEx() further below whenever aRegSubkey is NULL
  534     // or an empty string. aValueName is also checked to preserve the ability to delete a value
  535     // that exists directly under a root key.
  536     if (   !aRootKey
  537         || (!aRegSubkey || !*aRegSubkey) && !aValueName   ) // See comment above.
  538     {
  539         result = ERROR_INVALID_PARAMETER;
  540         goto finish;
  541     }
  542 
  543     // Open the key we want
  544     HKEY hRegKey;
  545     result = RegOpenKeyEx(aRootKey, aRegSubkey, 0, KEY_READ | KEY_WRITE | g->RegView, &hRegKey);
  546     if (result != ERROR_SUCCESS)
  547         goto finish;
  548 
  549     if (!aValueName) // Caller's signal to delete the entire subkey.
  550     {
  551         // Remove the entire Key
  552         result = RegRemoveSubkeys(hRegKey); // Delete any subitems within the key.
  553         RegCloseKey(hRegKey); // Close parent key.  Not sure if this needs to be done only after the above.
  554         if (result == ERROR_SUCCESS)
  555         {
  556             typedef LONG (WINAPI * PFN_RegDeleteKeyEx)(HKEY hKey , LPCTSTR lpSubKey , REGSAM samDesired , DWORD Reserved );
  557             static PFN_RegDeleteKeyEx _RegDeleteKeyEx = (PFN_RegDeleteKeyEx)GetProcAddress(GetModuleHandle(_T("advapi32")), "RegDeleteKeyEx" WINAPI_SUFFIX);
  558             if (g->RegView && _RegDeleteKeyEx)
  559                 result = _RegDeleteKeyEx(aRootKey, aRegSubkey, g->RegView, 0);
  560             else
  561                 result = RegDeleteKey(aRootKey, aRegSubkey);
  562         }
  563     }
  564     else
  565     {
  566         // Remove the value.
  567         result = RegDeleteValue(hRegKey, aValueName);
  568         RegCloseKey(hRegKey);
  569     }
  570 
  571 finish:
  572     return SetErrorsOrThrow(result != ERROR_SUCCESS, result);
  573 } // RegDelete()
  574 
  575 
  576 
  577 struct RegRootKeyType
  578 {
  579     LPTSTR short_name;
  580     LPTSTR long_name;
  581     HKEY key;
  582 };
  583 
  584 static RegRootKeyType sRegRootKeyTypes[] =
  585 {
  586     {_T("HKLM"), _T("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE},
  587     {_T("HKCR"), _T("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT},
  588     {_T("HKCC"), _T("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG},
  589     {_T("HKCU"), _T("HKEY_CURRENT_USER"), HKEY_CURRENT_USER},
  590     {_T("HKU"), _T("HKEY_USERS"), HKEY_USERS}
  591 };
  592 
  593 HKEY Line::RegConvertRootKeyType(LPTSTR aName)
  594 {
  595     for (int i = 0; i < _countof(sRegRootKeyTypes); ++i)
  596         if (!_tcsicmp(aName, sRegRootKeyTypes[i].short_name)
  597             || !_tcsicmp(aName, sRegRootKeyTypes[i].long_name))
  598             return sRegRootKeyTypes[i].key;
  599     return NULL;
  600 }
  601 
  602 LPTSTR Line::RegConvertRootKeyType(HKEY aKey)
  603 {
  604     for (int i = 0; i < _countof(sRegRootKeyTypes); ++i)
  605         if (aKey == sRegRootKeyTypes[i].key)
  606             return sRegRootKeyTypes[i].long_name;
  607     // These are either unused or so rarely used that they aren't supported:
  608     // HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_TEXT, HKEY_PERFORMANCE_NLSTEXT
  609     return _T("");
  610 }