Click here to Skip to main content
15,867,488 members
Articles / Programming Languages / C++
Article

Registry Wrapper Class (CRegistry)

Rate me:
Please Sign up or sign in to vote.
4.74/5 (72 votes)
2 Jan 200515 min read 211.9K   4.2K   95   64
An article and helper class for the Windows Registry.

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 );  // Same as passing just zero

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</CODE>);
REGENTRY_NONCONV_STORAGETYPE(<CODE>RECT</CODE>);

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:

// This assumes you loaded a key and have a multi-string value named "whatever".

if (regMyReg["whatever"].IsMultiString()) {

    // Enumerate each value in the string and display it
    for (size_t n = 0; n < regMyReg["whatever"].MultiCount(); n++) {
       ::MessageBox( NULL, regMyReg["whatever"].MultiGetAt(n), "Title", MB_OK);
    }

    // Clear the multi-string value and assign a new value
    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) {
      // This entry is stored as a string and has a value of "findme"
      *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) ) {
// Key does exist
 } else {
// Key does not exist
 }

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() ) {
   // Value does exist
} else {
   // Value does not exist
}

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) {

    /* First we construct our class, and because we don't
    want to create a key if it doesn't exist, we disable
    the CREG_CREATE flag: */     

    CRegistry regMyReg( NULL );  // No special flags

    /* Now attempt to open the key. */

    if ( regMyReg.Open("Software\\Microsoft\\Internet Explorer\\Main", 
                                                HKEY_CURRENT_USER) ) {

      // Display the current start page value
      ::MessageBox(NULL, regMyReg["Start Page"], "Current Value", MB_OK);

      // Now set the new value
      regMyReg["Start Page"] = "http://www.codeproject.com";

      // Display the new value
      ::MessageBox(NULL, regMyReg["Start Page"], "New Value", MB_OK);

      // Close the open key
      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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Well first of all, it is fairly obvious my name is Stuart Konen (I'm sure 50% of you just took notice), all of my life I've lived on a farm located in Northern Idaho. What shatters the stereotype of rural residence however, is the fact that I'm very active in the technology and programming worlds. I took up the hobby of programming at age 9, at that point it was little language known as Quick Basic *sigh*. Fast forward another 9 years... (Woah... I just realized that's half of my existence. But that's something I'll have to contemplate later, as I have an autobiography to tell).

Now my experience in programming has improved vastly, I've released various technologies and programs and I'm continuing to pump out concepts and systems that are getting glances from all over the world. My weapon of choice is C++, the core language of the majority of freshly released software, it's sleak, mean, and incrediably powerful. On the side I venture into web application development and site design, where my interest lies in PHP. Over the years my project have included everything from Artificial Intelligence to Web Statistic Tracking systems, but that's the past. What matters is the future... Remember that question we were always asked in grade school? Where did we see ourselves in 10 years. Well that question was asked about 8 years ago in my life, so it looks as though I only have two more for planning stages. In two years I see myself plunging into the world of research, creating my own Research and Development firm, aiming to meet the never-ending need for new and superior software and technology. Soon after becoming a multi-billionare I'll pursue my dream of world domination. Nobody is perfect...

Actually when it comes down to things, the money has no meaning. But there you have it, a 5 minute slice of my thoughts and time... If you have any job opportunities or have the slight urge to initiate a conversation with me, it can be done via email: skonen [at] gmail [dot] com

Comments and Discussions

 
GeneralRe: Just one problem! Pin
renchler24-Apr-06 6:37
renchler24-Apr-06 6:37 
GeneralRe: Article Inaccurate - MultiSz doesn't work Pin
Stuart Konen17-Dec-04 9:00
Stuart Konen17-Dec-04 9:00 
GeneralHigh Praise in Order Pin
Douglas R. Keesler17-Dec-04 12:23
Douglas R. Keesler17-Dec-04 12:23 
GeneralRe: Article Inaccurate - MultiSz doesn't work Pin
Blake Miller20-Dec-04 6:07
Blake Miller20-Dec-04 6:07 
GeneralGreat code; bloated article Pin
BlueComet20-Dec-04 9:33
BlueComet20-Dec-04 9:33 
GeneralArticle Inaccurate - MultiSz doesn't work Pin
Douglas R. Keesler17-Dec-04 7:38
Douglas R. Keesler17-Dec-04 7:38 
GeneralRe: Article Inaccurate - MultiSz doesn't work Pin
Douglas R. Keesler17-Dec-04 8:33
Douglas R. Keesler17-Dec-04 8:33 
QuestionHow can I create a new Registry Key??? Pin
Aaron Planell17-Dec-04 2:23
Aaron Planell17-Dec-04 2:23 
AnswerIt's very easy Pin
Stuart Konen17-Dec-04 6:53
Stuart Konen17-Dec-04 6:53 
GeneralRe: It's very easy Pin
Aaron Planell18-Dec-04 0:53
Aaron Planell18-Dec-04 0:53 
GeneralRe: It's very easy Pin
Aaron Planell18-Dec-04 1:44
Aaron Planell18-Dec-04 1:44 
GeneralRe: It's very easy Pin
Aaron Planell18-Dec-04 11:54
Aaron Planell18-Dec-04 11:54 
GeneralClass comparison Pin
David Crow3-Dec-04 6:11
David Crow3-Dec-04 6:11 
GeneralExcellent Class Pin
Jason Alexandra2-Dec-04 21:02
Jason Alexandra2-Dec-04 21:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.