Download

reg.zip (15,350 Byte)

Why?

Some day I reached the point where I had to read and write a couple of values from and to the registry. So I had a look at the API functions and turned pale. I searched the net, hoping that someone had been nice enough to write a class around these monster functions and had updloaded it to some webpage. Someone had been nice enough. But one class I found wouldn't even compile, another one would write anything, a simple string or a bool value, as a binary value, and yet another one had a terrible interface. I had to write my own class.

I aimed for a class that would save you as much work as possible. After using it, your registry should contain human readable values, that a user could edit with REGEDIT.

Danger!

Fooling around with the registry may produce catastrophic results. In the worst case, you will have to reinstall your OS.

That's why:

Besides, I have to admit that I don't know the first thing about Windows NT or 2000. That's why I can't say whether CRegistry will work on these OS's.

Terminology:

The best way to get a grip on the terms used here, is to picture the view you get of the registry when you run REGEDIT. Anything on the tree on the left side are KEYS. On the right side you will find VALUES that are below the keys in the hierarchy.

News

March, 12th, 2003: I added two member functions. The first one is

ReadRaw (const CString& valName, CArray<unsigned char, unsigned char> *dataArray)

It will read just anything, regardless of type and put the result into an array of bytes. The second one is

DWORD CRegistry::GetValueType (const CString& value)

This one will return the value type (REG_SZ, REG_DWORD, etc) of the value you pass as an argument.

August, 25th, 2002: Two more bugs squashed. The current path of a CRegistry object was rarely updated and thus wrong on most occasions. This should work now. Furthermore, querrying GetPath() will now give you information about the current root key. The second bug made enumeration with EnumKeys() and EnumValues() a once-per-program-run thing. Fixed! (Static class members are evil, Static class members are evil, Static class members are evil, ...)

July, 3rd, 2002: I just discovered a gruesome bug. Why didn't anyone report this? CRegistry would write new values with the option REG_OPTION_VOLATILE. This led to new values that would be gone after the next boot on NT and heirs. Values are now written with the option REG_OPTION_NON_VOLATILE and thus they should survive a reboot or two.

July, 2nd, 2002: I updated the class and added two methods:
bool CRegistry::EnumKeys (CString &keyname) will enumerate the subkeys of the currently open key. Call it until it returns false. After each call that returned true the CString argument you provided will contain the name of one of the subkeys.
bool CRegistry::EnumValues (CString &valuename, int &type) will enumerate the values contained in the currently open key. Call it until it returns false to enumerate all existing values. Supply a CString and an int. After each call that returned true, these variables will contain the name of the value and its type, respectively. The MSDN documentation on RegEnumValue() lists the possible values for the type variable.

Examples:

First

I want to link files with the extension .ABC with my application. My application is "c:\prog\prog.exe", I want to label the files as "ProgDats":

First thing to do is create a CRegistry object. The constructor gets the key that has to be opened:

CRegistry reg (HKEY_CLASSES_ROOT);

Let's check whether the extension .ABC is already linked to some other program:

bool exists = reg.HasKey (".abc");

If it is, I open the key and retrieve the document type:

reg.Open (".abc");
CString type;
reg.Read ("", type);

Now that I have the type, I can have a check which application is responsible:

if (!type.IsEmpty())
   if (reg.Open (HKEY_CLASSES_ROOT, type))
   {
      reg.Open ("shell\\open\\command");
      CString app;
      reg.Read ("", app);
   }

Now I can query the user (at least I should), if I can override the link. If I get the permission, I leave the old document type as it was and set my own type to ".abc":

reg.Open (HKEY_CLASSES_ROOT, ".abc");
reg.Write ("", "ProgDats");

The last missing piece is the command to open a file of type "ProgDats":

reg.Open (HKEY_CLASSES_ROOT);
reg.CreateKey ("ProgDats\\shell\\open\\command");
reg.Write ("", "C:\\PROG\\PROG.EXE \"%1\"");

... and that should be it!

Second:

I want to create a key for my application under HKEY_CURRENT_USER/Software. I need that key to store a couple of values that my program should remember between sessions.

First, I create an object of type CRegistry:

    CRegistry reg (HKEY_CURRENT_USER, "Software");

I now should check whether there already is a key of that name, but I skip that here, and simply create the key:

    reg.CreateKey ("Prog");

I want to save the following values:

Those should do it for now, I just have to write the values:

reg.Write ("Position", place);
reg.Write ("Option", option);
reg.Write ("Files", mru);

That was easy. A view at the registry with REGEDIT should now look something like this:

  HKEY_CURRENT_USER
  Software
      ...
   Prog   (Standard)          (value not set)
          Files count         0x00000001 (1)
          Files 1             "C:\temp\test.abc"
          Option              "false"
          Position flags      0x00000002 (2)
          Position normalPos  "5, 5, 600, 460"
          Position ptMax      "-1, -1"
          Position ptMin      "0, 0"
          Position showCmd    0x00000003 (3)
      ...

Three lines of code thus produce lots of values in the registry. And you can read these values with the naked eye (==REGEDIT.EXE).

