Introduction
If you are a Windows Programmer or aspiring to be one, it is almost inevitable that at some point or another, you�re going to have to make use of the Windows Registry. When and if you reach this point, you�ll be greeted with some rather unattractive and confusing Win32 registry functions. Using these functions directly often becomes a tedious and irritating experience� So, to assist in the process of accessing and manipulating the registry, I�ve created a wrapper class which makes the task child's-play, so you can spend more time working on your project and less time fretting about how to make use of the registry. To make use of the CRegistry
class within your project, simply employ the preprocessor to include "registry.h".
Opening and Closing a Registry Key
Registry key paths are often composed and opened with two distinct path parts: the root and the subkey, these paths combine to form the final key path. To further understand how these paths take shape, we�ll do some hands on investigation� We�ll start by going to Start->Run and then typing "regedit" within the input box, then click the OK button. If all went well, you should be staring at Microsoft�s much appreciated "Registry Editor". On the left, you are presented with the key browser; to the right, the currently open key�s values are displayed. While root keys can be any key which contains a subkey, the lowest level of root key are, by default, those which begin with an all uppercase hkey_, such as HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and so on� Within these root keys are sub keys. Microsoft�s Internet Explorer, for example, stores a great amount of registry values within the subkey: "Software\Microsoft\Internet Explorer". This subkey is located within the root key of HKEY_LOCAL_MACHINE, which is the most commonly referred to root key. Now that you know the difference between the root key and the subkey, we�ll move on to actually opening the key using the CRegistry
class.
After declaring and constructing a new CRegistry
object, opening a key is an incredibly simple matter; all you have to do is call the CRegistry
class� "Open
" function, which returns true on success and false on failure. This function takes two parameters, one is required and the other is optional. The first parameter conveys the subkey path of the key to open and the second is a handle of the root key. If no secondary parameter is supplied, by default, the registry key handle for HKEY_LOCAL_MACHINE is used. Below is an example of how to open a registry key:
CRegistry regMyReg;
RegMyReg.Open( "Software\\MyProgram", HKEY_LOCAL_MACHINE );
But what if the key doesn�t already exist? When attempting to open a key that does not already exist, by default, it will be created. This is due to a certain flag being set during class construction, the flag in our crosshairs is CREG_CREATE
. Setting or revoking the flag toggles key creation, so if you�d like to turn automatic creation off, override the default constructor parameter value and remove the flag. When the CREG_CREATE
flag is enabled, you must have write access to the registry (some administrators restrict write access in user accounts). This is performed during the class declaration:
CRegistry regMyReg ( 0 & ~CREG_CREATE );
After opening a registry key and reaching the point where your program no longer needs to access the particular key, the open key must be closed. Closing a key only involves a call to the class� parameter-less "Close
" function. While directly calling Close()
is heavily recommended, it is not required; because the function is executed on class deconstruction. However, leaving any handle open for vast periods in between use is dangerous, so it should always be closed when not being used for an extended period of time.
When you�re ready to use a key temporarily closed with the "Close
" function, you must once again call the "Open
" function before reading or writing to it. This can become a hassle if you�re making excessive use of the registry throughout your program, so you can instruct CRegistry
to automatically open and close the registry key for you. While this method isn�t always as efficient, it does prove itself practical in the way of getting rid of potential mistakes that are associated with repeatedly calling "Close
". To utilize this feature, you must specify the CREG_AUTOOPEN
flag during the class construction. After doing so, you only need to open the key once, and needn�t worry about calling Close()
, as the key will be opened whenever it is accessed, and closed whenever the registry read/write operations are idle.
The last flag you need to know about, toggles value caching� When caching is disabled, values are read directly from the registry every time they are accessed, instead of being read only once then stored and accessed in memory. The flag that disables caching is CREG_NOCACHE
, and by default, it is not set (caching is enabled).
Working with Registry Values
Accessing values stored within a key is extremely simple and just as easy. To access a basic value within an open registry key, you simply use the array subscript operator just as you would with a common array. There is however one exception; instead of using a numerical value to represent an element�s current position in an array, you use the value�s name. For example, to access the basic registry value of "Last Used", the following would be used:
regMyReg["Last Used"]
You use this form of access for both setting and retrieving basic registry values. Meaning, you don�t have to memorize two expansive sets of functions to read and write basic values to the registry. But what is all of this talk about basic values? What else is there? Well, CRegistry
identifies two groups of registry values which consist of "the convertibles" (basic values) and the "unconvertibles". The following data types and collections are already designated convertibles by CRegistry
:
__int64
(64 bit integers)
bool
- character arrays (for example:
char *
)
double
int
std::string
unsigned int
unsigned long
(DWORD
)
So, what is a convertible? Well, other than being a popular class of car, it is a value which can be interchanged with any other convertible. Take the following code as an example, notice we�re setting the registry value as an integer and then retrieving it as a string:
char *szUsed;
int iUsed = 16;
regMyReg["Times Used"] = iUsed;
szUsed = regMyReg["Times Used"];
::MessageBox( NULL, szUsed, "Value of Times Used", MB_OK);
Such a conversion can be performed with any convertible, meaning you can convert string to integer, unsigned int
to double
, and so on. Now, with the convertibles explained, we�ll learn about the unconvertibles� Just as the name implies, unconvertibles are restricted to being set and retrieved using one sole data type/object. There are two ways to access these values. The first is to access it in the exact same way as you would using "convertibles" (using the array subscript operator); however, this only works for data types and objects which have been predetermined to be "unconvertibles". When you download the unmodified CRegistry
class, only two types are recognized: RECT
and POINT
. Of course, you may want to use other types as well� Adding them is easy, simply open "regentry.h", and you�ll see:
REGENTRY_NONCONV_STORAGETYPE(<CODE>POINT);
REGENTRY_NONCONV_STORAGETYPE(RECT
);
This implements the types so they can be used with the subscript operator. To add more non convertible types, just add a new line below the above, the line should be in the following form:
REGENTRY_NONCONV_STORAGETYPE( the data/storage type );
Note that non-convertible types implemented this way should have a fixed memory size to be stored correctly. POINT
and RECT
are safe as they contain no pointers or references to data stored in memory. As an example, we�ll implement the SYSTEMTIME
structure as an unconvertible.
REGENTRY_NONCONV_STORAGETYPE(SYSTEMTIME);
From there on, you�ll be able to use the SYSTEMTIME
structure along with the array subscript operators to access a registry value that is stored with the SYSTEMTIME
structure:
SYSTEM_TIME stTime, stSavedTime;
GetSystemTime(&stTime);
regMyReg["Time"] = stTime;
stSavedTime = RegMyReg["Time"];
In the example above, the current system time is read into stTime
by calling GetSystemTime
, then stTime
is saved to the registry value named "Time" within the open key. Afterwards, the value is taken from the registry key and stored in the variable stSavedTime
, so the outcome of this example is: the two variables (stTime
and stTime
) will be equal. If you don�t want to directly implement a data type or object as an unconvertible via "regentry.h", then you may wish to use the ".WriteStruct
" and ".ReadStruct
" functions. However, doing so may become a nuisance as you must directly set and get the registry value using separate function calls rather than the array subscript operators:
SYSTEM_TIME stTime, stSavedTime;
GetSystemTime(&stTime);
regMyReg["Time"].SetStruct(stTime);
regMyReg["Time"].GetStruct(stSavedTime);
If you need to have more control over storing binary values, such as those which are not a fixed memory size, then you�ll want to use the ".SetBinary
" function to do so. The function takes two parameters, the first being the binary data, and the second being the size (in bytes) of the data being stored. To retrieve the value, you then call the ".GetBinary
" whose parameters are just the opposite: the pointer to the destination variable and the maximum number of bytes to read. If you need to know the size of the binary value (allocating a buffer), call ".GetBinaryLength
" as it will return the appropriate value.
Working with REG_MULTI_SZ (multi-string) values
In addition to storing single null-terminated strings, the Windows registry can also store string arrays, which are REG_MULTI_SZ
values. String arrays are double null-terminated strings consisting of single null-terminated strings, thus making up an "array" of strings� Being that, such values will probably only appeal to more experienced programmers; I won�t go into great deal about them. Within the source, I have commented multi-string related functions greatly, so you should be able to figure it out from there (they aren�t hard to comprehend).
Multi-string related functions are all named in the form of Multi* and *Multi, such as "MultiGetAt
" and "GetMulti
". Below is a list of the functions and their rather diminutive descriptions.
GetMulti
- Copies and returns the full string including all null-terminators.
SetMulti
- Just the opposite of the above, SetMulti
sets the multi-string value.
MultiAdd
- Adds a string to the end of the array.
MultiGetAt
- Returns a constant pointer to the string located at the passed index.
MultiSetAt
- Alters the value of the string located at the passed index.
MultiRemoveAt
- Removes the string located at the passed index.
MultiClear
- Removes and "clears" all the strings in the array.
MultiLength
- Returns the character length of the full string (including null terminators).
MultiCount
- Returns the number of strings located within the array.
To further add to your knowledge, here is a very simple example which displays each string of a multi-string value in a message box and then sets it with a new value:
if (regMyReg["whatever"].IsMultiString()) {
for (size_t n = 0; n < regMyReg["whatever"].MultiCount(); n++) {
::MessageBox( NULL, regMyReg["whatever"].MultiGetAt(n), "Title", MB_OK);
}
regMyReg["whatever"].MultiClear();
RegMyReg["whatever"].MultiAdd("String One");
RegMyReg["whatever"].MultiAdd("String Two");
RegMyReg["whatever"].MultiAdd("String Three");
}
Deleting Registry Values and Keys
Most likely, you�ll reach a point where you need to permanently delete a value or even a key from the registry. The process of deleting a value takes one function call and it comes in the form of ".Delete
", it is a parameter less function which performs the task of deleting the corresponding registry value from the open key. To delete a value named "Time", you would use something similar to the following:
regMyReg["Time"].Delete();
If you�re looking to delete an entire key instead, be extremely careful� You most certainly don�t want to unintentionally delete an important key. So, I implore you to use the greatest amount of caution when using "DeleteKey
", similar to deleting a value. The function takes no parameters and is irreversible. Deleting a key will result in the deletion of all values and sub keys it may hold, and when the process is complete, the open key will be closed and no longer accessible until it is once again created. Once again, here is an example for further clarification:
regMyReg.DeleteKey();
As with all examples within this article, the above is appropriate on the assumption that regMyReg
is an open key.
Refreshing Registry Keys
Note: Keys are automatically refreshed if the CREG_NOCACHE
flag was set when you constructed the class.
Because registry values can be stored in memory after opening a key, they are only transferred from the open registry key to the CRegistry
class once. For nearly all situations, this type of loading will not effect the manipulation of registry values at all. However, if you rely on another program which changes the values while you are reading them, then the result may be problematic. Instead of accessing the new value rendered by an active external program, you�ll still be accessing the values based on the initial values that were loaded while the key was being opened. Such a problem is easily solved; any time you want to refresh the values stored in memory (thinking the values may have been altered externally), you may effortlessly call Registry�s "Refresh
" function:
RegMyReg.Refresh();
Note that after setting a registry value internally, there is no need to refresh the key as the new value is properly stored in memory.
Enumerating Registry Values
Enumerating values via direct use of the Microsoft�s Registry API often turns into a more complicated task than it needs to be. CRegistry
makes the procedure easy. The underlying reason for this is that to perform registry enumeration on an open key, you only need to call two simple functions. The functions at hand are "GetAt
" and "Count
". "Count
" returns the number of registry entries currently stored in the object, and "GetAt
" returns a pointer to the registry entry stored at the passed index. Rather than explaining these very comprehensible functions, I�ll just give an example:
size_t nValueCount = regMyReg.Count();
CRegEntry *reEntry;
for ( size_t n = 0; n < nValueCount; n++) {
reEntry = regMyReg.GetAt(n);
if (reEntry.IsString() && stricmp(*reEntry, "findme") == 0) {
*reEntry = "new value";
}
}
In the above example, we enumerated through all the values, and if a value was a string and had a value of "findme", we changed the value to "new value". You�re also introduced to the "IsString
" function, which obviously returns true if the value is stored as a string. Naturally, we used this because we�re searching for a non-numerical string, although it isn�t required. In addition to checking whether the value is stored as a string, you can also check for binary ("IsBinary
"), double null-terminated string arrays ("IsMultiString
"), and DWORD
("IsDWORD
") registry values.
Checking Existence of Keys and Values
If your program relies on the value of a key and you don�t verify if the key or value exists before trying to read it, the results can ultimately be catastrophic. To battle such a feat, you have several functions in your arsenal to check the existence of keys, sub-keys, and values in the registry.
We�ll start off by exploring the function that allows you to find whether or not a key exists, the function is "KeyExists
" and it has two parameters. The first parameter is the location of the sub-key to open, the second is the handle to the root key. Joyously, the function can be called statically, so there is no need to declare a class variable. Here is an example that checks whether the sub-key: "Software\EA Games" exists within the HKEY_LOCAL_MACHINE root key:
if ( CRegistry::KeyExists( "Software\\EA Games", HKEY_LOCAL_MACHINE) ) {
} else {
}
Seems simple enough, doesn�t it? But what if you have an open key and want to verify a certain sub-key located within the key exists? In such a case, you�d use "SubKeyExists
". The function takes one lonely parameter, and it�s the path to the sub-key relative to the open key. So, if you�ve opened a key and want to check whether it contains the sub-key "Options\\Network", you�d pass "Options\\Network" as the parameter. Just like "KeyExists
", the return value is false if the key does not exist and true if it does.
Surely, you�re getting the hang of how to use the existence checking functions by now. But there is just one more function to check out and it�s used for checking the existence of values in an open key. Unlike the previously described functions, ".Exists
" is a member of the registry entry class, so calling the function takes place in a form similar to the following:
if ( regMyReg["value name"].Exists() ) {
} else {
}
Together, these three functions can be used to create a powerful verification technique, ensuring you�re not trying to access non-existent keys and values your program depends on. It�s also an excellent way to check whether certain software is installed on the user�s system�
Additional Notes
When retrieving an STL string "std::string
", explicit casting is required:
strMyString = (std::string)regMyReg["something"];
To access a key�s "default" value, you just pass a zero-length string as the key name. While this value key is rarely ever used, it is important to know how to access it:
regMyReg[""]
Start to Finish Example
If you�ve had any trouble following this article and how to use CRegistry
, perhaps this very straight forward example will help sanitize your mind of conflicting thoughts. While this example has no real applicable use, it does provide a clear picture of what is going on. Basically, it just reads the registry value containing Internet Explorer�s current start page, displays it in a message box, then sets it to http://www.codeproject.com, and once again displays the value in a message box:
#include "Registry.h"
int _stdcall WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
CRegistry regMyReg( NULL );
if ( regMyReg.Open("Software\\Microsoft\\Internet Explorer\\Main",
HKEY_CURRENT_USER) ) {
::MessageBox(NULL, regMyReg["Start Page"], "Current Value", MB_OK);
regMyReg["Start Page"] = "http://www.codeproject.com";
::MessageBox(NULL, regMyReg["Start Page"], "New Value", MB_OK);
regMyReg.Close();
} else {
::MessageBox(NULL, "Unable to open key!", "Error", MB_OK | MB_ICONHAND);
}
return 0;
}
In a Nutshell
So in a nutshell� If you�re moaning and groaning over using the registry, groan no more! CRegistry
makes the job easy, and as I�ve been stressing all along� simple. If you have any questions/comments or just want someone to scream at, please don�t hesitate to email me. I�d like to thank Blake Miller for giving me feedback on this class, it was greatly appreciated. But enough of the thankfulness, go out and take a swim into the Registry with help from CRegistry
!
History
- December 2nd, 2004: Submitted article to The Code Project.
- December 17th, 2004: Fixed wrongly named
MULTI_SZ
functions in article, see "Inaccurate - MultiSz doesn't work" comment below.
- December 31st, 2004: Fixed a small memory leak and also fixed a problem with reading
DWORD
values.