Click here to Skip to main content
15,885,908 members
Articles / Desktop Programming / MFC

The SBJ MVC Framework - The Design View, Responding to Model Change

Rate me:
Please Sign up or sign in to vote.
4.87/5 (22 votes)
19 Mar 2009CPOL13 min read 80.5K   2.4K   51  
A Model-View-Controller Framework that integrates with the MFC Doc/View architecture.
//------------------------------------------------------------------------------
//$Workfile: Registry.cpp $
//$Header: /DevNet/SbjCore/SbjCore/Sys/Registry.cpp 2     4/19/07 11:36a Steve $
//
// Stingray Software Extension Classes
// Copyright (C) 1995 Stingray Software Inc.
// All Rights Reserved
// 
// This source code is only intended as a supplement to the
// Stingray Extension Class product.
// See the SEC help files for detailed information
// regarding using SEC classes.
// 
//
//
//$Revision: 2 $
//
//@doc
//
//@module Module | Module Description
//
//-----------------------------------------------------------------------------

#include "stdafx.h"

#ifdef UNDER_CE
#define ZeroMemory(Destination,Length) memset((Destination),0,(Length))
#endif //UNDER_CE

#include "Registry.h"

namespace SbjCore
{
	namespace Sys
	{

		IMPLEMENT_DYNAMIC( Registry, CObject );

		#ifdef _DEBUG
		#undef THIS_FILE
		static char BASED_CODE THIS_FILE[] = __FILE__;
		#endif

		#define new DEBUG_NEW

		#if defined(WIN32)
		// The following static data members are not accessed
		// correctly when compiled as an extension dll.  Please 
		// use the corresponding constants defined in WINREG.H 

		//HKEY Registry::hKeyLocalMachine = HKEY_LOCAL_MACHINE;
		//HKEY Registry::hKeyClassesRoot  = HKEY_CLASSES_ROOT;
		//HKEY Registry::hKeyUsers		 = HKEY_USERS;
		//HKEY Registry::hKeyCurrentUser  = HKEY_CURRENT_USER;
		#endif

		//@doc Registry
		//@mfunc Constructs a Registry object.
		//@xref <c Registry>
		Registry::Registry()
		{
			// Warning:  Initialize is a virtual method. Calling virtual methods in 
			// constructors is not a good idea if the method is expected to be
			// overridden in a derived class.
			// Rob 1/99

			Initialize();
		}
			
		//@doc Registry
		//@mfunc Destructor.
		//@xref <c Registry>
		Registry::~Registry()
		{
			if ( m_hRegistry != (HKEY) NULL || m_hKey != (HKEY) NULL )
			{
				Close();	
			}

			Initialize();
		}

		//@doc Registry
		//@mfunc Initializes data members.
		//@rdesc void 
		//@comm This method is only intended to be called in the implementation of
		// the Registry class.  Override this method to perform custom 
		// initialization.
		//@xref <c Registry>
		void Registry::Initialize( void )
		{
			ASSERT_VALID( this );

			/*
			** Make sure everything is zeroed out
			*/

			m_strClassName.Empty();
			m_strComputerName.Empty();
			m_strKeyName.Empty();
			m_strRegistryName.Empty();

			// default initialization
			m_hRegistry = (HKEY) NULL;

			m_hKey								= (HKEY) NULL;
			m_bRemote							= FALSE;
			m_lErrorCode						= 0L;
			m_dwNumberOfSubkeys					= 0;
			m_bCloseKeyOnDisconnect             = TRUE;

		#ifdef WIN32
			m_dwLongestSubkeyNameLength			= 0;
			m_dwLongestClassNameLength			= 0;
			m_dwNumberOfValues					= 0;
			m_dwLongestValueNameLength			= 0;
			m_dwLongestValueDataLength			= 0;
			m_dwSecurityDescriptorLength		= 0;
			m_fileTimeLastWrite.dwLowDateTime	= 0;
			m_fileTimeLastWrite.dwHighDateTime	= 0;
		#endif
		}

		#ifdef WIN32 // WIN32 methods

