/*****************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

#include "stdafx.h"
#include "RegHelpers.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


BOOL FW_EXPORTED_API RegKeyExists(HKEY const Key, CString const & strSubKey)
{
   BOOL fExists = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      HKEY hTemp = NULL;

      fExists = RegOpenKey(Key, strSubKey, &hTemp, KEY_READ);

      if (hTemp != NULL) RegCloseKey(hTemp);
   }
   catch (CAffirmException ae)
   {
   }

   return fExists;
}

BOOL FW_EXPORTED_API RegCreateKey(HKEY const Key, CString const & strSubKey, HKEY * const phKey,
                                  DWORD const dwOptions /* = REG_OPTION_NON_VOLATILE */,
                                  REGSAM const samDesired /* = KEY_ALL_ACCESS */,
                                  DWORD * const pdwDisposition /* = NULL */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(phKey != NULL);

      CString strRemaining = strSubKey;
      CString strPartial = TEXT("");
      int iSlash = 0;

      while ((iSlash = strRemaining.Find(TEXT("\\"))) != -1)
      {
         strPartial += strRemaining.Left(iSlash);
         strRemaining = strRemaining.Right(strRemaining.GetLength() - (iSlash + 1));

         if (!RegKeyExists(Key, strPartial))
         {
            HKEY hTemp = NULL;

            // TRACE("RegCreateKey(): Creating key %s\n", strPartial);
            AffirmThrow(RegCreateKeyEx(Key, (LPCTSTR)strPartial, 0, NULL,
                                       dwOptions, samDesired, NULL, &hTemp, NULL) == ERROR_SUCCESS);
            AffirmThrow(hTemp != NULL);
            RegCloseKey(hTemp);
         }

         strPartial += TEXT("\\");
      }

      strPartial += strRemaining;

      // TRACE("RegCreateKey(): Creating key %s\n", strPartial);
      AffirmThrow(RegCreateKeyEx(Key, (LPCTSTR)strPartial, 0, NULL,
                                 dwOptions, samDesired, NULL, phKey, pdwDisposition) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegOpenKey(HKEY const Key, CString const & strSubKey, HKEY * const phKey,
                                REGSAM const samDesired /* = KEY_ALL_ACCESS */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(phKey != NULL);

      fSuccess = (RegOpenKeyEx(Key, (LPCTSTR)strSubKey, 0, samDesired, phKey) == ERROR_SUCCESS);
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// NOTE: This function calls itself recursively to delete any subkeys!
BOOL FW_EXPORTED_API RegDelKey(HKEY const Key, CString const & strSubKey)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      HKEY hSubKey = NULL;

      if (RegOpenKey(Key, strSubKey, &hSubKey))
      {
         AffirmThrow(hSubKey != NULL);

         try
         {
            CStringArray astrSubSubKeys;
            AffirmThrow(RegEnumSubKeyNames(hSubKey, astrSubSubKeys));

            // Call our selves recursively
            for (int iSubSubKey = 0; iSubSubKey < astrSubSubKeys.GetSize(); iSubSubKey++)
            {
               AffirmThrow(RegDelKey(hSubKey, astrSubSubKeys[iSubSubKey]));
            }
         }
         catch (CAffirmException ae)
         {
         }

         RegCloseKey(hSubKey);

         // Now delete the key we were told to
         AffirmThrow(RegDeleteKey(Key, (LPCTSTR)strSubKey) == ERROR_SUCCESS);
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegDelValue(HKEY const Key, CString const & ValueName)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      if (RegValueExists(Key, ValueName)) AffirmThrow(RegDeleteValue(Key, (LPCTSTR)ValueName) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Registry read/write utility functions

// Determine value type of a given registry tag
DWORD FW_EXPORTED_API RegValueType(HKEY const Key, CString const & ValueName)
{
	DWORD Type = REG_NONE;

   try
   {
      AffirmThrow(Key != NULL);

	   if (RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, &Type, NULL, NULL) != ERROR_SUCCESS)
      {
         // Should is this be an assert?
         // No!  Really, the only way this is gonna happen is if the value doesn't exist.
         // Therefore, return REG_NONE, so that we can use RegValueExists().
         // TRACE("RegValueType(): RegQueryValueEx() failed!  Defaulting to REG_NONE.\n");
         // Reset this because we don't know if RegQueryValueEx() modified it or not
		   Type = REG_NONE;
      }
   }
   catch (CAffirmException ae)
   {
   }

	return Type;
}

// Determine size of a given registry tag
DWORD FW_EXPORTED_API RegValueSize(HKEY const Key, CString const & ValueName)
{
   DWORD cb = 0;

   try
   {
      AffirmThrow(Key != NULL);

	   if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, NULL, &cb) == ERROR_SUCCESS);
      }
   }
   catch (CAffirmException ae)
   {
   }

	return cb;
}