Reference

This is the list of all the public member functions, squeezed into categories. The categories are:

Constructing a CRegistry object

CRegistry ()
Default constructor. Only does some member initialization.
CRegistry (HKEY key)
Constructs an objects and opens the key (such as HKEY_CURRENT_USER)
CRegistry (HKEY key, const CString& path)
Construction and opening of a path within a given key

Copying and assigning

I have provided a copy constructor and operator= so you can pass CRegistry objects to functions and assign objects to each other.

Navigating

bool Open (HKEY key, const CString& path)
bool Open (HKEY key)
bool Open (const CString& path)
These will direct your existing object to a new position (or set the position for the first time if you used the default constructor).
CString GetPath ()
This one will give you a CString representation of your current position. E.g. you could end up with "HKEY_CURRENT_USER\Software\Microsoft\Windows"
Close ()
This will close the currently open key. You don't have to call this method as any call to open() and eventually the destructor will close any open handles. But for the orderly, I made the function public.

Dealing with keys

bool HasKey (const CString& path)
This one will tell you whether the currently open key has a subkey that goes by the name provided by path.
bool CreateKey (const CString& path, bool follow=true)
Will create a subkey of the current key and tell you whether this was successful or not. The name of the new subkey is taken from the path argument. The second argument will tell CRegistry whether you want your current key to be changed to the new key (the default) or whether you want to stay where you are.
bool DeleteKey (HKEY key, const CString& path)
You should be extremely careful when using this one. It will delete anything you tell it to delete. Using this is simple, recovering your windows installation after reg.DeleteKey (HKEY_LOCAL_MACHINE, "Software") is a little more tricky. So please don't do it.
HKEY GetKey ()
If you need to have a handle to your current key, just ask this method.
bool EnumKeys (CString& keyname)
With this member function you can enumerate all the subkeys of your current key:
while (reg.EnumKeys (cs)) cout << cs << endl;

Dealing with values

bool DeleteValue (const CString& name)
Will delete a value from the registry.
bool EnumValues (CString& valuename, int& type)
If you need to find out which values are stored under your current subkey, use this member function. It will set the value's name into the first argument and the value's type in the second argument. It will return true until you retrieved the last value:
while (reg.EnumValue (cs, i)) << cout cs << endl;
DWORD GetValueType (const CString& value)
If you want to find out which type of data are stored under a given name, pass the name to this function and it will do it's best to return the corresponding type (e.g. REG_SZ).

Writing values

bool Write (const CString& valName, const someType& value)
Write() is heavily overloaded and should suit most needs (at least if you are saving configuration data). "someType" can be:
int
This one is obvious: it will save an integer value. The name of the value will be the one provided by "valName". The type used for the actual registry value will be REG_DWORD
DWORD
Even more obvious, same as above.
CPoint&
This is where it gets interesting. You can save (and retrieve) CPoints by using this version. Afterwards, the registry will actually contain a string: "x, y".
CRect&
Similar to the CPoint version, this one will save a whole CRect. Again, CRegistry will use a string: "left, top, bottom, right".
bool
Should be useful for nearly anybody. Save the state of a boolean variable. Again, CRegistry will use a string. This time it will either contain the word "true" or "false".
CString&
Another pretty obvious one: Saves a string to the registry.
char*
Dito.
CStringArray&
This one will save a complete array of strings. Very handy for a recent file list, e.g. A couple of registry values will be created by this one. Suppose you provided the string "MRU" as the value name and a CStringArray of size two. What you will get is a value named "MRU count", type DWORD, value 2; a value named "MRU 1" with the first string, and a value named "MRU 2" with the second string.
WINDOWPLACEMENT&
To store the placement values of your application window, just pass the corresponding WINDOWPLACEMENT struct into this method. It will save flags, ptMaxPosition, ptMinPosition, rcNormalPosition, and showCmd.

Reading values

bool Read (const CString& valName, someType& value)
This method, too, is heavily overloaded: "someType" can be an int, a DWORD, a CPoint, a CRect, a bool, a CString, a CStringArray, and a WINDOWPLACEMENT structure. Of course, most of these can only be used if you used CRegistry to write the value or if you know that the value is stored in a compatible format. However, you can use the versions that use int and DWORD on values of type REG_DWORD and the CString version for REG_SZ and REG_EXPAND_SZ. If a value is of type REG_EXPAND_SZ, environment variables are indeed expanded by Read().
bool ReadRaw (const CString& valName, CArray<unsigned char, unsigned char> *dataArray)
If you need to have a more direct way of reading a value, use this function. It takes the value name as the first argument and a pointer to a CArray of unsigned chars as the second. It will read the value regardless of its type and store all the contents into the CArray. I thought that this would be quite nice, since you won't have to deal with the size of the data because CRegistry and CArray will do that for you.

License

The CRegistry code is freeware. Use it whichever way you like, but please leave my name in the header file. If you find CRegistry useful and actually use it, I would be happy to hear from you: Send me a little message. The same goes for problem or bug reports.