Click here to Skip to main content
15,879,474 members
Articles / Desktop Programming / MFC

Include/Exclude List Boxes

Rate me:
Please Sign up or sign in to vote.
4.50/5 (5 votes)
25 Feb 2000CPOL 122.8K   2.2K   47  
How to package lots of standard functionality into a CListBox derived class.
///////////////////////////////////////////////////////////////////////////////
//
// File           : $Workfile:   RegistryKey.cpp  $
// Version        : $Revision:   1.5  $
// Function       : 
//
// Author         : $Author:   len  $
// Date           : $Date:   Oct 25 1998 11:24:38  $
//
// Notes          : 
//
// Modifications  :
//
// $Log:   G:/Documents/JetByte/Source/JetByteTools/Win32Tools/PVCS/RegistryKey.cpv  $
// 
//    Rev 1.5   Oct 25 1998 11:24:38   len
// Tidy up after running Lint...
// 
//    Rev 1.4   Oct 21 1998 20:08:52   len
// Bug fixes as reported by Steve Greenland - steve.greenland@aspentech.com
// 
//    Rev 1.3   Aug 27 1998 07:46:56   len
// Reference counted HKEY and loads of other things.
// 
//    Rev 1.2   Jun 06 1998 07:42:34   Len
// Made delete key reliable. Added DeleteKeyAndSubKeys.
// Tidied up.
// 
//    Rev 1.1   May 25 1998 11:02:40   Len
// Bug fixes.
// 
//    Rev 1.0   May 18 1998 07:49:24   Len
// Initial revision.
// 
///////////////////////////////////////////////////////////////////////////////
//
// Copyright 1998 JetByte Limited.
//
// JetByte Limited grants you ("Licensee") a non-exclusive, royalty free, 
// licence to use, modify and redistribute this software in source and binary 
// code form, provided that i) this copyright notice and licence appear on all 
// copies of the software; and ii) Licensee does not utilize the software in a 
// manner which is disparaging to JetByte Limited.
//
// This software is provided "AS IS," without a warranty of any kind. ALL
// EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING 
// ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. JETBYTE LIMITED AND ITS LICENSORS 
// SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
// USING, MODIFYING OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO 
// EVENT WILL JETBYTE LIMITED BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, 
// OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 
// DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING 
// OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF JETBYTE LIMITED 
// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
//
// This software is not designed or intended for use in on-line control of
// aircraft, air traffic, aircraft navigation or aircraft communications; or in
// the design, construction, operation or maintenance of any nuclear
// facility. Licensee represents and warrants that it will not use or
// redistribute the Software for such purposes.
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Include files
///////////////////////////////////////////////////////////////////////////////

#include "RegistryKey.hpp"

#include <tchar.h>
#include <stdio.h>
#include <malloc.h>

///////////////////////////////////////////////////////////////////////////////
// Namespace: JetByteTools
///////////////////////////////////////////////////////////////////////////////

namespace JetByteTools {

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Construction and destruction
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::CRegistryKey(
   HKEY hKey) 
   :  m_pKey(NewCountedKey(hKey, true))
{
}

CRegistryKey::CRegistryKey(
   LPTSTR pRemoteMachine,
   HKEY hKey) 
   :  m_pKey(0)
{
   HKEY theKey = hKey;

   LONG result = RegConnectRegistry(pRemoteMachine, hKey, &theKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CRegistryKey() - RegConnectRegistry"), result);
   }

   m_pKey = NewCountedKey(theKey, true);
}


CRegistryKey::CRegistryKey(
   HKEY hKey, 
   LPCTSTR pSubKey, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPTSTR pRemoteMachine /* = 0 */)
   :  m_pKey(0)
{
   HKEY theKey = hKey;

   // if we're passed a remote machine name...
   // do a connect registry first

   if (pRemoteMachine)
   {
      LONG result = RegConnectRegistry(pRemoteMachine, hKey, &theKey);

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::CRegistryKey() - RegConnectRegistry"), result);
      }
   }

   HKEY newKey;

   LONG result = RegOpenKeyEx(theKey, pSubKey, 0, samDesired, &newKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CRegistryKey(HKEY hKey ...)"), result);
   }

   m_pKey = NewCountedKey(newKey, true);
}