BOOL FW_EXPORTED_API RegEnumSubKeyNames(HKEY const Key, CStringArray & astrSubKeys)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      astrSubKeys.RemoveAll();

      int iSubKey = 0;
      TCHAR szSubKey[MAX_PATH];
      DWORD cchSubKey = CharCount(szSubKey);

      while (RegEnumKeyEx(Key, iSubKey, szSubKey, &cchSubKey,
                          NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
      {
         astrSubKeys.Add(szSubKey);
         cchSubKey = CharCount(szSubKey);
         iSubKey++;
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegEnumValueNames(HKEY const Key, CStringArray & astrValues, DWORD const dwOnlyEnumType /* = REG_NONE */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      astrValues.RemoveAll();

      int iValue = 0;
      TCHAR szValue[MAX_PATH];
      DWORD cchValue = CharCount(szValue);
      DWORD dwType = 0;

      while (RegEnumValue(Key, iValue, szValue, &cchValue,
                          NULL, &dwType, NULL, NULL) == ERROR_SUCCESS)
      {
         if ((dwOnlyEnumType == REG_NONE) || (dwOnlyEnumType == dwType)) astrValues.Add(szValue);

         cchValue = CharCount(szValue);
         iValue++;
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegEnumStrValues(HKEY const Key,  CMapRegStrValues & mapValues)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      mapValues.RemoveAll();

      CStringArray astrValueNames;
      AffirmThrow(RegEnumValueNames(Key, astrValueNames, REG_SZ));

      for (int i = 0; i < astrValueNames.GetSize(); i++)
      {
         CString strValue;
         AffirmThrow(RegReadStrValue(Key, astrValueNames[i], strValue));
         mapValues.SetAt(astrValueNames[i], strValue);
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegEnumDWordValues(HKEY const Key,  CMapRegDWordValues & mapValues)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      mapValues.RemoveAll();

      CStringArray astrValueNames;
      AffirmThrow(RegEnumValueNames(Key, astrValueNames, REG_DWORD));

      for (int i = 0; i < astrValueNames.GetSize(); i++)
      {
         DWORD dwValue = 0;
         AffirmThrow(RegReadDWordValue(Key, astrValueNames[i], &dwValue));
         mapValues.SetAt(astrValueNames[i], dwValue);
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Read a REG_SZ registry tag
BOOL FW_EXPORTED_API RegReadStrValue(HKEY const Key, CString const & ValueName, CString & Value, CString const & Default /* = TEXT("") */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_SZ);

         DWORD cb = RegValueSize(Key, ValueName);
         AffirmThrow(cb > 0);

			BYTE * pb = new BYTE[cb];

         try
         {
            AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, pb, &cb) == ERROR_SUCCESS);

				Value = (TCHAR *)pb;
            fSuccess = TRUE;
         }
         catch (CAffirmException ae)
         {
         }

			delete[] pb;
      }
      else
      {
		   Value = Default;
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Write a REG_SZ value to the registry
BOOL FW_EXPORTED_API RegWriteStrValue(HKEY const Key, CString const & ValueName, CString const & Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(RegWriteSzValue(Key, ValueName, (LPCTSTR)Value));

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadSzValue(HKEY const Key, CString const & ValueName, TCHAR * const Value, DWORD const cchValue, TCHAR const * const Default /* = TEXT("") */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(cchValue > 0);
      AffirmThrow(AfxIsValidAddress(Value, (cchValue * sizeof(TCHAR))));

      CString str;
      CString strDefault = Default;

      AffirmThrow(RegReadStrValue(Key, ValueName, str, strDefault));

      lstrcpyn(Value, (LPCTSTR)str, cchValue);
      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteSzValue(HKEY const Key, CString const & ValueName, TCHAR const * const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(AfxIsValidString(Value));

      DWORD cb = ((lstrlen(Value) + 1) * sizeof(TCHAR));
      AffirmThrow(RegSetValueEx(Key, (LPCTSTR)ValueName, 0, REG_SZ, (BYTE *)Value, cb) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Read a REG_EXPAND_SZ registry tag
BOOL FW_EXPORTED_API RegReadExpandStrValue(HKEY const Key, CString const & ValueName, CString & Value, CString const & Default /* = TEXT("") */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_EXPAND_SZ);

         DWORD cb = RegValueSize(Key, ValueName);
         AffirmThrow(cb > 0);

			BYTE * pb = new BYTE[cb];

         try
         {
            AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, pb, &cb) == ERROR_SUCCESS);

				Value = (TCHAR *)pb;
            fSuccess = TRUE;
         }
         catch (CAffirmException ae)
         {
         }

			delete[] pb;
      }
      else
      {
		   Value = Default;
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Write a REG_EXPAND_SZ value to the registry
BOOL FW_EXPORTED_API RegWriteExpandStrValue(HKEY const Key, CString const & ValueName, CString const & Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(RegWriteExpandSzValue(Key, ValueName, (LPCTSTR)Value));

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadExpandSzValue(HKEY const Key, CString const & ValueName, TCHAR * const Value, DWORD const cchValue, TCHAR const * const Default /* = TEXT("") */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(cchValue > 0);
      AffirmThrow(AfxIsValidAddress(Value, (cchValue * sizeof(TCHAR))));

      CString str;
      CString strDefault = Default;

      AffirmThrow(RegReadExpandStrValue(Key, ValueName, str, strDefault));

      lstrcpyn(Value, (LPCTSTR)str, cchValue);
      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteExpandSzValue(HKEY const Key, CString const & ValueName, TCHAR const * const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(AfxIsValidString(Value));

      DWORD cb = ((lstrlen(Value) + 1) * sizeof(TCHAR));
      AffirmThrow(RegSetValueEx(Key, (LPCTSTR)ValueName, 0, REG_EXPAND_SZ, (BYTE *)Value, cb) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadIntValue(HKEY const Key, CString const & ValueName, int * const Value, int const Default /* = 0 */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(AfxIsValidAddress(Value, sizeof(*Value)));

      DWORD dwValue = 0;
      AffirmThrow(RegReadDWordValue(Key, ValueName, &dwValue, (DWORD)Default));
      *Value = (int)dwValue;

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Read a REG_DWORD registry tag.
BOOL FW_EXPORTED_API RegReadDWordValue(HKEY const Key, CString const & ValueName, DWORD * const Value, DWORD const Default /* = 0 */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(AfxIsValidAddress(Value, sizeof(*Value)));

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_DWORD);

         DWORD cb = sizeof(*Value);
         AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, (BYTE *)Value, &cb) == ERROR_SUCCESS);

         fSuccess = TRUE;
      }
      else
      {
         *Value = Default;
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteIntValue(HKEY const Key, CString const & ValueName, int const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      AffirmThrow(RegWriteDWordValue(Key, ValueName, (DWORD)Value));

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Write a REG_DWORD value to the registry
BOOL FW_EXPORTED_API RegWriteDWordValue(HKEY const Key, CString const & ValueName, DWORD const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      AffirmThrow(RegSetValueEx(Key, (LPCTSTR)ValueName, 0, REG_DWORD, (BYTE *)&Value, sizeof(Value)) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadBoolValue(HKEY const Key, CString const & ValueName, BOOL * const Value, BOOL const Default /* = FALSE */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_DWORD);

         // NOTE:
         //    Boolean values in the registry are stored as REG_DWORD!
         DWORD dwBool = 0;
         AffirmThrow(RegReadDWordValue(Key, ValueName, &dwBool));
         *Value = (dwBool == 0) ? FALSE : TRUE;

         fSuccess = TRUE;
      }
      else
      {
         *Value = Default;
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteBoolValue(HKEY const Key, CString const & ValueName, BOOL const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      // NOTE:
      //    Boolean values in the registry are stored as REG_DWORD!
      AffirmThrow(RegWriteDWordValue(Key, ValueName, Value ? 1 : 0));

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Read a REG_BINARY value to the registry
BOOL FW_EXPORTED_API RegReadBinaryValue(HKEY const Key, CString const & ValueName, BYTE * const Value, DWORD & cbValue, BYTE const Default /* = 0 */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(cbValue > 0);
      AffirmThrow(AfxIsValidAddress(Value, cbValue));

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_BINARY);

         AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, Value, &cbValue) == ERROR_SUCCESS);

         fSuccess = TRUE;
      }
      else
      {
         memset(Value, Default, cbValue);
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Write a REG_BINARY value to the registry
BOOL FW_EXPORTED_API RegWriteBinaryValue(HKEY const Key, CString const & ValueName, BYTE const * const Value, DWORD const cbValue)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(cbValue > 0);
      AffirmThrow(AfxIsValidAddress(Value, cbValue));

      AffirmThrow(RegSetValueEx(Key, (LPCTSTR)ValueName, 0, REG_BINARY, Value, cbValue) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Read a REG_MULTI_SZ value to the registry
BOOL FW_EXPORTED_API RegReadMultiStrValue(HKEY const Key, CString const & ValueName, CStringArray & Value, CStringArray const * const Default /* = NULL */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

      Value.RemoveAll();

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_MULTI_SZ);

         DWORD cb = RegValueSize(Key, ValueName);
         AffirmThrow(cb > 0);

			BYTE * pb = new BYTE[cb];

         try
         {
            AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, pb, &cb) == ERROR_SUCCESS);

				TCHAR const * psz = (TCHAR *)pb;
				while(*psz != TEXT('\0'))
				{
					Value.Add(psz);
					psz += (lstrlen(psz) + 1);
				}

            fSuccess = TRUE;
         }
         catch (CAffirmException ae)
         {
         }

			delete[] pb;
      }
      else
      {
			if (Default != NULL) Value.Copy(*Default);
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

// Write a REG_MULTI_SZ value to the registry
BOOL FW_EXPORTED_API RegWriteMultiStrValue(HKEY const Key, CString const & ValueName, CStringArray const & Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);

	   INT_PTR cStr = Value.GetSize();
	   INT_PTR cch = 0;

	   for (INT_PTR i = 0; i < cStr; i++)
	   {
		   cch += (Value[i].GetLength() + 1);
	   }
	   cch += 1;      // double null termination

      TCHAR * pszMulti = new TCHAR[cch];
	   memset(pszMulti, '\0', (cch * sizeof(TCHAR)));

      try
      {
         TCHAR * psz = pszMulti;
	      for (i = 0; i < cStr; i++)
	      {
            lstrcpy(psz, (LPCTSTR)Value[i]);
		      psz += (lstrlen(psz) + 1);
	      }

         AffirmThrow(RegWriteMultiSzValue(Key, ValueName, pszMulti));

         fSuccess = TRUE;
      }
      catch (CAffirmException ae)
      {
      }

	   delete[] pszMulti;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadMultiSzValue(HKEY const Key, CString const & ValueName, TCHAR * const Value, DWORD const cbValue, TCHAR const * const Default /* = TEXT("") */)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);
      AffirmThrow(cbValue > 0);
      AffirmThrow(AfxIsValidAddress(Value, cbValue));

      memset(Value, '\0', cbValue);

      if (RegValueExists(Key, ValueName))
      {
         AffirmThrow(RegValueType(Key, ValueName) == REG_MULTI_SZ);

         DWORD cb = cbValue;
         AffirmThrow(RegQueryValueEx(Key, (LPCTSTR)ValueName, NULL, NULL, (BYTE *)Value, &cb) == ERROR_SUCCESS);

         fSuccess = TRUE;
      }
      else
      {
         DWORD cbDefault = 0;
         TCHAR const * psz = Default;

		   while(*psz != TEXT('\0'))
         {
            AffirmThrow(AfxIsValidString(psz));

            cbDefault += ((lstrlen(psz) + 1) * sizeof(TCHAR));    // convert to bytes
		      psz += (lstrlen(psz) + 1);
         }
         cbDefault += sizeof(TCHAR);      // double null termination

         memcpy(Value, Default, min(cbValue, cbDefault));
         fSuccess = TRUE;
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteMultiSzValue(HKEY const Key, CString const & ValueName, TCHAR const * const Value)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(Value != NULL);

      DWORD cb = 0;

		TCHAR const * psz = Value;
		while(*psz != TEXT('\0'))
      {
         AffirmThrow(AfxIsValidString(psz));

         cb += ((lstrlen(psz) + 1) * sizeof(TCHAR));    // convert to bytes
		   psz += (lstrlen(psz) + 1);
      }
      cb += sizeof(TCHAR);    // double null termination

      AffirmThrow(RegSetValueEx(Key, (LPCTSTR)ValueName, 0, REG_MULTI_SZ, (BYTE *)Value, cb) == ERROR_SUCCESS);

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegReadMultipleValues(HKEY const Key, REGVALUE * const pRegVals, DWORD const cRegVals)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(pRegVals != NULL);
      AffirmThrow(cRegVals > 0);
      AffirmThrow(AfxIsValidAddress(pRegVals, sizeof(*pRegVals) * cRegVals));

      for (DWORD i = 0; i < cRegVals; i++)
      {
         REGVALUE * pRegVal = &pRegVals[i];

         switch (pRegVal->dwType)
         {
            case REG_SZ:
            {
               AffirmThrow(RegReadSzValue(Key, pRegVal->pszName, (TCHAR *)pRegVal->pvData, pRegVal->cbData, (TCHAR *)pRegVal->pvDefault));
               break;
            }

            case REG_DWORD:
            {
               AffirmThrow(RegReadDWordValue(Key, pRegVal->pszName, (DWORD *)pRegVal->pvData, (DWORD)pRegVal->pvDefault));
               break;
            }

            case REG_BINARY:
            {
               AffirmThrow(RegReadBinaryValue(Key, pRegVal->pszName, (BYTE *)pRegVal->pvData, pRegVal->cbData, (BYTE)pRegVal->pvDefault));
               break;
            }

            case REG_MULTI_SZ:
            {
               AffirmThrow(RegReadMultiSzValue(Key, pRegVal->pszName, (TCHAR *)pRegVal->pvData, pRegVal->cbData, (TCHAR *)pRegVal->pvDefault));
               break;
            }

            default: AffirmThrow(FALSE); break;
         }
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FW_EXPORTED_API RegWriteMultipleValues(HKEY const Key, REGVALUE const * const pRegVals, DWORD const cRegVals)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(Key != NULL);
      AffirmThrow(pRegVals != NULL);
      AffirmThrow(cRegVals > 0);
      AffirmThrow(AfxIsValidAddress(pRegVals, sizeof(*pRegVals) * cRegVals));

      for (DWORD i = 0; i < cRegVals; i++)
      {
         REGVALUE const * pRegVal = &pRegVals[i];

         switch (pRegVal->dwType)
         {
            case REG_SZ:
            {
               AffirmThrow(RegWriteSzValue(Key, pRegVal->pszName, (TCHAR *)pRegVal->pvData));
               break;
            }

            case REG_DWORD:
            {
               AffirmThrow(RegWriteDWordValue(Key, pRegVal->pszName, (DWORD)pRegVal->pvData));
               break;
            }

            case REG_BINARY:
            {
               AffirmThrow(RegWriteBinaryValue(Key, pRegVal->pszName, (BYTE *)pRegVal->pvData, pRegVal->cbData));
               break;
            }

            case REG_MULTI_SZ:
            {
               AffirmThrow(RegWriteMultiSzValue(Key, pRegVal->pszName, (TCHAR *)pRegVal->pvData));
               break;
            }

            default: AffirmThrow(FALSE); break;
         }
      }

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}