		//@doc Registry
		//@mfunc Closes the Registry object connection
		//@rdesc Nonzero if successful, otherwise 0.
		//@comm Releases the handle of the Registry current key.
		// Encapsulates the RegCloseKey API.  This method is available
		// in both 16 and 32 bit versions.
		//@xref <c Registry>  <mf Registry::Open>
		BOOL Registry::Close( void )
		{
			ASSERT_VALID( this );

			BOOL bSuccess = TRUE;

			if ( (m_hKey != (HKEY)NULL)  && (m_hKey != m_hRegistry))
			{
				m_lErrorCode = ::RegCloseKey( m_hKey );

				if ( m_lErrorCode != ERROR_SUCCESS )
					bSuccess = FALSE;
			} 
			

			m_hKey = (HKEY)NULL;


			if ( m_hRegistry != (HKEY)NULL )
			{
				if (m_bCloseKeyOnDisconnect) 
				{
					m_lErrorCode = ::RegCloseKey( m_hRegistry );

					if ( m_lErrorCode != ERROR_SUCCESS )
						bSuccess = FALSE;
				}

				m_hRegistry = (HKEY) NULL;
			}

			Initialize();

			return bSuccess;
		}
		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Establishes a connection with the registry.
		//@rdesc Nonzero if a connection to the key was established, otherwise 0.
		//@comm This method is called as the first step in accessing the registry.  
		// It connects to an existing key in the registry on the 
		// local or remote  computer named by lpszComputerName.  If 
		// lpszComputerName is NULL or contains an empty string, 
		// the local system is assumed.
		//
		//Once a connection to the registry is established, use the 
		// <mf Registry::Open> method to open keys in the registry.
		//@devnote 32-bit only.  Also, if connecting to a remote registry,
		// and the name of the remote system cannot be found, this method
		// will not return until a time-out occurs.
		//@parm  HKEY | hKeyToOpen | The handle of the registry key to connect to.
		//@parm  LPCTSTR | lpszComputerName  | Name of the computer to connect to.
		//@xref <c Registry> <mf Registry::Open> <mf Registry::Close>
		//@ex | Registry reg;
		//BOOL rc = reg.Connect(HKEY_LOCAL_MACHINE, "RemoteSystemName");
		BOOL Registry::Connect( HKEY hKeyToOpen, LPCTSTR lpszComputerName,BOOL bCloseKeyOnDisconnect)
		{
			ASSERT_VALID( this );

			/*
			** lpszComputerName can be NULL
			*/

			if ( m_hRegistry != (HKEY) NULL || m_hKey != (HKEY) NULL )
			{
				Close();	
			}

			BOOL bSaveCloseKey = m_bCloseKeyOnDisconnect;
			m_bCloseKeyOnDisconnect = bCloseKeyOnDisconnect;

			m_bRemote = ((NULL != lpszComputerName) && (_tcslen(lpszComputerName)));

			if ( hKeyToOpen == HKEY_CLASSES_ROOT || hKeyToOpen == HKEY_CURRENT_USER )
			{
				if (m_bRemote) 
				{
					// NT won't allow you to connect to these hives via RegConnectRegistry
					// on a remote system, so we'll just skip that step  

					m_lErrorCode = ERROR_INVALID_HANDLE;
				}
				else
				{
					m_hRegistry = hKeyToOpen;
					m_hKey = m_hRegistry;
					m_lErrorCode = ERROR_SUCCESS;
				}
			}
			else
			{
					m_lErrorCode = ::RegConnectRegistry( (TCHAR *) lpszComputerName, hKeyToOpen, &m_hRegistry );
					m_hKey = m_hRegistry;
			}

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				if (!m_bRemote)
				{
					TCHAR szComputerName[ MAX_PATH ] = _T("");
					DWORD dwSize = MAX_PATH;
			 
					m_strComputerName.Empty();
					if ( ::GetComputerName( szComputerName, &dwSize ) == TRUE ) 
					{
						m_strComputerName = szComputerName;
					}
				}
				else
				{
					m_strComputerName = lpszComputerName;
				}


				// Derive the root key string
				KeyToStr(hKeyToOpen,m_strRegistryName);

				return( TRUE );
			}
			else
			{
				// if unsuccessful, do not allow state to change
				m_bCloseKeyOnDisconnect = bSaveCloseKey;
				return( FALSE );
			}
		}
		#else
		// This is a replacement version of the Connect method
		// for CE that just calls the Open method
		BOOL Registry::Connect( HKEY hKeyToOpen, LPCTSTR lpszComputerName,BOOL bCloseKeyOnDisconnect)
		{
			ASSERT_VALID( this );
			
			if ( m_hRegistry != (HKEY) NULL || m_hKey != (HKEY) NULL )
			{
				Close();	
			}
			
			BOOL bSaveCloseKey = m_bCloseKeyOnDisconnect;
			m_bCloseKeyOnDisconnect = bCloseKeyOnDisconnect;
			
			m_bRemote = FALSE;
			
			if ( hKeyToOpen == HKEY_CLASSES_ROOT || hKeyToOpen == HKEY_CURRENT_USER )
			{
				m_hRegistry = hKeyToOpen;
				m_hKey = m_hRegistry;
				m_lErrorCode = ERROR_SUCCESS;
			}
			else
			{
				if ((hKeyToOpen == HKEY_LOCAL_MACHINE) || (hKeyToOpen == HKEY_USERS))
				{
					m_lErrorCode = ::RegOpenKeyEx( hKeyToOpen,NULL,0,0, &m_hRegistry );
					m_hKey = m_hRegistry;
				}
				else
				{
					m_lErrorCode = ERROR_INVALID_HANDLE;
				}
			}
			
			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				
				TCHAR szComputerName[ MAX_PATH ] = _T("");
				// CE REIMPL
				// Get the local device name as the computer name
				m_strComputerName = szComputerName;		
				// Derive the root key string
				KeyToStr(hKeyToOpen,m_strRegistryName);
				
				return( TRUE );
			}
			else
			{
				// if unsuccessful, do not allow state to change
				m_bCloseKeyOnDisconnect = bSaveCloseKey;
				return( FALSE );
			}
		}
		#endif //UNDER_CE (WindowsCE)
		//@doc Registry
		//@mfunc Creates key in registry.
		//@rdesc Nonzero if subkey was successfully created, otherwise 0.
		//@syntax Create( LPCTSTR lpszSubkeyName,
		// LPCTSTR lpszClassName, CreateOptions	options,
		// CreatePermissions permissions, LPSECURITY_ATTRIBUTES	pSecurityAttributes,
		// CreationDisposition * pDisposition);
		//@syntax Create(LPCTSTR lpszSubkeyName, LPCTSTR lpszClassName);
		//@parm LPCTSTR | lpszSubkeyName | (32-bit) Name of the subkey to create.
		//@parm LPCTSTR | name_of_subkey | (16-bit) Name of the subkey to create.
		//@parm LPCTSTR | lpszClassName | Name of the class to associate 
		// with the created key.
		//@parm CreateOptions | options | Specifies special options for 
		// the created key.  See Comments for more information.
		//@parm CreatePermissions | permissions | Specifies access permissions 
		// for the key.  See Comments for more information.
		//@parm LPSECURITY_ATTRIBUTES | pSecurityAttributes | Points to a 
		// SECURITY_ATTRIBUTES structure containing security attributes for 
		// the new key.
		//@parm CreationDisposition * | pDisposition | Points to a variable to 
		// receive disposition information.  See Comments for further information.
		//@comm Creates a new registry subkey.  Based on RegCreateKey API for 16-bit, 
		// and RegCreateKeyEx for 32-bit.  Possible values for options include :
		//@flag REG_OPTION_VOLATILE | This key is volatile; the information is stored 
		// in memory and is not preserved when the system is restarted. The 
		// RegSaveKey function does not save volatile keys.
		//@flag REG_OPTION_NON_VOLATILE | This key is not volatile; the information 
		// is stored in a file and is preserved when the system is restarted. 
		// The RegSaveKey function saves keys that are not volatile.
		//
		//Possible values for permissions include:
		//
		//@flag KEY_ALL_ACCESS | Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, 
		// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and KEY_SET_VALUE access.
		//@flag KEY_CREATE_LINK | Permission to create a symbolic link.
		//@flag KEY_CREATE_SUB_KEY | Permission to create subkeys.
		//@flag KEY_ENUMERATE_SUB_KEYS | Permission to enumerate subkeys.
		//@flag KEY_EXECUTE | Permission for read access.
		//@flag KEY_NOTIFY | Permission for change notification.
		//@flag KEY_QUERY_VALUE | Permission to query subkey data.
		//@flag KEY_READ | Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, 
		// and KEY_NOTIFY access.
		//@flag KEY_SET_VALUE | Permission to set subkey data.
		//@flag KEY_WRITE | Combination of KEY_SET_VALUE and KEY_CREATE_SUB_KEY 
		// access.
		//
		//Possible return values for pDisposition include:
		//
		//@flag REG_CREATED_NEW_KEY | The key did not exist and was created.
		//@flag REG_OPENED_EXISTING_KEY | The key existed and was simply opened 
		// without being changed.
		//@xref <c Registry> <mf Registry::Open> <mf Registry::Close>
		BOOL Registry::Create( LPCTSTR				lpszSubkeyName,
								 LPCTSTR				lpszClassName,
								 CreateOptions			options,
								 CreatePermissions		permissions,
								 LPSECURITY_ATTRIBUTES	pSecurityAttributes,
								 CreationDisposition *	pDisposition
								 )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSubkeyName != NULL );

			if ( NULL == lpszSubkeyName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD disposition = 0;

			if ( NULL == lpszClassName )
			{
				lpszClassName = _T("");
			}

			CString strSubkey(lpszSubkeyName);

			NormalizeKey(strSubkey);

			// The key handle (can) be equal to the registry handle.
			// If this is the case, closing the key also closes the
			// registry, so we should avoid this.	
			if ( (m_hKey != (HKEY) NULL) && (m_hKey != m_hRegistry) ) {
				m_lErrorCode = ::RegCloseKey(m_hKey);
				ASSERT( ERROR_SUCCESS == m_lErrorCode );
			}

			m_lErrorCode = ::RegCreateKeyEx( m_hRegistry,
							 strSubkey,
							 (DWORD) 0,
							 (TCHAR *) lpszClassName,
							 options,
							 permissions,
							 pSecurityAttributes,
							 &m_hKey,
							 &disposition );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				if ( pDisposition != NULL )
				{
					*pDisposition = (CreationDisposition) disposition;
				}

				m_strKeyName = lpszSubkeyName;

				return( TRUE );
			}
			else
			{
				return( FALSE );
			}
		}

		//@doc Registry
		//@mfunc Deletes the named registry value of the current key.
		//@syntax (32-bit) BOOL DeleteKey( LPCTSTR lpszKeyToDelete, BOOL bRecursive);
		//@syntax (16-bit) BOOL DeleteKey( LPCTSTR lpszKeyToDelete);
		//@rdesc Nonzero if key was deleted, otherwise 0.
		//@parm  LPCTSTR | lpszKeyToDelete | Name of the key to delete.
		//@parmopt  BOOL | bRecursive | FALSE | Indicates recursive delete
		// operation is requested.
		//@comm Deletes the named registry key. If bRecursive is set to
		// TRUE, it will delete keys in the registry which have subkeys.
		//
		// The name of the key to delete can be a relative name (to the 
		// currently open key), or can be an absolute key name.
		//
		// Unless the current key was deleted by this operation, the 
		// current open key is not affected for this Registry instance.
		// If however, the current key was deleted, the current key is 
		// set to the parent key of the deleted key.
		//
		//@devnote The recursive function is not available in the 16 bit
		// version.
		//@xref <c Registry> <mf Registry::DeleteValue> <mf Registry::Close>
		//@end
		BOOL Registry::DeleteKey( LPCTSTR lpszKeyToDelete, BOOL bRecursive /* = FALSE */)
		{
			ASSERT_VALID( this );
			ASSERT( lpszKeyToDelete != NULL );

			Registry reg;
			BOOL bSubKey = FALSE; // assume the key is an absolute path
			BOOL bSuccess = TRUE; // assume no failure so far

			if (NULL == lpszKeyToDelete)
			{

				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			CString strKeyToDelete(lpszKeyToDelete);

			/*
			** Whoa Nelly! If this is a recursive delete, fail if the 
			** key to delete is a system root key
			*/

			if (bRecursive && (NULL != lpszKeyToDelete) && !(_tcslen(lpszKeyToDelete)))
			{

				if (   (m_hRegistry == HKEY_CLASSES_ROOT)
					|| (m_hRegistry == HKEY_CURRENT_USER)
					|| (m_hRegistry == HKEY_LOCAL_MACHINE)
					|| (m_hRegistry == HKEY_USERS)
		#ifndef UNDER_CE
					|| (m_hRegistry == HKEY_PERFORMANCE_DATA)

		#if(WINVER >= 0x0400)			
					|| (m_hRegistry == HKEY_CURRENT_CONFIG)
					|| (m_hRegistry == HKEY_DYN_DATA)
		#endif /* WINVER >= 0x0400 */
		#endif  //UNDER_CE (WindowsCE)

					)
				{

					// A recursive delete from an empty key name will remove 
					// all subkeys from the root key.  This assert is here 
					// as a warning that this condition was present.
					ASSERT(FALSE);

					m_lErrorCode = ERROR_INVALID_PARAMETER;
					return FALSE;
				}

			}

		//	if (!reg.Connect(m_hRegistry, m_bRemote?m_strComputerName:(LPCTSTR)NULL, FALSE))
			if (!reg.Connect(StrToKey(m_strRegistryName), m_bRemote?m_strComputerName:(LPCTSTR)NULL, FALSE))
			{
				m_lErrorCode = reg.m_lErrorCode;
				return FALSE;
			}

			/*
			** Can the key be opened relative to the current key?
			*/

			if (strKeyToDelete.Find('\\') != 0) {
				CString strKey = ConcatenateKeys(m_strKeyName, lpszKeyToDelete);

				if (reg.Open(strKey))
					bSubKey = TRUE;
			}

			/*
			** else, try it as an absolute key name...
			*/
			if (!bSubKey) {
				if (!reg.Open(lpszKeyToDelete))
				{
					m_lErrorCode = reg.m_lErrorCode;
					return FALSE;
				}
			}


			if (bRecursive)
			{
				/*
				** Now do a recursive pre-order traversal.
				*/
				CString strEnumKey;
				
				while(reg.EnumerateKeys(0, strEnumKey) && bSuccess) {
					bSuccess = reg.DeleteKey(strEnumKey, TRUE);
					m_lErrorCode = reg.m_lErrorCode;
					}	

			}

			/*
			** Now delete the leaf whether recursive or not...
			*/

			if (bSuccess)
			{

				/*
				** Is the key a full path?
				*/

				if (bSubKey)
				{
					/*
					** User had not given us a full path so assume the name of the key he passed us
					** is a key off of the current key
					*/

					m_lErrorCode = ::RegDeleteKey( m_hKey, lpszKeyToDelete);
				}
				else
				{
					/*
					** You can't delete a key given a full path. What you have to do is back up 
					** one level and then do a delete
					*/
					
					CString strFullKeyName = lpszKeyToDelete;
					CString strParent;
					CString strChild;

					if ( strFullKeyName.Find( '\\' ) == (-1) )
					{
						strParent = "";
						strChild = lpszKeyToDelete;
					}

					else 
					{
						int nLastBackslashLocation = strFullKeyName.GetLength() - 1;

						/*
						** We know this loop will succeed because a back slash was found 
						** in the above if statement
						*/

						while( strFullKeyName[ nLastBackslashLocation ] != '\\' )
						{
							nLastBackslashLocation--;
						}

						CString strOpenKeyName = m_strKeyName;

						strParent = strFullKeyName.Left( nLastBackslashLocation );
						strChild = strFullKeyName.Right( ( strFullKeyName.GetLength() - nLastBackslashLocation ) - 1 );
					}

					/*
					** Now we open the parent key and delete the child
					** (Use the temporary Registry object so as not to
					** change the current key of this object!)
					*/

					if ( reg.Open( strParent ) == TRUE )
					{
						m_lErrorCode = ::RegDeleteKey( reg.m_hKey, strChild );

						/*
						** Now, make sure that the current key is still valid
						** (i.e., that it wasn't a key that was deleted)
						*/

						if (!reg.Open(m_strKeyName)) 
							Open(strParent);

					}
					else
					{
						m_lErrorCode = reg.m_lErrorCode;
						return( FALSE );
					}
				}
			}

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}						

		//@doc Registry
		//@mfunc Deletes the named registry value of the current key.
		//@rdesc Nonzero if value was deleted, otherwise 0.
		//@parm  LPCTSTR | lpszValueToDelete  | Name of value to delete.
		//@comm Deletes the named registry value of the current key of Registry.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::DeleteKey> <mf Registry::Close>
		BOOL Registry::DeleteValue( LPCTSTR lpszValueToDelete )
		{
			ASSERT_VALID( this );

			/*
			** lpszValueToDelete can be NULL
			*/

			m_lErrorCode = ::RegDeleteValue( m_hKey, (LPTSTR) lpszValueToDelete );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}


		//@doc Registry
		//@mfunc Enumerates subkeys of the currently open key.
		//@syntax (32-bit) EnumerateKeys(const DWORD dwSubkeyIndex,
		// CString& strSubkeyName);
		//@syntax (32-bit) EnumerateKeys(const DWORD dwSubkeyIndex,
		// CString& strSubkeyName,
		// CString& strClassName );
		//@syntax (32-bit) EnumerateKeys(const DWORD dwSubkeyIndex,
		// LPTSTR lpszSubkeyName,
		// LPDWORD lpdwSizeSubkeyName,
		// LPTSTR lpszClassName, 
		// LPDWORD lpdwSizeClassName);
		//@syntax (16-bit) EnumerateKeys( const DWORD dwIndex, 
		// LPTSTR lpszBuffer, 
		// DWORD dwBufferSize);
		//@rdesc Nonzero if subkey was enumerated, otherwise 0.
		//@comm The 32-bit version encapsulates RegEnumerateKeys API, 
		// the 16-bit version encapsulates RegEnumKey.
		//@parm const DWORD | dwSubkeyIndex | Index of desired subkey
		//@parm CString& | strSubkeyName | String to receive the
		// enumerated key name.
		//@parm CString& | strClassName | String to receive the 
		// enumerated class name.
		//@parm LPTSTR | lpszSubkeyName | Buffer to receive the
		// enumerated key name
		//@parm LPDWORD | lpdwSizeSubkeyName | The size of the 
		// lpszSubkeyName buffer in bytes
		//@parm LPTSTR | lpszClassName | Buffer to receive the 
		// enumerated class name
		//@parm LPDWORD | lpdwSizeClassName | The size of the 
		// lpdwSizeClassName buffer in bytes.
		//@xref <c Registry> <mf Registry::EnumerateValues>
		//@ex | UINT nIndex = 0;
		//CString strKeyName;
		//while (EnumerateKeys(nIndex++, strKeyName))
		//    m_listbox.AddString(strKeyName);
		//@end
		BOOL Registry::EnumerateKeys(const DWORD dwSubkeyIndex,
										LPTSTR lpszSubkeyName,
										LPDWORD lpdwSizeSubkeyName,
										LPTSTR lpszClassName, /* = NULL */
										LPDWORD lpdwSizeClassName /* = NULL */) 
		{
			ASSERT(lpszSubkeyName);
			ASSERT(lpdwSizeSubkeyName);
			// lpszClassName can be NULL
			// lpdwClassNameSize can be NULL

			m_lErrorCode = ::RegEnumKeyEx( m_hKey, 
							dwSubkeyIndex, 
							lpszSubkeyName, 
							lpdwSizeSubkeyName,
							NULL,
							lpszClassName,
							lpdwSizeClassName,
							&m_fileTimeLastWrite );

			return m_lErrorCode;	

		}

		BOOL Registry::EnumerateKeys(const DWORD dwSubkeyIndex,
										CString& strSubkeyName) 
		{
			TCHAR szSubkeyName[ 2048 ];
			DWORD dwSizeSubkeyName = sizeof( szSubkeyName ) - 1;

			LONG lResult;
			
			lResult = EnumerateKeys(dwSubkeyIndex, 
									(LPTSTR)szSubkeyName, &dwSizeSubkeyName, 
									NULL, NULL);

			if ((ERROR_SUCCESS == lResult) || (ERROR_MORE_DATA == lResult))
			{
				strSubkeyName = szSubkeyName;
				return TRUE;
			}

			return FALSE;

		}


		BOOL Registry::EnumerateKeys(const DWORD dwSubkeyIndex,
										CString& strSubkeyName,
										CString& strClassName )
		{
			ASSERT_VALID( this );

			TCHAR szSubkeyName[ 2048 ];
			TCHAR szClassName [ 2048 ];

			DWORD dwSizeSubkeyName = sizeof( szSubkeyName ) - 1;
			DWORD dwSizeClassName  = sizeof( szClassName  ) - 1;

			LONG lResult;

			lResult = EnumerateKeys(dwSubkeyIndex,
									szSubkeyName, &dwSizeSubkeyName,
									szClassName, &dwSizeClassName);

			if ((ERROR_SUCCESS == lResult) || (ERROR_MORE_DATA == lResult))
			{
				strSubkeyName = szSubkeyName;
				strClassName  = szClassName;

				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Enumerates values of the currently open key.
		// EnumerateValues( const DWORD dwValueIndex,
		// CString&	strValueName,
		// KeyValueTypes& type_code );
		//@syntax EnumerateValues( const DWORD dwValueIndex,
		// CString&	strValueName,
		// KeyValueTypes& type_code,
		// LPBYTE lpbDataBuffer,
		// DWORD& dwSizeDataBuffer );
		//@syntax EnumerateValues( const DWORD dwValueIndex,
		// CString& strValueName,
		// KeyValueTypes* pTypeCode,
		// LPBYTE lpbDataBuffer,
		// LPDWORD	lpdwSizeDataBuffer);
		//@rdesc Nonzero if value was deleted, otherwise 0.
		//@parm const DWORD | dwValueIndex | Index of value to query.
		//@parm CString& | strValueName | String to receive the 
		// enumerated value name.
		//@parm KeyValueTypes& | type_code | KeyValueType to receive
		// the data type.
		//@parm LPBYTE | lpbDataBuffer | A data buffer to receive
		// the value data.
		//@parm DWORD& | dwSizeDataBuffer | The size of the value
		// data buffer in bytes.
		//@comm Encapsulates the RegEnumValue() API.
		//@devnote For 32-bit only.  Check SECREG.H for optional parameters.
		//@xref <c Registry> <mf Registry::EnumerateKeys>
		//@end
		BOOL Registry::EnumerateValues( const DWORD dwValueIndex,
										  CString&		 strValueName,
										  KeyValueTypes& type_code,
										  LPBYTE			lpbDataBuffer,
										  DWORD&			dwSizeDataBuffer )
		{
			return EnumerateValues(	dwValueIndex, strValueName, 
									&type_code, lpbDataBuffer, &dwSizeDataBuffer);
		}

		BOOL Registry::EnumerateValues( const DWORD dwValueIndex,
							CString&		 strValueName,
							KeyValueTypes& type_code )
		{
			return EnumerateValues(	dwValueIndex, strValueName, 
									&type_code, NULL, NULL);
		}


		BOOL Registry::EnumerateValues( const DWORD dwValueIndex,
							CString&		 strValueName,
							KeyValueTypes* pTypeCode,			/* = NULL */
							LPBYTE			lpbDataBuffer,		/* = NULL */
							LPDWORD		  lpdwSizeDataBuffer	/* = NULL */)
		{

			ASSERT_VALID( this );

			/*
			** pTypeCode, lpbDataBuffer and dwSizeDataBuffer can be NULL
			*/

			DWORD dwTempCodeType;

			TCHAR szTempName[ 2048 ];

			DWORD dwTempNameSize = sizeof( szTempName );
			
			m_lErrorCode = ::RegEnumValue( m_hKey,
							dwValueIndex,
							szTempName,
							&dwTempNameSize,
							NULL,
							&dwTempCodeType,
							lpbDataBuffer,
							lpdwSizeDataBuffer );

			
			// LH: check for success or "more data" return codes
			// Note:  This ERROR_MORE_DATA return code is not listed in the 
			//		  Windows documentation for this function.
			if (( ERROR_SUCCESS == m_lErrorCode )  || ( ERROR_MORE_DATA == m_lErrorCode ))
			{
				if (pTypeCode)
				{
					*pTypeCode = (KeyValueTypes) dwTempCodeType;
				}

				strValueName  = szTempName;
				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Writes the attributes of the currently open key.
		//@rdesc Nonzero if registry was flushed, otherwise 0.
		//@comm  Encapsulates RegFlushKey() API.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::Open> <mf Registry::Close>
		BOOL Registry::Flush( void )
		{
			ASSERT_VALID( this );

			m_lErrorCode = ::RegFlushKey( m_hKey );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Retrieves a REG_BINARY value.
		//@rdesc Nonzero if successful, otherwise 0.
		//@parm  LPCTSTR | lpszValueName | The value name.
		//@parm  CByteArray& | return_array  | A CByteArray
		// to receive the binary data.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::GetValue> <mf Registry::SetBinaryValue>
		BOOL Registry::GetBinaryValue( LPCTSTR lpszValueName, CByteArray& return_array )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwBufferSize = m_dwLongestValueDataLength;

			LPBYTE lpbMemoryBuffer = (LPBYTE) ::malloc( dwBufferSize );

			if ( NULL == lpbMemoryBuffer )
			{
				m_lErrorCode = ::GetLastError();
				return( FALSE );
			}

			BOOL bReturn = TRUE;

			KeyValueTypes type = typeBinary;

			if ( QueryValue( lpszValueName, type, lpbMemoryBuffer, dwBufferSize ) == TRUE )
			{
				/*
				** We've got data so give it back to the caller
				*/

				return_array.RemoveAll();

				DWORD dwIndex = 0;

				while( dwIndex < dwBufferSize )
				{
					return_array.Add( lpbMemoryBuffer[ dwIndex ] );
					dwIndex++;
				}

				bReturn = TRUE;
			}
			else
			{
				bReturn = FALSE;
			}

			::free( lpbMemoryBuffer );

			return( bReturn );
		}

		//@doc Registry
		//@mfunc Retrieves a REG_DWORD value (32-bit only).
		//@rdesc BOOL 
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName | The value name.
		//@parm  DWORD& | dwReturnValue | A DWORD to receive
		// the data.
		//@xref <c Registry> <mf Registry::GetValue> <mf Registry::SetDoubleWordValue>
		BOOL Registry::GetDoubleWordValue( LPCTSTR lpszValueName, DWORD& dwReturnValue )
		{
			 ASSERT_VALID( this );
			 ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwBufferSize = sizeof( DWORD );

			KeyValueTypes type = typeDoubleWord;

			return( QueryValue( lpszValueName, type, (LPBYTE) &dwReturnValue, dwBufferSize ) );
		}

		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Encapsulates the RegGetSecurity() call.
		//@rdesc Nonzero if security information was successfully retrieved, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  const SECURITY_INFORMATION | security_info | Descriptor structure 
		// of what security information is being requested.
		//@parm PSECURITY_DESCRIPTOR | lpbDataBuffer | A buffer to receive the
		// requested information.
		//@parm DWORD& | dwSizeDataBuffer | Size of retrieved buffer.
		//@xref <c Registry> <mf Registry::GetValue>
		BOOL Registry::GetSecurity( const SECURITY_INFORMATION security_info,
									  PSECURITY_DESCRIPTOR	lpbDataBuffer,
									  DWORD&					dwSizeDataBuffer )
		{
			ASSERT_VALID( this );
			ASSERT( lpbDataBuffer != NULL );

			if ( NULL == lpbDataBuffer )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegGetKeySecurity( m_hKey,
												security_info,
												lpbDataBuffer,
												&dwSizeDataBuffer );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}
		#endif //UNDER_CE (WindowsCE)
		//@doc Registry
		//@mfunc Retrieves a REG_SZ value.
		//@rdesc Nonzero if successful, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName | The name of the value.
		//@parm  CString& | strReturn | A CString to receive the string data.
		//@xref <c Registry> <mf Registry::GetValue> <mf Registry::SetStringValue>
		BOOL Registry::GetStringValue( LPCTSTR lpszValueName, CString& strReturn )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			TCHAR szTemp[ 2048 ];
			DWORD dwBufferSize = 2048;

		#if defined(WIN32)
			::ZeroMemory( szTemp, sizeof( szTemp ) );
		#endif //win32

			KeyValueTypes type = typeString;

			if ( QueryValue( lpszValueName, type, (LPBYTE) szTemp, dwBufferSize ) == TRUE )
			{
				strReturn = szTemp;
				return( TRUE );
			}
			else
			{
				strReturn.Empty();
				return( FALSE );
			}
		}

		//@doc Registry
		//@mfunc Retrieves a REG_MULTI_SZ value.
		//@rdesc Nonzero if successful, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName | The value name.
		//@parm  CStringArray& | return_array | A CStringArray to receive
		// the string data.
		//@xref <c Registry> <mf Registry::GetValue> <mf Registry::SetStringArrayValue>
		BOOL Registry::GetStringArrayValue( LPCTSTR lpszValueName, CStringArray& return_array )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwBufferSize = m_dwLongestValueDataLength;

			LPBYTE lpbMemoryBuffer = (LPBYTE) ::malloc( dwBufferSize );

			if ( NULL == lpbMemoryBuffer )
			{
				m_lErrorCode = ::GetLastError();
				return( FALSE );
			}

			BOOL bReturn = TRUE;

			KeyValueTypes type = typeMultipleString; // A double NULL terminated string

			if ( QueryValue( lpszValueName, type, lpbMemoryBuffer, dwBufferSize ) == TRUE )
			{
				/*
				** We've got data so give it back to the caller
				*/

				LPTSTR lpszStrings = (LPTSTR) lpbMemoryBuffer;

				return_array.RemoveAll();

				DWORD dwLen = 0;
				while(((dwLen*sizeof(TCHAR)) < (dwBufferSize-sizeof(TCHAR))))
				{
					return_array.Add( (LPCTSTR) lpszStrings );
					int nLen = (int)_tcslen( (LPCTSTR) lpszStrings) + 1;
					lpszStrings += nLen;
					dwLen += nLen;
				}

				bReturn = TRUE;
			}
			else
			{
				bReturn = FALSE;
			}

			::free( lpbMemoryBuffer );

			return( bReturn );
		}

		//@doc Registry
		//@mfunc Retrieves the value of the current key.
		//@syntax virtual BOOL GetValue(LPCTSTR lpszValueName, CByteArray& return_array);
		//@syntax virtual BOOL GetValue(LPCTSTR lpszValueName, DWORD& dwReturnValue);
		//@syntax virtual BOOL GetValue(LPCTSTR lpszValueName, CStringArray& return_array);
		//@syntax virtual BOOL GetValue(LPCTSTR lpszValueName, CString& strReturn);
		//@rdesc Nonzero if value was successfully retrieved, otherwise 0.
		//@comm Retrieves data from named value.  Encapsulates RegQueryValueEx API.
		//
		//These methods assume the type of data is known when called.  This can
		// be determined by either enumerating the data with 
		// <mf Registry::EnumerateValues>, or calling <mf Registry::QueryValue>.
		//@devnote For 32-bit only.
		//@parm LPCTSTR | lpszValueName | The value name
		//@parm CByteArray& |return_array | A CByteArray to receive binary data.
		//@parm DWORD& | dwReturnValue | A DWORD to receive DWORD data
		//@parm CStringArray& | return_array | A CStringArray to receive
		// multi-string data.
		//@parm CString& | strReturn | A CString to receive string data.
		//@xref <c Registry> <mf Registry::Open> <mf Registry::Close>
		// <mf Registry::EnumerateValues> <mf Registry::QueryValue>
		// <mf Registry::GetBinaryValue> <mf Registry::GetDoubleWordValue>
		// <mf Registry::GetStringValue> <mf Registry::GetStringArrayValue>
		// <mf Registry::SetValue>
		BOOL Registry::GetValue( LPCTSTR lpszValueName, CByteArray& return_array )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( GetBinaryValue( lpszValueName, return_array ) );
		}

		BOOL Registry::GetValue( LPCTSTR lpszValueName, DWORD& dwReturnValue )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( GetDoubleWordValue( lpszValueName, dwReturnValue ) );
		}

		BOOL Registry::GetValue( LPCTSTR lpszValueName, CString& strReturn )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( GetStringValue( lpszValueName, strReturn ) );
		}

		BOOL Registry::GetValue( LPCTSTR lpszValueName, CStringArray& return_array )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( GetStringArrayValue( lpszValueName, return_array ) );
		}
		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Encapsulates the RegLoadKey API.
		//@rdesc Nonzero if loaded correctly, otherwise 0.
		//@parm  LPCTSTR | lpszSubkeyName | Nonzero if loaded correctly, otherwise 0.
		//@parm  LPCTSTR | lpszFileName | Name of file that stores the hive of registry info.
		//@comm Deletes the named registry value.  Based on the RegLoadKey API.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::Save>
		BOOL Registry::Load( LPCTSTR lpszSubkeyName, LPCTSTR lpszFileName )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSubkeyName != NULL );
			ASSERT( lpszFileName != NULL );

			if ( NULL == lpszSubkeyName  || NULL == lpszFileName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegLoadKey( m_hRegistry, lpszSubkeyName, lpszFileName );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Indicates when a registry key has changed.  
		//@rdesc BOOL 
		//@comm Encapsulates RegNotifyChangeKeyValue API to indicate when a registry 
		// key has changed.  If bWaitForChange is TRUE, the wait is performed 
		// synchronously, and control returns immediately to the program.  
		// Otherwise the function will wait for the event to occur.  
		// The changes_to_be_reported parameter accepts the same arguments as 
		// the fdwNotifyFilter parameter in the RegNotifyChangeKeyValue API, and 
		// can include any combination of the following values:
		//@flag REG_NOTIFY_CHANGE_NAME | Changes to key names that occur in the 
		// specified key or in the specified key and its subkeys cause a change 
		// notification. This includes key creations and deletions.
		//@flag REG_NOTIFY_CHANGE_ATTRIBUTES | Attribute changes that occur in a 
		// key or in a key and its subkeys cause a change notification.
		//@flag REG_NOTIFY_CHANGE_LAST_SET | Changes to the last write time that 
		// occur in a key or in a key and its subkeys cause a change notification.
		//@flag REG_NOTIFY_CHANGE_SECURITY | Security-descriptor changes that occur 
		// in a key or in a key and its subkeys cause a change notification.
		//@devnote For 32-bit only.
		//@parm  const HANDLE | hEvent | Handle of signalled event.
		//@parm  const NotifyChangeFilter | reported_changes | Type of change 
		// to be reported (see Comments).
		//@parm  const BOOL | bAllSubkeys | Boolean flag to indicate whether any 
		// change in subkeys should cause notification.
		//@parm  const BOOL | bWaitForChange | Specifies how a change should be 
		// reported.
		//@xref <c Registry>
		BOOL Registry::NotifyChange( const HANDLE	hEvent,
									   const NotifyChangeFilter reported_changes,
									   const BOOL		bAllSubkeys,
									   const BOOL		bWaitForChange )
		{
			ASSERT_VALID( this );

			m_lErrorCode = ::RegNotifyChangeKeyValue( m_hKey,
							bAllSubkeys,
							reported_changes,
							hEvent,
							bWaitForChange);

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				return( TRUE );
			}
			return( FALSE );
		}
		#endif //UNDER_CE (WindowsCE)

		//@doc Registry
		//@mfunc Opens a registry key.
		//@syntax (32-bit) BOOL Open( LPCTSTR lpszSubkey, 
		// const CreatePermissions security_access_mask );
		//@syntax (16-bit) BOOL Open( LPCTSTR lpszSubkey);
		//@rdesc Nonzero if key was successfully opened, otherwise 0.
		//@parm  LPCTSTR | lpszSubkey | Name of key to open.
		//@parm  const CreatePermissions | security_access_mask  | Security flags 
		// to associate with the open key (see Comments).
		//@comm Opens the specified key with the requested permissions.  The 32-bit 
		// version encapsulates the RegOpenKeyEx API, while the 16-bit version 
		// encapsulates the RegOpenKey API.  The security_access_mask can be 
		// a combination of the following parameters:
		//@flag KEY_ALL_ACCESS | Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, 
		// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and KEY_SET_VALUE access.
		//@flag KEY_CREATE_LINK | Permission to create a symbolic link.
		//@flag KEY_CREATE_SUB_KEY | Permission to create subkeys.
		//@flag KEY_ENUMERATE_SUB_KEYS | Permission to enumerate subkeys.
		//@flag KEY_EXECUTE | Permission for read access.
		//@flag KEY_NOTIFY | Permission for change notification.
		//@flag KEY_QUERY_VALUE | Permission to query subkey data.
		//@flag KEY_READ | Combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, 
		// and KEY_NOTIFY access.
		//@flag KEY_SET_VALUE | Permission to set subkey data.
		//@flag KEY_WRITE | Combination of KEY_SET_VALUE and 
		// KEY_CREATE_SUB_KEY access.
		//@xref <c Registry> <mf Registry::Close>
		BOOL Registry::Open( LPCTSTR lpszSubkey, const CreatePermissions security_access_mask )
		{
			ASSERT_VALID( this );

			/*
			** lpszSubkey can be NULL
			*/

			// The registry key member can be null at this point.  If so,
			// use the default registry key (HKEY_CLASSES_ROOT)
			if ((HKEY) NULL == m_hRegistry ) 
				m_hRegistry = HKEY_CLASSES_ROOT;

			// The key handle (can) be equal to the registry handle.
			// If this is the case, closing the key also closes the
			// registry, so we should avoid this.
			if ( (m_hKey != (HKEY) NULL) && (m_hKey != m_hRegistry) ) {
				m_lErrorCode = ::RegCloseKey(m_hKey);
				ASSERT( ERROR_SUCCESS == m_lErrorCode );
			}

			CString strSubkey(lpszSubkey);

			NormalizeKey(strSubkey);

			m_lErrorCode = ::RegOpenKeyEx( m_hRegistry, strSubkey, NULL, security_access_mask, &m_hKey );

			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				QueryInfo();
				m_strKeyName = lpszSubkey;

				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Queries for information about the currently opened key
		//@rdesc Nonzero if successful, othewise 0.
		//@comm Queries for information about the currently opened key.  
		// The following public members are updated to reflect the query:
		//
		//m_strClassName				class name<nl>
		//m_dwNumberOfSubkeys			number of subkeys<nl>
		//m_dwLongestSubkeyName			length of longest subkey name<nl>
		//m_dwLongestClassNameLength	length of longest subkey class name<nl>
		//m_dwNumberOfValues			number of subkey values<nl>
		//m_dwLongestValueNameLength	length of longest value name<nl>
		//m_dwLongestValueDataLength	longest value data length<nl>
		//m_dwSecurityDescription		security descriptor length<nl>
		//m_fileTimeLastWrite			last write time of buffer<nl>
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::QueryValue>
		BOOL Registry::QueryInfo( void )
		{
			ASSERT_VALID( this );

			TCHAR szClassName[ 2048 ];

			DWORD dwSizeClassName = sizeof( szClassName ) - 1;
		#ifndef UNDER_CE
		#if defined(WIN32) 
			::ZeroMemory( szClassName, sizeof( szClassName ) );
		#endif
		#endif //UNDER_CE (WindowsCE)		
			m_lErrorCode = ::RegQueryInfoKey( m_hKey,
							szClassName,
							&dwSizeClassName,
							(LPDWORD) NULL,
							&m_dwNumberOfSubkeys,
							&m_dwLongestSubkeyNameLength,
							&m_dwLongestClassNameLength,
							&m_dwNumberOfValues,
							&m_dwLongestValueNameLength,
							&m_dwLongestValueDataLength,
							&m_dwSecurityDescriptorLength,
							&m_fileTimeLastWrite );
			
			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				m_strClassName = szClassName;
				m_timeLastWrite = CTime( m_fileTimeLastWrite );
				 
				return( TRUE );
			}
			return( FALSE );
		}


		//@doc Registry
		//@mfunc Encapsulates the RegQueryValueEx API.
		//@syntax (32-bit) BOOL QueryValue( LPCTSTR	lpszValueName,
		// KeyValueTypes& value_type,
		// LPBYTE lpbBuffer,
		// DWORD& dwBufferSize );
		//@syntax (16-bit) BOOL QueryValue(  LPCTSTR lpszSubkeyName,
		// SECPBYTE lpbBuffer,
		// LONG& lBufferSize );
		//@rdesc Nonzero if value was successfully queried, otherwise 0.
		//@comm Queries the value of the currently opened registry key and copies 
		// the value data into the buffer.  Encapsulates the RegQueryValue API.
		//@parm LPCTSTR | lpszValueName | Name of value to query.
		//@parm LPCTSTR | lpszSubkeyName | name of subkey to query.
		//@parm KeyValueTypes& | value_type | Buffer to hold value type.
		//@parm LPBYTE | lpbBuffer | Buffer to hold value data.
		//@parm DWORD&  | dwBufferSize | Size of buffer pointed to by lpbBuffer; 
		//@parm DWORD&  | dwBufferSize | Size of buffer pointed to by lpbBuffer; 
		// on return contains size of data actually copied to buffer.
		//@parm LONG& | lBufferSize | Size of buffer pointed to by lpbBuffer; 
		// on return contains size of data actually copied to buffer.
		//@xref <c Registry> <mf Registry::QueryInfo>
		BOOL Registry::QueryValue( LPCTSTR	lpszValueName,
									 KeyValueTypes&	value_type,
									 LPBYTE			lpbBuffer,
									 DWORD&			dwBufferSize )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			/*
			** lpbBuffer and dwBufferSize can be NULL
			*/

			if ( NULL == lpszValueName )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwTempDataType = value_type;

			m_lErrorCode = ::RegQueryValueEx( m_hKey,
							(TCHAR *) lpszValueName,
							NULL,
							&dwTempDataType,
							lpbBuffer,
							&dwBufferSize );


			if ( ERROR_SUCCESS == m_lErrorCode )
			{
				value_type = (KeyValueTypes) dwTempDataType;
				return( TRUE );
			}
			return( FALSE );
		}
		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Encapsulates the RegReplaceKey API.
		//@rdesc Nonzero if value was successfully replaced, otherwise 0.
		//@parm LPCTSTR | lpszSubkeyName | Name of subkey to replace.
		//@parm     LPCTSTR lpszNewFile Name of the file storing registration information.
		//@parm     LPCTSTR lpszBackupFile Name of the file to store a backup of the key being 
		//@comm Replaces the subkey data with the information contained in the lpszNewFile.  
		// Encapsulates the RegReplaceKey API.	
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::Load> <mf Registry::UnLoad>
		// <mf Registry::Restore>
		BOOL Registry::Replace( LPCTSTR lpszSubkeyName,
								  LPCTSTR lpszNewFile,
								  LPCTSTR lpszBackupFile )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSubkeyName != NULL );
			ASSERT( lpszNewFile != NULL );
			ASSERT( lpszBackupFile != NULL );

			if (  NULL == lpszSubkeyName  ||
				NULL == lpszNewFile  ||
				NULL == lpszBackupFile  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegReplaceKey( m_hKey, 
							lpszSubkeyName,
							lpszNewFile,
							lpszBackupFile );

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}

		//@doc Registry
		//@mfunc Encapsulates the RegRestoreKey API.
		//@rdesc Nonzero if key was successfully restored, otherwise 0.
		//@parm  LPCTSTR | lpszSavedTreeFile | Name of file with registry information.
		//@parm  const DWORD | dwVolatilityFlags | Specifies volatility of the key.
		//@comm Overwrites the current registry key with the information contained in the saved tree file.  
		// Encapsulates the RegRestoreKey API.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::Load> <mf Registry::Replace>
		BOOL Registry::Restore( LPCTSTR lpszSavedTreeFile, const DWORD dwVolatilityFlags )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSavedTreeFile != NULL );

			if (  NULL == lpszSavedTreeFile  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegRestoreKey( m_hKey,
							lpszSavedTreeFile,
							dwVolatilityFlags );
			
			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}	
			return( FALSE );
		}


		//@doc Registry
		//@mfunc Encapsulates the RegSaveKey API.
		//@rdesc Nonzero if key was successfully saved, otherwise 0.
		//@comm  Saves the registration information for the current key.  
		// Encapsulates the RegSaveKey API. 
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszDestFile | Specifies the file where 
		// registration information associated with the current key will be stored.
		//@parm  LPSECURITY_ATTRIBUTES | pSecurityAttributes  | Points to a 
		// SECURITY_ATTRIBUTES structure for the new file.
		//@xref <c Registry> <mf Registry::Load> <mf Registry::Replace>
		// <mf Registry::Restore>
		BOOL Registry::Save( LPCTSTR lpszDestFile, LPSECURITY_ATTRIBUTES pSecurityAttributes )
		{
			ASSERT_VALID( this );
			ASSERT( lpszDestFile != NULL );

			if (  NULL == lpszDestFile  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegSaveKey( m_hKey, lpszDestFile, pSecurityAttributes );

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}
		#endif //UNDER_CE (WindowsCE)
		//@doc Registry
		//@mfunc Sets a value for the key name specified as REG_BINARY.
		//@rdesc Nonzero if successful, otherwise 0.
		//@parm  LPCTSTR | lpszValueName |  The value name.
		//@parm  const CByteArray& | bytes_to_write | The binary data to set.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::SetValue> <mf Registry::GetBinaryValue>
		BOOL Registry::SetBinaryValue( LPCTSTR lpszValueName, const CByteArray& bytes_to_write )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwBufferSize = (DWORD)bytes_to_write.GetSize();

			LPBYTE lpbMemoryBuffer = (LPBYTE) ::malloc( dwBufferSize );

			if (  NULL == lpbMemoryBuffer  )
			{
				m_lErrorCode = ::GetLastError();
				return( FALSE );
			}

			DWORD dwIndex = 0;

			while( dwIndex < dwBufferSize )
			{
				lpbMemoryBuffer[ dwIndex ] = bytes_to_write[ dwIndex ];
				dwIndex++;
			}

			BOOL bReturn = SetValue( lpszValueName, typeBinary, lpbMemoryBuffer, dwBufferSize );

			::free( lpbMemoryBuffer );

			return( bReturn );
		}

		//@doc Registry
		//@mfunc Sets a value for the key name specified as REG_DWORD.
		//@rdesc Nonzero if successful, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName | The value name.
		//@parm  DWORD | dwValue  | The value to set.
		//@xref <c Registry> <mf Registry::SetValue> <mf Registry::GetDoubleWordValue>
		BOOL Registry::SetDoubleWordValue( LPCTSTR lpszValueName, DWORD dwValue )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetValue( lpszValueName, typeDoubleWord, (const PBYTE) &dwValue, sizeof( DWORD ) ) );
		}
		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Encapsulates the RegSetKeySecurity API.
		//@rdesc Nonzero if security was successfully set, otherwise 0.
		//@parm  const SECURITY_INFORMATION& | SecurityInformation | Descriptor structure 
		// of what security information is being set
		//@parm const PSECURITY_DESCRIPTOR  | pSecurityDescriptor  | Points to the 
		// security attributes being set for the currently open key.
		//@comm Sets the security for the currently open registry key.  
		// Encapsulates the RegSetKeySecurity API.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::SetValue>
		BOOL Registry::SetSecurity( const SECURITY_INFORMATION& SecurityInformation,
									  const PSECURITY_DESCRIPTOR  pSecurityDescriptor )
		{
			ASSERT_VALID( this );
			ASSERT( pSecurityDescriptor != NULL );

			if (  NULL == pSecurityDescriptor  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegSetKeySecurity( m_hKey, SecurityInformation, pSecurityDescriptor );

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				 return( TRUE );
			}
			return( FALSE );
		}
		#endif //UNDER_CE (WindowsCE)

		//@doc Registry
		//@mfunc Sets a value for the key name specified as REG_SZ (32-bit only).
		//@rdesc Nonzero if successful, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName |  The value name.
		//@parm  const CString& | string_value | The string data to set.
		//@xref <c Registry> <mf Registry::SetValue> <mf Registry::SetStringArrayValue>
		// <mf Registry::GetStringValue>
		BOOL Registry::SetStringValue( LPCTSTR lpszValueName, const CString& string_value )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetValue( lpszValueName, typeString, (const PBYTE) (const TCHAR *) string_value, string_value.GetLength()*sizeof(TCHAR) + 1 ) );
		}

		//@doc Registry
		//@mfunc Sets a value for the key name specified as REG_MULTI_SZ.
		//@rdesc Nonzero if successful, otherwise 0.
		//@devnote For 32-bit only.
		//@parm  LPCTSTR | lpszValueName |  The value name.
		//@parm  const CStringArray& | string_array | The string data to set.
		//@xref <c Registry> <mf Registry::SetValue> <mf Registry::SetStringValue>
		// <mf Registry::GetStringArrayValue>
		BOOL Registry::SetStringArrayValue( LPCTSTR lpszValueName, const CStringArray& string_array )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			DWORD dwBufferSize = 0;

			/*
			** Find out how big our buffer needs to be...
			*/

			int nIndex		= 0;
			int nNumStrings = (int)string_array.GetSize();

			while( nIndex < nNumStrings )
			{
				 dwBufferSize += string_array[nIndex].GetLength() + 1;
				 nIndex++;
			}

			/*
			** Don't forget the second NULL needed for double null terminated strings...
			*/
			dwBufferSize++;

			dwBufferSize *= sizeof(TCHAR);

			LPBYTE lpbMemoryBuffer = (LPBYTE) ::malloc( dwBufferSize  );

			if (  NULL == lpbMemoryBuffer  )
			{
				m_lErrorCode = ::GetLastError();
				return( FALSE );
			}
		#ifndef UNDER_CE
			::ZeroMemory( lpbMemoryBuffer, dwBufferSize );
		#endif //UNDER_CE (WindowsCE)

			/*
			** OK, now add the strings to the memory buffer
			*/

			LPTSTR lpszString = (LPTSTR) lpbMemoryBuffer;

			nIndex				 = 0;
			int nStringLength  = 0;

			while( nIndex < nNumStrings )
			{
				 _tcscpy_s( &lpszString[ nStringLength ], dwBufferSize, string_array[nIndex] );
				 nStringLength += string_array[nIndex].GetLength() + 1;

				nIndex++;
			}

			nStringLength++;

			BOOL bReturn = TRUE;

			KeyValueTypes type = typeMultipleString; // A double NULL terminated string

			if ( SetValue( lpszValueName, type, lpbMemoryBuffer, dwBufferSize ) != TRUE )
			{
				bReturn = FALSE;
			}

			::free( lpbMemoryBuffer );

			return( bReturn );
		}

		//@doc Registry
		//@mfunc Sets a value for the key name specified.
		//@syntax (32-bit) virtual BOOL SetValue(LPCTSTR lpszValueName, const CByteArray& bytes_to_write);
		//@syntax (32-bit) virtual BOOL SetValue(LPCTSTR lpszValueName, DWORD dwValue);
		//@syntax (32-bit) virtual BOOL SetValue(LPCTSTR lpszValueName, const CStringArray& strings_to_write);
		//@syntax (32-bit) virtual BOOL SetValue(LPCTSTR lpszValueName, const CString& strWrite);
		//@syntax (32-bit) virtual BOOL SetValue(LPCTSTR lpszValueName, const KeyValueTypes type_of_value_to_set, LPBYTE lpbValueData, const DWORD dwSize);
		//@syntax (16-bit) virtual BOOL SetValue(LPCTSTR lpSubKey, const CString& strWrite);
		//@rdesc Nonzero if value was successfully set, otherwise 0.
		//@parm LPCTSTR | lpszValueName | Name of value to set (32-bit).
		//@parm LPCTSTR | lpSubKey | Name of the subkey to set value for )16-bit).
		//@parm const CByteArray& | bytes_to_write | Array of bytes to write as binary type.
		//@parm DWORD | dwValue | DWORD value to write.
		//@parm const CStringArray& | strings_to_write | Array of strings to write.
		//@parm const CString&  | strWrite | Single string to write.
		//@parm const KeyValueTypes | type_of_value_to_set | Type of value to be set.  
		// Can be one of the following:
		//@flag typeBinary |
		//@flag typeDoubleWord |
		//@flag typeDoubleWordLittleEndian |
		//@flag typeDoubleWordBigEndian | 
		//@flag typeUnexpandedString |
		//@flag typeSymbolicLink |
		//@flag typeMultipleString |
		//@flag typeNone |
		//@flag typeResourceList |
		//@flag typeString |
		//@parm LPBYTE  | lpbValueData | Pointer to buffer of data to be set.
		//@parm const DWORD | dwSize | Size of buffer pointed to by lpbBuffer.
		//@comm Sets the named value with the data value passed in.  The 32-bit version
		// encapsulates the RegSetValueEx API, 16-bit encapsulates the RegSetValue API.
		//@xref <c Registry> <mf Registry::GetValue>
		BOOL Registry::SetValue( LPCTSTR lpszValueName, const CByteArray& bytes_to_write )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetBinaryValue( lpszValueName, bytes_to_write ) );
		}

		BOOL Registry::SetValue( LPCTSTR lpszValueName, DWORD dwValue )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetDoubleWordValue( lpszValueName, dwValue ) );
		}

		BOOL Registry::SetValue( LPCTSTR lpszValueName, const CStringArray& strings_to_write )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetStringArrayValue( lpszValueName, strings_to_write ) );
		}

		BOOL Registry::SetValue( LPCTSTR lpszValueName, const CString& strWrite )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );

			if (  NULL == lpszValueName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			return( SetStringValue( lpszValueName, strWrite ) );
		}

		BOOL Registry::SetValue( LPCTSTR		lpszValueName, 
						 const KeyValueTypes	type_of_value_to_set, 
						 LPBYTE					lpbValueData, 
						 const DWORD			dwSize )
		{
			ASSERT_VALID( this );
			ASSERT( lpszValueName != NULL );
			ASSERT( lpbValueData != NULL );

			if (  NULL == lpszValueName  ||  NULL == lpbValueData  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegSetValueEx( m_hKey,
											lpszValueName,
											0,
											type_of_value_to_set,
											lpbValueData,
											dwSize );
			
			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}
		#ifndef UNDER_CE
		//@doc Registry
		//@mfunc Encapsulates the RegUnLoadKey API.
		//@rdesc Nonzero if key was successfully unloaded, otherwise 0.
		//@parm  LPCTSTR lpszSubkey 
		//@comm Unloads the specified key and its subkeys from the registry.  
		// Encapsulates the RegUnLoadKey API.
		//@devnote For 32-bit only.
		//@xref <c Registry> <mf Registry::Load>
		BOOL Registry::UnLoad( LPCTSTR lpszSubkey )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSubkey != NULL );

			if (  NULL == lpszSubkey  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			m_lErrorCode = ::RegUnLoadKey( m_hKey, lpszSubkey );

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}
		#endif //UNDER_CE (WindowsCE)
		// Utility routine to convert a string to the HKEY_ equiv.
		//@doc Registry
		//@mfunc Converts an HKEY constant to its string equivalent.
		//@rdesc Nonzero if successful, otherwise 0.
		//@parm HKEY | hKey | HKEY handle to convert.
		//@parm CString& | strKey | String to receive conversion
		//@comm This method maps the standard HKEY values to strings
		//
		//The keys this method recognizes (and their mapped strings) include:<nl>
		//HKEY_CLASSES_ROOT		"HKEY_CLASSES_ROOT"<nl>
		//HKEY_CURRENT_USER		"HKEY_CURRENT_USER"<nl>
		//HKEY_LOCAL_MACHINE	"HKEY_LOCAL_MACHINE"<nl>
		//HKEY_USERS			"HKEY_USERS"<nl>
		//HKEY_CURRENT_CONFIG	"HKEY_CURRENT_CONFIG"<nl>
		//HKEY_DYN_DATA			"HKEY_DYN_DATA"<nl>
		//
		//If a the key cannot be matched, the string reference
		// is not altered.
		//@xref <c Registry> <mf Registry::StrToKey>
		BOOL Registry::KeyToStr(HKEY hKey,CString& strKey) {

			// assume failure
			BOOL rc = FALSE;

			if ( HKEY_CLASSES_ROOT == hKey ) {
				strKey = _T("HKEY_CLASSES_ROOT");
				rc = TRUE;
			}
			else if ( HKEY_CURRENT_USER  == hKey ) {
				strKey = _T("HKEY_CURRENT_USER");
				rc = TRUE;
			}
			else if ( HKEY_LOCAL_MACHINE == hKey ) {
				strKey = _T("HKEY_LOCAL_MACHINE");
				rc = TRUE;
			}
			else if ( HKEY_USERS == hKey ) {
				strKey = _T("HKEY_USERS");
				rc = TRUE;
			}
		#ifndef UNDER_CE
			else if ( HKEY_CURRENT_CONFIG  == hKey ) {
				strKey = _T("HKEY_CURRENT_CONFIG");
				rc = TRUE;
			}
			else if ( HKEY_DYN_DATA  == hKey ) {
				strKey = _T("HKEY_DYN_DATA");
				rc = TRUE;
			}
		#endif //UNDER_CE (WindowsCE)

			// if no match was found, do not modify string,
			// simply return false

			return rc;
		}
			
		//@doc Registry
		//@mfunc Converts an string equivalent to its HKEY constant.
		//@rdesc The matching HKEY successful, otherwise 0.
		//@parm CString& | strKey | String to receive conversion
		//@comm This method maps the standard strings to HKEY values.
		// See <mf Registry::KeyToStr> for recognized strings.
		//@xref <c Registry> <mf Registry::KeyToStr>
		HKEY Registry::StrToKey(const CString& strKey){
			HKEY hKey=NULL;
			if(strKey==_T("HKEY_CLASSES_ROOT"))
				hKey=HKEY_CLASSES_ROOT;
			else if(strKey==_T("HKEY_CURRENT_USER"))
				hKey=HKEY_CURRENT_USER;
			else if(strKey==_T("HKEY_LOCAL_MACHINE"))
				hKey=HKEY_LOCAL_MACHINE;
			else if(strKey==_T("HKEY_USERS"))
				hKey=HKEY_USERS;
		#ifndef UNDER_CE
			else if(strKey==_T("HKEY_CURRENT_CONFIG"))
				hKey=HKEY_CURRENT_CONFIG;
			else if(strKey==_T("HKEY_DYN_DATA"))
				hKey=HKEY_DYN_DATA;
		#endif //UNDER_CE (WindowsCE)
				
			return hKey;		
		}

		#else // WIN16 routines

		BOOL Registry::Close( void )
		{
			ASSERT_VALID( this );

			BOOL bSuccess = TRUE;

			if ( m_hKey != (HKEY) NULL )
			{
				m_lErrorCode = ::RegCloseKey( m_hKey );

				if ( m_lErrorCode != ERROR_SUCCESS )
					bSuccess = FALSE;

				// if the key handle is equal to the registry handle, 
				// let's null out the registry handle so that we 
				// don't try to close the same handle twice below
				// (thus avoiding a return code of SUCCESS_INVALID_HANDLE)
				if (m_hKey == m_hRegistry) 
					m_hRegistry = (HKEY) NULL;

				m_hKey = (HKEY) NULL;

			}

			if ( m_hRegistry != (HKEY) NULL )
			{
				if (m_bCloseKeyOnDisconnect)
				{
					m_lErrorCode = ::RegCloseKey( m_hRegistry );

					if ( m_lErrorCode != ERROR_SUCCESS )
						bSuccess = FALSE;
				}

				m_hRegistry = (HKEY) NULL;
			}

			Initialize();

			return bSuccess;
		}

		BOOL Registry::Create( LPCTSTR lpszSubkeyName, 
						LPCTSTR lpszClassName
					  )
		{
			ASSERT_VALID( this );
			ASSERT( lpszSubkeyName != NULL );

			if (  NULL == lpszSubkeyName  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}


			if (  NULL == lpszClassName  )
			{
				lpszClassName = "";
			}
			
			// The key handle (can) be equal to the registry handle.
			// If this is the case, closing the key also closes the
			// registry, so we should avoid this.
			if ( (m_hKey != (HKEY) NULL) && (m_hKey != m_hRegistry) ) {
				m_lErrorCode = ::RegCloseKey(m_hKey);
				ASSERT(  ERROR_SUCCESS == m_lErrorCode  );
			}

			m_lErrorCode = ::RegCreateKey( m_hRegistry,
							lpszSubkeyName,
							&m_hKey);
			
			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				m_strKeyName = lpszSubkeyName;		 
				return( TRUE );
			}
			return( FALSE );
		}

		BOOL Registry::DeleteKey( LPCTSTR lpszKeyToDelete)
		{
			ASSERT_VALID( this );
			ASSERT( lpszKeyToDelete != NULL );

			if (  NULL == lpszKeyToDelete  )
			{
				m_lErrorCode = ERROR_INVALID_PARAMETER;
				return( FALSE );
			}

			/*
			** You can't delete a key given a full path. What you have to do is back up one level and then do a delete
			*/

			CString strFullKeyName = lpszKeyToDelete;

			if ( strFullKeyName.Find( '\\' ) == (-1) )
			{
				/*
				** User had not given us a full path so assume the name of the key he passed us
				** is a key off of the current key
				*/

				m_lErrorCode = ::RegDeleteKey( m_hKey, lpszKeyToDelete);
			}
			else
			{
				int nLastBackslashLocation = strFullKeyName.GetLength() - 1;

				/*
				** We know this loop will succeed because a back slash was found in the above if statement
				*/

				while( strFullKeyName[ nLastBackslashLocation ] != '\\' )
				{
					nLastBackslashLocation--;
				}

				CString strOpenKeyName = m_strKeyName;

				CString strParent = strFullKeyName.Left( nLastBackslashLocation );
				CString strChild  = strFullKeyName.Right( ( strFullKeyName.GetLength() - nLastBackslashLocation ) - 1 );

				/*
				** Now we open the parent key and delete the child
				*/

				if ( Open( strParent ) == TRUE )
				{
					m_lErrorCode = ::RegDeleteKey( m_hKey, strChild );
				}
				else
				{
					m_strKeyName = strOpenKeyName;
					return( FALSE );
				}
			}

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}						

		BOOL Registry::EnumerateKeys( const DWORD dwIndex, LPTSTR lpszBuffer, DWORD dwBufferSize)
		{
			ASSERT_VALID( this );

			m_lErrorCode = ::RegEnumKey( m_hKey, 
							dwIndex,
							lpszBuffer,
							dwBufferSize);	

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		} 

		//@doc Registry
		//@mfunc Determines the number of subkeys in the curently open key.
		//@rdesc Nonzero if the key was enumerated, otherwise 0.
		//@parm  LPCTSTR | lpszBuffer | The key to be enumerated.
		//@parm  UINT | &nKeys | The number of subkeys result.
		//@devnote For 16-bit only.
		//@xref <c Registry> <mf Registry::EnumerateKeys>
		BOOL Registry::GetSubkeys( LPCTSTR lpszBuffer, UINT &nKeys)
		{
			// Determine number of subkeys  
			char temp_buf[1000]; 
			HKEY hKeyHolder = m_hKey;
			nKeys = 0;

			Open(lpszBuffer);
			while (EnumerateKeys((DWORD)nKeys++, temp_buf, 1000));
			
			// Close the enumerator and reset the original key
			Close();
			m_hKey = hKeyHolder;
			
			// Make sure the count is accurate since the last EnumerateKeys call has to fail
			nKeys--; 
			return TRUE;
		}



		BOOL Registry::Open( LPCTSTR lpszSubkey)
		{
			ASSERT_VALID( this );

			/*
			** lpszSubkey can be NULL
			*/

			// The key handle (can) be equal to the registry handle.
			// If this is the case, closing the key also closes the
			// registry, so we should avoid this.
			if ( (m_hKey != (HKEY) NULL) && (m_hKey != m_hRegistry) ) {
				m_lErrorCode = ::RegCloseKey(m_hKey);
				ASSERT( m_lErrorCode == ERROR_SUCCESS );
			}

			m_lErrorCode = ::RegOpenKey( m_hRegistry, lpszSubkey, &m_hKey );

			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				m_strKeyName = lpszSubkey;
				return( TRUE );
			}
			return( FALSE );
		}

		BOOL Registry::QueryValue(  LPCTSTR lpszSubkeyName,
							 SECPBYTE	lpbBuffer,
							 LONG&		lBufferSize )
		{
			ASSERT_VALID(this); 
			LONG lCheck;

			m_lErrorCode = ::RegQueryValue(m_hKey, lpszSubkeyName, lpbBuffer, &lCheck);
			lBufferSize = lCheck;
				 
			if ( ERROR_SUCCESS == m_lErrorCode)
			{
				return ( TRUE );
			}
			return ( FALSE );
		}

		BOOL Registry::SetValue( LPCTSTR				 lpszSubkeyName, 
						 const CString& strWrite )
		{
			ASSERT_VALID( this );
			ASSERT( strWrite != NULL );

			m_lErrorCode = ::RegSetValue( m_hKey,
						 lpszSubkeyName,
						 REG_SZ, // must be REG_SZ for Win3.1
						 strWrite,
						 0);
			if (  ERROR_SUCCESS == m_lErrorCode  )
			{
				return( TRUE );
			}
			return( FALSE );
		}

		#endif


		// Normalizes a keyname
		void Registry::NormalizeKey(CString& strKey, BOOL bSubkey) {

			// the convention is to remove any backslashes from the end
			// if it is a subkey, also remove it from the beginning if present

			if (strKey.GetLength() == 0) {
				return;
			}
			else {

				int nLen = strKey.GetLength();

				// is there a backslash at the end?
				if (strKey.ReverseFind('\\') == (nLen-1))
					// remove it
					strKey = strKey.Mid(0,nLen-1);

				nLen = strKey.GetLength();

				// is there a backslash at the beginning?
				if (strKey.Find('\\') == 0) {
					// if this is a subkey, remove it
					if (bSubkey)
						strKey = strKey.Mid(1);
				}

			}


		}

		// Concatenates a key with a subkey
		CString Registry::ConcatenateKeys (LPCTSTR szKey, LPCTSTR szSubkey) {

			CString strKey(szKey);
			CString strSubkey(szSubkey);

			NormalizeKey(strKey);
			NormalizeKey(strSubkey, TRUE);

			return strKey + "\\" + strSubkey;
		}

	}
}


//*** Modification History ***
//$Log: /DevNet/SbjCore/SbjCore/Sys/Registry.cpp $
// 
// 2     4/19/07 11:36a Steve
// 
// 1     4/02/07 4:56p Steve
// 

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
SBJ
United States United States
Real name is Steve Johnson. Programming since 1979. Started on a Heathkit Micro with a DEC LSI-11 and UCSD Pascal. Moved to PCs & DOS as soon as Turbo Pascal became available. Did some Assembly, ISR, TSR etc. All this while working for a Manufacturing Co. for 8 years. Had my own solo Co. doing barcode labeling software for 4 years (terrible business man, all I wanted to do was code). Since then working for various software companies. Moved to Windows around the time of 3.1 with Borland C then C++. Then on to VC++ and MFC, and just about anything I could get my hands on or had to learn for my job, and been at it ever since. Of course recently I've been playing with .NET, ASP, C#, WPF etc.

Comments and Discussions