CRegistryKey::CRegistryKey(const CRegistryKey &rhs)
   :  m_pKey(rhs.m_pKey->AddRef())
{

}

CRegistryKey::~CRegistryKey()
{
   m_pKey = m_pKey->Release();
}

///////////////////////////////////////////////////////////////////////////////
// Assignment
///////////////////////////////////////////////////////////////////////////////

CRegistryKey &CRegistryKey::operator=(const CRegistryKey &rhs)
{
   if (this != &rhs)
   {
      CCountedRegKey *pNewKey = rhs.m_pKey->AddRef();
      
      m_pKey->Release();

      m_pKey = pNewKey;
   }

   return *this;
}

// NOTE: We OWN This hKey from this point on...

CRegistryKey &CRegistryKey::operator=(HKEY hKey)
{
   CCountedRegKey *pNewKey = NewCountedKey(hKey, true);
   
   m_pKey->Release();

   m_pKey = pNewKey;

   return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Static helper function...
///////////////////////////////////////////////////////////////////////////////

CCountedRegKey *CRegistryKey::NewCountedKey(
   HKEY hKey, 
   bool bCloseKeyOnFailure /* = false */)
{
   CCountedRegKey *pCCountedRegKey = 0;

   try
   {
      pCCountedRegKey = new CCountedRegKey(hKey);
   }
   catch (...) //xalloc &e)
   {
      pCCountedRegKey = 0;
   }

   if (!pCCountedRegKey)
   {
      if (bCloseKeyOnFailure)
      {
         RegCloseKey(hKey);
      }

      throw Exception(_T("CRegistryKey::NewCCountedRegKey()"), 
         ERROR_NOT_ENOUGH_MEMORY);
   }

   return pCCountedRegKey;
}

///////////////////////////////////////////////////////////////////////////////
// Registry API wrappers...
///////////////////////////////////////////////////////////////////////////////

CRegistryKey CRegistryKey::OpenKey(
   LPCTSTR pSubKey, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */) const
{
   return CRegistryKey(*this, pSubKey, samDesired);
}

void CRegistryKey::DeleteKey(
   LPCTSTR pKeyName) const 
{
   // Behaviour of RegDeleteKey differs on Win95 and NT
   // On 95 RegDeleteKey will delete keys with subkeys
   // on NT it wont.
   // To add some consistency to the world DeleteKey 
   // will always fail to delete a key with sub keys and
   // DeleteKeyAndSubKeys will always work...


   // scope the key...
   {
      CRegistryKey deadKey = OpenKey(pKeyName); 

      if (deadKey.BeginSubkeyIteration() != deadKey.EndSubkeyIteration())
      {
         throw Exception(_T("CRegistryKey::DeleteKey()"), ERROR_ACCESS_DENIED);
      }
   }

   LONG result = RegDeleteKey(m_pKey->GetCounted(), pKeyName);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::DeleteKey()"), result);
   }
}

void CRegistryKey::DeleteKeyAndSubkeys(
   LPCTSTR pKeyName) const 
{
   // Win95 doesn't need this as it deletes subkeys by default
   // NT wont delete a key with subkeys...

   // Scope the deadKey...
   {
      CRegistryKey deadKey = OpenKey(pKeyName);

		// This wont work, we are frigging with the sub keys of the key we're
		// iterating...

//      for (SubkeyIterator it = deadKey.BeginSubkeyIteration();
//         it != deadKey.EndSubkeyIteration();
//        ++it)
//      {
//         deadKey.DeleteKeyAndSubkeys(it.GetName());
//      }

      for (SubkeyIterator it = deadKey.BeginSubkeyIteration();
         it != deadKey.EndSubkeyIteration();
         it = deadKey.BeginSubkeyIteration())
      {
         deadKey.DeleteKeyAndSubkeys(it.GetName());
      }


   }

   DeleteKey(pKeyName);
}

