# Simple registry class Enhancement

By , 15 May 2002

 Before Run After Run Data stored in system registry

## Purposes of class

### Cache

The purpose of this class is to simplify working with registry data. To make the class more useful I made two things: create internal cache and create default data store of registry values. The first feature is used to boost performance of registry access. The second feature used in the case when the application, which will use this class, starts the first time on the computer and doesn't find its own registry values. In such case the default values will be in the class cache and on access the application will have only default values. These simplify installation of applications on new computers and also give an easy way to configure the application. Default values will be rewritten by the class if variables of the same name are found in the registry.

The class gives a great performance boost when accessing registry data often.
There is also simple error detection.

All values are stored into the registry as string data.
Only one level of subkeys are scanned by the RefreshContents() function. (it can be redefined by inherited classes)

## Implementation Notes

For the implementation of internal cache std::map<std::string, std::string> was used. In it the class stores values found by the registry path set on the class construction call. The Class scan the registry path for subkeys and store them into cache like DOS paths. Values on the upper level of registry can be easily accessed by its name, other values found in subkeys can be accessed by path.
Example:

  /* Registry access example */
registry.GetValueByName( "value-Name", strValue ); // upper level variables
registry.GetValueByName( "stldbg_optdlg\\autoexpand", strValue ); // subkey value access

If you try to get a variable value, and the variable is not yet in cache, then the access functions will return NULL string or Zero Long value. The class doesn't create any notification by which can be detected if it tries to access a non existing variable. In most cases this is not needed at all.
All values are first stored only in the cache, and only after a call to RefreshRegistry() function will they be stored in the  system registry. As you understand RegistryEx class can be used to share data between many dialogs or classes. That is why its most useful to make RegistryEx class instance singleton in application.

In using the cache there are some exceptions: on calling the delete functions, values are deleted from the system registry and from the cache too. That is why the delete functions can work a little slower then access functions. Also on application crash the data will not be stored into the registry, but data will be deleted as well. To make the application safe when working with the registry it is recommended to call the RefreshRegistry() function after each significant change of registry values. Update of system registry is not well optimized, it's simply created values and subkeys according to cache content.

### Default Values

Default Values in class are created by virtual function SetDefaults(). By default class CRegistryEx does not have any implementation of this function. Function is defined with protected access and can be only redefined by inherited classes. This function is called by RefreshContents() function before trying to read system registry. In example you can saw redefinition of SetDefaults() function. To simplify work with class cache any class which inherit CRegistryEx will have free access to it. Developer can choose which style of access he/she wants to use: direct access or throw Accessor functions.

## How to use it

CRegistryEx - is a utility class which can be used in MFC or WTL(Win32) projects to store and set values into system registry. Most interested feastures of class are:

• On Init CRegistryEx class read all values from registry and stores them into cache. Also in cache added all one level lower sub-folders. That feature limits memory usage and give easier way to use registry settings.
Example:
We have such structure of registry:
HKEY_LOCAL_MACHINE            ( folder )
+ Software                  ( folder )
+ MyApplication           ( folder )
+ Dialog1               ( folder )
+ Dialog2               ( folder )
+ Dialog3               ( folder )
+ Additional Settings ( folder )


  CRegistryEx registry( HKEY_LOCAL_MACHINE, "software\\MyApplication" );
try
{
registry.RefreshContents();
}
catch( string err )
{
cout << err.c_str();
exit(-1);
}


After such calls in CRegistryEx class cache will be stored values of such folders: MyApplication, Dialog1, Dialog2, Dialog3. "Additional Settings" folder values will not be read.

• GetValueByName
• GetArrayByTemplate
• GetArrayByArray
• Write:
• SetValueByName
• SetValueByTemplate
• SetArrayByArray
• Delete:
• DeleteByName
• DeleteByTemplate
• DeleteByArray

In the case when value is not in registry then when the READ functions is called it will return NULL length string or LONG value equal to 0. To add a value into the registry simple call WRITE functions and all values will be modified or added. To Refresh the cache from the registry use the function RefreshContents(). To Store all values into the registry use the function RefreshRegistry().

## Array and Template Works

Templates are used to store a list(array) of values into the registry. Template can specify variables with only one diff - order number. Template have printf function format. Into Template can be added only one %d marker, otherwise will be stack corruption...

NOTE: Template is a very useful feature to store and restore history of dialogs settings.

All values on WRITE functions calls will be renumbered and stored with new name. That is why before calling WRITE functions call DELETE function to clean registry from old values.
Example:
  void CStringClipBoard::SetInitSettings()
{
CInitDialogImpl::SetInitSettings();
/* ...  */
m_pRegistry->DeleteByTemplate( "findhistory\\history%d" );
m_pRegistry->SetValueByTemplate( "findhistory\\history%d", m_arrHistory );
};

