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.
Advantages
The class gives easy to use access to the system registry and simplifies most common tasks.
The class gives a great performance boost when accessing registry data often.
Class gives easy access to history data.
There is also simple error detection.
Disadvantage
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.GetValueByName( "value-Name", strValue );
registry.GetValueByName( "stldbg_optdlg\\autoexpand", strValue );
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 )
Into code must be added:
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.
User can get access to values using functions:
- 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 )
{
m_mapValues[ "\\limit" ] = "20";
m_mapValues[ "\\matchcase" ] = "0";
m_mapValues[ "\\regexp" ] = "0";
m_mapValues[ "\\up" ] = "0";
m_mapValues[ "keywords_optdlg\\extensions" ] = ".cpp;.hpp;.hxx;.cxx;.c;.h;";
m_mapValues[ "search\\search1" ] = "// NOTE: ";
m_mapValues[ "search\\text1" ] = "Carefully read notes about code";
m_mapValues[ "search\\search2" ] = "// TEST: ";
m_mapValues[ "search\\text2" ] = "Carefully read test comments";
m_mapValues[ "sorting_optdlg\\parsetime" ] = "10000";
m_mapValues[ "sorting_optdlg\\showtime" ] = "20000";
m_mapValues[ "stldbgdlg\\autoexpand" ] = "400";
m_mapValues[ "stldbgdlg\\limit" ] = "20";
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:
CRegistryDemo registry( HKEY_CURRENT_USER, "Software\\RegistryEx Test" );
try
{
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();
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.