CRegistryKey CRegistryKey::CreateKey(
   LPCTSTR pSubKey, 
   LPTSTR pClass /* = _T("") */, 
   DWORD dwOptions /* = REG_OPTION_NON_VOLATILE */, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   DWORD disposition;

   CRegistryKey key = CreateOrOpenKey(
                        pSubKey,
                        &disposition,
                        pClass,
                        dwOptions,
                        samDesired,
                        pSecurityAttributes);
   
   if (disposition != REG_CREATED_NEW_KEY)
   {
      RegCloseKey(key);

      throw Exception(_T("CRegistryKey::CreateKey()"), ERROR_ALREADY_EXISTS);
   }


   return key;
}

CRegistryKey CRegistryKey::CreateOrOpenKey(
   LPCTSTR pSubKey, 
   DWORD *pDisposition /* = NULL */,
   LPTSTR pClass /* = _T("") */, 
   DWORD dwOptions /* = REG_OPTION_NON_VOLATILE */, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   HKEY hKey;

   DWORD disposition;

   if (!pDisposition)
   {
      pDisposition = &disposition;
   }

   LONG result = RegCreateKeyEx(m_pKey->GetCounted(), pSubKey, 0, pClass, dwOptions,
                           samDesired, pSecurityAttributes, &hKey,
                           pDisposition);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CreateOrOpenKey()"), result);
   }

   return CRegistryKey(hKey);
}

bool CRegistryKey::HasSubkey(
   LPCTSTR pSubKey,    
   REGSAM samDesired /* = KEY_ALL_ACCESS */) const
{
   bool hasKey = false;

   HKEY hKey;

   LONG result = RegOpenKeyEx(m_pKey->GetCounted(), pSubKey, 0, samDesired, &hKey);

   if (ERROR_SUCCESS == result)
   {
      hasKey = true;

      RegCloseKey(hKey);
   }
   else // should check for errors that mean no we dont have it, or we 
      // have it but not with the right access and throw if a real error
   {
      hasKey = false;
   }

   return hasKey;
}

CRegistryKey CRegistryKey::ConnectRegistry(LPTSTR pMachineName) const
{
   HKEY hKey;

   LONG result = RegConnectRegistry(pMachineName, m_pKey->GetCounted(), &hKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::ConnectRegistry()"), result);
   }

   return CRegistryKey(hKey);
}

void CRegistryKey::FlushKey() const
{
   LONG result = RegFlushKey(m_pKey->GetCounted());

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::FlushKey()"), result);
   }
}

CRegistryKey::operator HKEY() const 
{ 
   return m_pKey->GetCounted(); 
}

void CRegistryKey::DeleteValue(LPCTSTR pValueName) const
{
   LONG result = RegDeleteValue(m_pKey->GetCounted(), pValueName);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::DeleteValue()"), result);
   }
}

// Subkey iteration

CRegistryKey::SubkeyIterator CRegistryKey::BeginSubkeyIteration() const
{
   return SubkeyIterator(m_pKey);
}

CRegistryKey::SubkeyIterator CRegistryKey::EndSubkeyIteration() const
{
   return SubkeyIterator(0);
}
///////////////////////////////////////////////////////////////////////////////
// Value iteration
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::ValueIterator CRegistryKey::BeginValueIteration() const
{
   return ValueIterator(m_pKey);
}

CRegistryKey::ValueIterator CRegistryKey::EndValueIteration() const
{
   return ValueIterator(0);
}

///////////////////////////////////////////////////////////////////////////////
// Query values
///////////////////////////////////////////////////////////////////////////////

bool CRegistryKey::QueryValue(LPCTSTR pValueName, LPBYTE *ppBytes) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_BINARY || dwType == REG_NONE)
   {
      // bufSize should now tell us how much space we need...

		// TODO Smart pointer
		
      pBuffer = (LPBYTE)malloc(bufSize);

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            pBuffer,
            &bufSize);

      if (ERROR_SUCCESS == result)
      {
         ok = true;
         *ppBytes = pBuffer;
      }
      else
      {
         free(pBuffer); //crap

         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}

bool CRegistryKey::QueryValue(LPCTSTR pValueName, LPCTSTR *ppString) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
   {
      // bufSize should now tell us how much space we need...

      pBuffer = (LPBYTE)malloc(bufSize);

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            pBuffer,
            &bufSize);

      if (ERROR_SUCCESS == result)
      {
         ok = true;
         *ppString = (LPCTSTR)pBuffer;
      }
      else
      {
         free(pBuffer); //crap

         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}