After function call in registry will be added variables like:
  history1
history2
history3
...
historyN

into subkey findhistory...

NOTE: in class for variables names can be used only symbols in lower case otherwise value will not be found in CRegistryEx class cache.

Values automatically will be added into registry on destructor call.

## Example Description

Let's define our own class which will inherit from the CRegistryEx class and redefine the SetDefaults function of it. Then let's add a function which will list the internal cache.

class CRegistryDemo : public CRegistryEx
{
public:
explicit CRegistryDemo( HKEY key, const string &path ) : CRegistryEx( key, path ){};
void PrintCacheContent( void )
{
const TRegMap &maps = GetRegistryMap();
TRegMap::const_iterator iter;

for( iter = maps.begin(); iter != maps.end(); iter++ )
{
printf( "Path == %s\nValue == %s\n", iter->first.c_str(), iter->second.c_str() );
}
};

protected:
virtual void SetDefaults( void )
{
// find dialog
m_mapValues[ "\\limit" ] = "20";
m_mapValues[ "\\matchcase" ] = "0";
m_mapValues[ "\\regexp" ] = "0";
m_mapValues[ "\\up" ] = "0";

// search file extensions
m_mapValues[ "keywords_optdlg\\extensions" ] = ".cpp;.hpp;.hxx;.cxx;.c;.h;";

// keywords list
m_mapValues[ "search\\search1" ] = "// NOTE: ";
m_mapValues[ "search\\search2" ] = "// TEST: ";

m_mapValues[ "sorting_optdlg\\parsetime" ] = "10000";
m_mapValues[ "sorting_optdlg\\showtime" ]  = "20000";

// STL debug dialog
m_mapValues[ "stldbgdlg\\autoexpand" ] = "400";
m_mapValues[ "stldbgdlg\\limit" ] = "20";

// stl debug options
m_mapValues[ "stldbg_optdlg\\autoexpand" ] = "1";
m_mapValues[ "stldbg_optdlg\\autotype" ] = "1";
m_mapValues[ "stldbg_optdlg\\showsymbolchange" ] = "0";
m_mapValues[ "stldbg_optdlg\\symbolchange" ] = "1";
}
};


After creation of our own class our main() function will look like:

  // set start point of registry class
CRegistryDemo registry( HKEY_CURRENT_USER, "Software\\RegistryEx Test" );

try
{
// Refresh class cache from registry
registry.RefreshContents();
}
catch( string error )
{
printf( "Error: %s", error.c_str() );
exit( -1 );
}

LONG lTest1;
string strTest1;

registry.GetValueByName( "stldbgdlg\\autoexpand", lTest1 );
registry.GetValueByName( "search\\text1", strTest1 );

printf( "autoexpand value = %d\ntext = %s", lTest1, strTest1.c_str() );

registry.PrintCacheContent();

// NOTE: on CRegistryEx class destructor call, all values will be saved
// to registry according to specified paths...

return 0;


## History

v1.0 First this class was implemented and used in Visual Studio Add-in: "ToDoList Add-on".
v1.1 Current version published now.

 Oleksandr Kucherenko CEO ArtfulBits Inc. Ukraine Member
Name:Kucherenko Oleksandr

Born:September 20, 1979

Platforms: Win32, Linux; - well known and MS-DOS; Win16; OS/2 - old time not touched;

Hardware: IBM PC

Programming Languages: Assembler (for Intel 80386); Borland C/C++; Borland Pascal; Object Pascal; Borland C++Builder; Delphi; Perl; Java; Visual C++; Visual J++; UML; XML/XSL; C#; VB.NET; T-SQL; PL/SQL; and etc.

Development Environments: MS Visual Studio 2001-2008; MS Visual C++; Borland Delphi; Borland C++Builder; C/C++ any; Rational Rose; GDPro; Together and etc.

Libraries: STL, ATL, WTL, MFC, NuMega Driver Works, VCL; .NET 1.0, 1.1, 2.0, 3.5; and etc.

Technologies: Client/Server; COM; DirectX; DirectX Media; BDE; HTML/DHTML; ActiveX; Java Servlets; DCOM; COM+; ADO; CORBA; .NET; Windows Forms; GDI/GDI+; and etc.

Application Skills: Databases - design and maintain, support, programming; GUI Design; System Programming, Security; Business Software Development. Win/Web Services development and etc.