bool CRegistryKey::QueryValue(
   LPCTSTR pValueName, 
   DWORD &dwValue) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_DWORD || 
       dwType == REG_DWORD_LITTLE_ENDIAN || 
       dwType == REG_DWORD_BIG_ENDIAN)
   {
      // bufSize should be sizeof(DWORD)
      
      if (bufSize != sizeof(DWORD))
      {
         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            (LPBYTE)&dwValue,
            &bufSize);

      if (ERROR_SUCCESS != result)
      {
          throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}



// QueryValue for other types
// What about multiple values?

CRegistryKey::Value CRegistryKey::QueryValue(LPCTSTR pValueName /* = 0 */) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   // bufSize should now tell us how much space we need...

   pBuffer = new unsigned char[bufSize];

   result = RegQueryValueEx(
         m_pKey->GetCounted(), 
         pValueName, 
         0,
         &dwType,
         pBuffer,
         &bufSize);

   if (ERROR_SUCCESS != result)
   {
      delete[] pBuffer; //crap

      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   return Value(pValueName, pBuffer, bufSize, dwType);
}






void CRegistryKey::SetValue(
    LPCTSTR pValueName, 
    LPBYTE pBytes, 
    DWORD cbBytes) const
{
   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      REG_BINARY,
      pBytes,
      cbBytes);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   LPCTSTR pValueName, 
   LPCTSTR pValue, 
   DWORD dwType /*= REG_SZ*/) const
{
   if (dwType != REG_SZ &&
       //dwType != REG_LINK && 
       dwType != REG_EXPAND_SZ)
       // Handle MULTI_SZ and do the strlen ourselves?
   {
      throw Exception(_T("CRegistryKey::SetValue()"), ERROR_INVALID_FLAGS);
   }

   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      dwType,
      (LPBYTE)pValue,
      _tcslen(pValue) + sizeof(TCHAR));      // length of string plus null

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   LPCTSTR pValueName, 
   DWORD value, 
   DWORD dwType /*= REG_DWORD*/) const
{
   if (dwType != REG_DWORD &&
       dwType != REG_DWORD_LITTLE_ENDIAN && 
       dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), ERROR_INVALID_FLAGS);
   }

   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      dwType,
      (LPBYTE)&value,
      sizeof(DWORD));

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   const Value &value) const
{
   // Should the value store the name too?
   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      value.m_pName,
      0,
      value.m_dwType,
      value.m_pBuffer,
      value.m_bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::LoadKey(LPCTSTR pSubkeyName, LPCTSTR pFile) const
{
   LONG result = RegLoadKey(m_pKey->GetCounted(), pSubkeyName, pFile);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::LoadKey()"), result);
   }
}

void CRegistryKey::UnLoadKey(LPCTSTR pSubkeyName) const
{
   LONG result = RegUnLoadKey(m_pKey->GetCounted(), pSubkeyName);

   if (ERROR_SUCCESS == result)
   {
      throw Exception(_T("CRegistryKey::UnLoadKey()"), result);
   }
}

void CRegistryKey::SaveKey(
   LPCTSTR pFile, 
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   LONG result = RegSaveKey(m_pKey->GetCounted(), pFile, pSecurityAttributes);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SaveKey()"), result);
   }
}

void CRegistryKey::RestoreKey(LPCTSTR pFile, DWORD flags /* = 0 */) const
{
   LONG result = RegRestoreKey(m_pKey->GetCounted(), pFile, flags);

   if (ERROR_SUCCESS == result)
   {
      throw Exception(_T("CRegistryKey::RestoreKey()"), result);
   }
}

void CRegistryKey::ReplaceKey(
   LPCTSTR pNewFile, 
   LPCTSTR pOldFile, 
   LPCTSTR pSubkeyName /* = 0 */) const
{
   LONG result = RegReplaceKey(m_pKey->GetCounted(), pSubkeyName, pNewFile, pOldFile);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::ReplaceKey()"), result);
   }
}