Votes of 3 or less require a comment

 Search this forum Profile popups    Spacing RelaxedCompactTight   Noise Very HighHighMediumLowVery Low   Layout Open AllThread ViewNo JavascriptPreview   Per page 102550
 First Prev Next
 license? Corey W 30 May '08 - 3:08
 CRegistryEx::RefreshContents bizulk 14 Sep '05 - 6:08
 In the CRegistryEx::RefreshContents function you can read theses codes :  SetDefaults();  ... m_qSubKeys.push( strlwr( buffer ) ); ... varName = subKey + "\\" + strlwr( buffer );    Using the strlwr() function makes problem if you use key with uppercase characters. for exemple : "Password\PasswordLength" "Users\UserName" If you define defaults values with uppercase characters you will whave lowercase-duplicate-values in the cache. MoreOver, when you call Read/Write functions as SetValueByeName you don't use strlwr() so the key is added in the map as is, and if after that you call you'll read the key in lower case. This makes trouble when you make some string compares. I suggest deleting the use of strlwr(). I have made some tests after that it works well.:)   ------ Best Regards Bizulk   -- modified at 13:36 Friday 7th October, 2005 Original code of refreshcontents :  //varName = subKey + "\\" + strlwr( buffer ); varName = subKey + "\\" + buffer ; // 09/2005 - sli if( dwType == REG_SZ ) varData = (char *)databuf; else continue; m_mapValues[ varName ] = varData; dwIndex++; } while( 1 );  if there is a key of type REG_DW you will be in infinite loop, correction (ignore key):    //varName = subKey + "\\" + strlwr( buffer ); varName = subKey + "\\" + buffer ; // 09/2005 - sli if( dwType == REG_SZ ) { varData = (char *)databuf; m_mapValues[ varName ] = varData; } else { #ifdef _DEBUG std::string strErrorMsg; strErrorMsg = "There is a Key whith wrong type : " ; strErrorMsg = strErrorMsg + varName ; AfxMessageBox(strErrorMsg.c_str()) ; #endif }; dwIndex++; } while( 1 );  Sign In·View Thread·Permalink
 Re: CRegistryEx::RefreshContents [modified] alexn 24 Jul '06 - 15:20
 Kucherenko's class is good, but it'll go in an infinite loop even after your suggestions..   E.g. HKEY_LOCAL_MACHINE +Software +CodeProject +Unicode string array +Folder +Another string  This combination alone will take RefreshContents() for a good spin , I'll try to fix though..   -- modified at 21:21 Monday 24th July, 2006 Sign In·View Thread·Permalink
 I want to check whether or not a value exists in a specific key, I am having trouble checking it. I can the value to the key, but I can't read it for some reason. Here's the code snippet I'm having problems with:    int timeKey = 12345; char* whatHappened = "";   registry.GetValueByName("Key", whatHappened);   if ( strcmp(whatHappened,"") == 0) registry.SetValueByName("Key",timeKey); else { //... some code }   Basically, from what I know in the article, if "whatHappened" contains "" string, then the value in the key doesn't exist, so I go ahead an add it. Otherwise, if it does exist, then I do other stuff. Is there something wrong with this? I've wasted several hours on it, and I still don't have a way out. Please help.   Thanks. Sign In·View Thread·Permalink
 Re: Problem openning and reading keys Alex Kucherenko 11 Mar '04 - 21:50
 if key does not exists in map then will be returned new instance of std::string... it will be empty.   from MSDN about std::map<>::operator [] If the argument key value is not found, then it is inserted along with the default value of the data type.   if you want to check if key exists before you must write additional method which will check if internal map contains key or not... it requere one call of method find...   from MSDN: When using operator[] to insert elements, the returned reference does not indicate whether an insertion is changing a pre-existing element or creating a new one. The member functions find and insert can be used to determine whether an element with a specified key is already present before an insertion.    I'll recommend you to inherit Registry class or add into base class method that will check is key exists in internal map and use this method in code.   Good Luck Alex Kucherenko Sign In·View Thread·Permalink
 How to read Shell MRU Lists Anonymous 16 May '02 - 5:56
 Can you show how to read the MRULists that are stored by the shell?   For example, as stored in the following key:   HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSaveMRU\*   And also in this one:   HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs   Thanks Sign In·View Thread·Permalink
 Re: How to read Shell MRU Lists Alex Kucherenko 24 May '02 - 2:04
 You can do this only by class modification... I just only show idea not implementation.   That is why class support only REG_SZ datatype, all other are ignored... If you want to make thing better do it himself...   P.S.> By the way in future update of this article i make necessary changes to class...   Thanks for feedback Good Luck Alex Kucherenko Sign In·View Thread·Permalink
 Re: How to read Shell MRU Lists Alex Kucherenko 18 Jul '02 - 1:45
 Hi. You can use "%1s" template, but it's not safe for class. So use such printf formating templates with care.   Good Luck Sign In·View Thread·Permalink
 Last Visit: 31 Dec '99 - 18:00     Last Update: 19 May '13 - 19:38 Refresh 1