PSECURITY_DESCRIPTOR CRegistryKey::GetKeySecurity(
   SECURITY_INFORMATION securityInformation) const
{
   TExpandableBuffer<unsigned char> descriptor;

   DWORD dwSize = 0;
   
   LONG result = RegGetKeySecurity(m_pKey->GetCounted(), 
      securityInformation, 
      0,
      &dwSize);

   if (ERROR_INSUFFICIENT_BUFFER == result)
   {
      descriptor.Resize(dwSize);

      result = RegGetKeySecurity(m_pKey->GetCounted(), 
            securityInformation, 
            (PSECURITY_DESCRIPTOR)descriptor.GetBuffer(),
            &dwSize);
   }

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::GetKeySecurity()"), result);
   }

   return descriptor.ReleaseBuffer();
}


void CRegistryKey::SetKeySecurity(
   SECURITY_INFORMATION securityInformation,
   PSECURITY_DESCRIPTOR pSecurityDescriptor) const
{
   LONG result = RegSetKeySecurity(m_pKey->GetCounted(), 
      securityInformation, 
      pSecurityDescriptor);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetKeySecurity()"), result);
   }
}

void CRegistryKey::NotifyChangeKeyValue(
   HANDLE hEvent, 
   bool bSubKeys /* = false */,
   DWORD dwNotifyFilter /* = REG_NOTIFY_CHANGE_LAST_SET */) const
{
   LONG result = RegNotifyChangeKeyValue(m_pKey->GetCounted(),
      bSubKeys,
      dwNotifyFilter,
      hEvent,
      true);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::RegNotifyChangeKeyValue()"), result);
   }
}

void CRegistryKey::NotifyChangeKeyValue(
   bool bSubKeys /* = false */,
   DWORD dwNotifyFilter /* = REG_NOTIFY_CHANGE_LAST_SET */) const
{
   LONG result = RegNotifyChangeKeyValue(m_pKey->GetCounted(),
      bSubKeys,
      dwNotifyFilter,
      0,
      false);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::RegNotifyChangeKeyValue()"), result);
   }
}


///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::SubkeyIterator
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::SubkeyIteratorImpl::SubkeyIteratorImpl(CCountedRegKey *pKey) 
   :  CRegKeyIterator(pKey),
      m_Name(0),
      m_Class(0)
{
   if (pKey)
   {
      DWORD dwNumSubKeys = 0;
      DWORD dwMaxNameLen = 0;
      DWORD dwMaxClassLen = 0;

      LONG result = RegQueryInfoKey(
         pKey->GetCounted(),
         NULL,    // Not interested in this key's class 
         NULL,    // ditto
         NULL,    // Reserved
         &dwNumSubKeys,    
         &dwMaxNameLen,    
         &dwMaxClassLen,    
         NULL,    // Not interested in number of values 
         NULL,    // Not interested in max length of value name
         NULL,    // Not interested in max length of value buffer
         NULL,    // Not interested in length of security descriptor
         NULL);   // Not interested in last write time

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::SubkeyIterator::SubkeyIterator()"),
            result);
      }

      if (0 != dwNumSubKeys)
      {
         // Allow for NULL character...

         m_Name.Resize(dwMaxNameLen + 1);
         m_Class.Resize(dwMaxClassLen + 1);
      }
   }
}

bool CRegistryKey::SubkeyIteratorImpl::operator==(const SubkeyIteratorImpl &rhs) const
{
   return CRegKeyIterator::operator==(rhs);
}

LPCTSTR CRegistryKey::SubkeyIteratorImpl::GetName() const
{
   return m_Name;
}

LPCTSTR CRegistryKey::SubkeyIteratorImpl::GetClass() const
{
   return m_Class;
}

CRegistryKey CRegistryKey::SubkeyIteratorImpl::OpenKey(
   REGSAM samDesired /*= KEY_ALL_ACCESS*/) const
{
   return CRegistryKey(m_pKey->GetCounted(), m_Name, samDesired);
}

bool CRegistryKey::SubkeyIteratorImpl::GetItem()
{
   FILETIME m_lastWriteTime;

   bool ok = true;
   bool done = false;

   // Could initialise with a call to GetKeyInfo to get longest key name etc

   while (!done && ok)
   {

      DWORD nameLen = m_Name.GetSize();
      DWORD classLen = m_Class.GetSize();

      LONG result = RegEnumKeyEx(
                     m_pKey->GetCounted(), 
                     m_index,
                     m_Name, 
                     &nameLen,
                     NULL,
                     m_Class,
                     &classLen,
                     &m_lastWriteTime);

      if (ERROR_NO_MORE_ITEMS == result)
      {
         ok = false;
      }
      else if (ERROR_MORE_DATA == result)
      {
         // Size has changed since we started 

         DWORD dwNumSubKeys = 0;
         DWORD dwMaxNameLen = 0;
         DWORD dwMaxClassLen = 0;

         result = RegQueryInfoKey(
            m_pKey->GetCounted(),
            NULL,    // Not interested in this key's class 
            NULL,    // ditto
            NULL,    // Reserved
            &dwNumSubKeys,    
            &dwMaxNameLen,    
            &dwMaxClassLen,    
            NULL,    // Not interested in number of values 
            NULL,    // Not interested in max length of value name
            NULL,    // Not interested in max length of value buffer
            NULL,    // Not interested in length of security descriptor
            NULL);   // Not interested in last write time

         if (ERROR_SUCCESS != result)
         {
            throw Exception(_T("CRegistryKey::SubkeyIterator::GetSubkeyInfo()"),
               result);
         }

         if (0 != dwNumSubKeys)
         {
            // Allow for NULL character...

            m_Name.Resize(dwMaxNameLen + 1);
            m_Class.Resize(dwMaxClassLen + 1);
         }
         else
         {
            done = true;
         }
      }
      else if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::SubkeyIterator::GetSubkeyInfo()"),
            result);
      }
      else
      {
         done = true;
      }
   }
   return ok;
}
///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::ValueIteratorImpl
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::ValueIteratorImpl::ValueIteratorImpl(CCountedRegKey *pKey) 
   :  CRegKeyIterator(pKey),
      m_Name(0),
      m_Buffer(0),
		m_dwType(REG_NONE),
		m_dwBufUsed(0),
		m_pStringRep(0)
{
   if (m_pKey)
   {
      DWORD dwNumValues = 0;
      DWORD dwMaxValueNameLen = 0;
      DWORD dwMaxValueLen = 0;

      LONG result = RegQueryInfoKey(
         m_pKey->GetCounted(),
         NULL,    // Not interested in this key's class 
         NULL,    // ditto
         NULL,    // Reserved
         NULL,    // Not interested in number of sub keys
         NULL,    // Not interested in max sub key name length
         NULL,    // Not interested in max sub key class length
         &dwNumValues,
         &dwMaxValueNameLen,
         &dwMaxValueLen,
         NULL,    // Not interested in length of security descriptor
         NULL);   // Not interested in last write time

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::ValueIterator::ValueIterator()"),
            result);
      }

      if (0 != dwNumValues)
      {
         // Allow for NULL character...

         m_Name.Resize(dwMaxValueNameLen + 1);
         m_Buffer.Resize(dwMaxValueLen + 1);
			m_dwBufUsed = 0;
			m_dwType = REG_NONE;
      }
   }
}

bool CRegistryKey::ValueIteratorImpl::operator==(
      const ValueIteratorImpl &rhs) const
{
   return CRegKeyIterator::operator==(rhs);
}

LPCTSTR CRegistryKey::ValueIteratorImpl::GetName() const
{
   return m_Name;
}

LPCTSTR CRegistryKey::ValueIteratorImpl::AsString() const
{
	if (!m_pStringRep)
	{
		m_pStringRep = Value::AsString(m_dwType, m_Buffer, m_dwBufUsed);
	}

	return m_pStringRep;
}

CRegistryKey::ValueIteratorImpl::operator CRegistryKey::Value() const
{
	return Value(m_Name, m_Buffer, m_dwBufUsed, m_dwType);
}

bool CRegistryKey::ValueIteratorImpl::GetItem()
{
   bool ok = true;
   bool done = false;

	// String representation cache is no longer valid

	delete[] m_pStringRep;
	m_pStringRep = 0;

   while (!done && ok)
   {

      DWORD nameLen = m_Name.GetSize();
      m_dwBufUsed = m_Buffer.GetSize();

      LONG result = RegEnumValue(
                     m_pKey->GetCounted(), 
                     m_index,
                     m_Name, 
                     &nameLen,
                     NULL,
                     &m_dwType,
                     m_Buffer,
                     &m_dwBufUsed);

      if (ERROR_NO_MORE_ITEMS == result)
      {
         ok = false;
      }
      else if (ERROR_MORE_DATA == result)
      {
         DWORD dwNumValues = 0;
         DWORD dwMaxValueNameLen = 0;
         DWORD dwMaxValueLen = 0;

         result = RegQueryInfoKey(
            m_pKey->GetCounted(),
            NULL,    // Not interested in this key's class 
            NULL,    // ditto
            NULL,    // Reserved
            NULL,    // Not interested in number of sub keys
            NULL,    // Not interested in max sub key name length
            NULL,    // Not interested in max sub key class length
            &dwNumValues,
            &dwMaxValueNameLen,
            &dwMaxValueLen,
            NULL,    // Not interested in length of security descriptor
            NULL);   // Not interested in last write time

         if (ERROR_SUCCESS != result)
         {
            throw Exception(_T("CRegistryKey::ValueIterator::GetValueInfo()"),
               result);
         }

         if (0 != dwNumValues)
         {
            // Allow for NULL character...

            m_Name.Resize(dwMaxValueNameLen + 1);
            m_Buffer.Resize(dwMaxValueLen + 1);
         }
         else
         {
            done = true;
         }
      }
      else if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::ValueIterator::GetValueInfo()"),
            result);
      }
      else
      {
         done = true;
      }
   }
   return ok;
}

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::Exception
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::Exception::Exception(
   const LPCTSTR pWhere, 
   LONG error)
   :  CWin32Exception(pWhere, (DWORD)error)
{
}

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::Value
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   const LPBYTE pBuffer, 
   DWORD bufSize, 
   DWORD dwType /*= REG_BINARY*/)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType), 
      m_pBuffer((LPBYTE)duplicateBuffer(pBuffer, bufSize)), 
      m_bufSize(bufSize),
      m_pStringRep(0)
{
}

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   LPCTSTR pString, 
   DWORD dwType /*= REG_SZ */)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType),
      m_pBuffer((LPBYTE)_tcsdup(pString)),
      m_bufSize(_tcslen(pString)),
      m_pStringRep(0)
{
   if (m_dwType != REG_EXPAND_SZ && 
       m_dwType != REG_SZ)
   {
      throw Exception();
   }
}

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   DWORD dwValue, 
   DWORD dwType /*= REG_DWORD*/)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType),
      m_pBuffer(new unsigned char[sizeof(DWORD)]),
      m_bufSize(sizeof(DWORD)),
      m_pStringRep(0)
{
   if (dwType != REG_DWORD &&
       dwType != REG_DWORD_LITTLE_ENDIAN && 
       dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception();
   }

   memcpy(m_pBuffer, &dwValue, sizeof(DWORD));
}

CRegistryKey::Value::Value(const Value &rhs)
   :  m_pName(_tcsdup(rhs.m_pName)), 
      m_dwType(rhs.m_dwType),
      m_pBuffer(rhs.duplicateBuffer()),
      m_bufSize(rhs.m_bufSize),
      m_pStringRep(0)
{
   memcpy(m_pBuffer, rhs.m_pBuffer, m_bufSize);
}

CRegistryKey::Value::~Value()
{
   delete[] m_pName;
   delete[] m_pBuffer;
   delete[] m_pStringRep;
}

CRegistryKey::Value &CRegistryKey::Value::operator=(const Value &rhs)
{
   if (this != &rhs)
   {
      LPBYTE pNewBuffer = rhs.duplicateBuffer();
      LPBYTE pOldBuffer = m_pBuffer;
	  
      LPTSTR pNewName = _tcsdup(rhs.m_pName);
      LPTSTR pOldName = m_pName;

      m_dwType = rhs.m_dwType;
      m_bufSize = rhs.m_bufSize;

      m_pBuffer = pNewBuffer;
      m_pName = pNewName;

      delete[] pOldBuffer;
      delete[] pOldName;

      delete[] m_pStringRep;
      m_pStringRep = 0;
   }

   return *this;
}

CRegistryKey::Value::operator LPBYTE() const
{
   if (m_dwType != REG_BINARY && 
       m_dwType != REG_NONE)
   {
      throw Exception();
   }

   return m_pBuffer;
}

CRegistryKey::Value::operator LPCTSTR() const
{
   if (m_dwType != REG_EXPAND_SZ && 
       m_dwType != REG_SZ)
   {
      throw Exception();
   }

   return (LPCTSTR)m_pBuffer;
}

CRegistryKey::Value::operator DWORD() const
{
   if (m_dwType != REG_DWORD &&
       m_dwType != REG_DWORD_LITTLE_ENDIAN && 
       m_dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception();
   }

   return *(DWORD*)m_pBuffer;
}

LPCTSTR CRegistryKey::Value::Name() const
{
	return m_pName;
}

LPCTSTR CRegistryKey::Value::AsString() const
{
	if (!m_pStringRep)
	{
		m_pStringRep = AsString(m_dwType, m_pBuffer, m_bufSize);
	}

	return m_pStringRep;
}

LPTSTR CRegistryKey::Value::AsString(
	DWORD dwType, 
	const LPBYTE pBuffer, 
	DWORD bufSize)
{
	LPTSTR pStringRep;

   // TODO Convert all reps to a string rep

   if (dwType == REG_EXPAND_SZ || 
       dwType == REG_SZ)
   {
      // Already a string!

      pStringRep = _tcsdup((LPTSTR)pBuffer);
   }
	else if (dwType == REG_MULTI_SZ)
	{
		// multiple strings...

		// Should strip the \0's out...
		pStringRep = _tcsdup((LPTSTR)pBuffer);
	}
	else if (dwType == REG_BINARY || 
			   dwType == REG_NONE)
   {
		pStringRep = bytesAsString(pBuffer, bufSize);
   }
   else if (dwType == REG_DWORD ||
            dwType == REG_DWORD_LITTLE_ENDIAN || 
            dwType == REG_DWORD_BIG_ENDIAN)
   {
      pStringRep = _tcsdup(_T("A number"));
   }
	else
	{
		// Anything else...
      pStringRep = bytesAsString(pBuffer, bufSize);
	}

   return pStringRep;
}

LPTSTR CRegistryKey::Value::bytesAsString(
	const LPBYTE pBuffer, 
	DWORD bufSize)
{
	LPTSTR pStringRep = new TCHAR[(bufSize * 2) + 1];
	
	LPTSTR pHere = pStringRep;
	LPBYTE pBuf = pBuffer;

	for (DWORD i = 0; i < bufSize; i++)
	{
		_stprintf(pHere, _T("%2.2x"), *pBuf);

		pHere += 2;
		pBuf++;
	}

	return pStringRep;
}

LPBYTE CRegistryKey::Value::duplicateBuffer() const
{
	return duplicateBuffer(m_pBuffer, m_bufSize);
}

LPBYTE CRegistryKey::Value::duplicateBuffer(LPBYTE pBuffer, DWORD bufSize) const
{
	LPBYTE pNewBuffer = new unsigned char[bufSize];
	memcpy(pNewBuffer, pBuffer, bufSize);
	
	return pNewBuffer;
}


CRegistryKey::Value::Exception::Exception()
   :  CRegistryKey::Exception(_T("CRegistryKey::Value::Exception"),
         ERROR_INVALID_PARAMETER)
{
}

///////////////////////////////////////////////////////////////////////////////
// Namespace: JetByteTools
///////////////////////////////////////////////////////////////////////////////

} // End of namespace JetByteTools 

///////////////////////////////////////////////////////////////////////////////
// End of file...
///////////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) JetByte Limited
United Kingdom United Kingdom
Len has been programming for over 30 years, having first started with a Sinclair ZX-80. Now he runs his own consulting company, JetByte Limited and has a technical blog here.

JetByte provides contract programming and consultancy services. We can provide experience in COM, Corba, C++, Windows NT and UNIX. Our speciality is the design and implementation of systems but we are happy to work with you throughout the entire project life-cycle. We are happy to quote for fixed price work, or, if required, can work for an hourly rate.

We are based in London, England, but, thanks to the Internet, we can work 'virtually' anywhere...

Please note that many of the articles here may have updated code available on Len's blog and that the IOCP socket server framework is also available in a licensed, much improved and fully supported version, see here for details.

Comments and Discussions