Click here to Skip to main content
Click here to Skip to main content

A handy class to make use of Windows Registry

By , 15 Aug 2004
 

Sources update

The sources have been updated. (Hopefully) This will make them compilable on VC 7+. I couldn't test it with VC 7, but I expect it will be OK now. For more info take a look what was the problem.

New Additions

This class has been modified:

  • Improved internal logic.
  • Improved support for writing structured data to registry.
  • Added possibility to see internal execution flow (e.g., when data is read from and written to the registry).
  • Added support for HKEY_LOCAL_MACHINE, HKEY_USERS, and all the others...

In short, in order to store a structure in registry, you only need to do like this:

SomeStruct X;
//set struct values...
reg["SomeStructData"].set_struct(X);

In order to read back the stored structure:

SomeStruct X;
//set struct values...
if(reg["SomeStructData"].get_struct(X)){
    //Ok, X contains the read data...
}else {
    //error reading struct.
}

In order to use HKEY_LOCAL_MACHINE or other, you can pass a second parameter of type registry::Key to the registry constructor:

registry reg("Sowtware\\Whatever",registry::hkey_local_machine);

The second parameter is optional, it defaults to registry::hkey_current_user.

In order to see internal execution flow, you may #define WANT_TRACE. And in console mode, you'll see when and what objects are created, and when values read to/ written from the registry. There was added a new project with_trace to see that.

//with trace

#define WANT_TRACE //define this to be able to see internal details...

#include "registry.h"
#include "string.h"
#include <STRING>

struct Something{
    int area;
    float height;
};

int main(int, char*)
{
    registry reg("Software\\Company");
    {
        Something X;
        X.area=100;
        X.height=4.5;

        reg["Something"].set_struct(X);
    }
    Something Z;
    if(reg["Something"].get_struct(Z)){
        printf("area is: %i\nheight is: %f\n", Z.area,Z.height);
    }

    return 0;
}

It produces the following output:

with_trace output

Introduction

First of all, you need only one file registry.h, that's included in the source distribution. Read on...

//Dirty and fast howto

#include "registry.h"
#include "string.h"

int main(int, char*){

    registry reg("Software\\Company");
    reg["Language"]="English";        //set value
    reg["Width"]=50;
    printf("The address is: %s\n",(char*)reg["Address"]); 
    //prints nothing unless you put something in
    return 0;
}

A more descriptive howto:

//Long story:

#include "registry.h"
#include "string.h"

struct ServerAddress{
    char name[64];
    char ip[15];
    short port;
};

int main(int,char*){

    //first step is to create our home registry 
    //record(or open it if exists)
    //the constructor accepts a string and if you do:
    registry settings("Software\\MyCoolApp");
    //you open HKEY_CURRENT_USER\Sowtware\MyCoolApp

    //Due to simplicity this class is desined to deal with only 
    //HKEY_CURRENT_USER registry group. 
    //You may, however, modify this behavior if you need

    //To make sure that the home key has been created or 
    //opened you can test it like this:

    if(!settings){
        //error code goes here...
        exit(1);
    }

    //The next step is to create (or open if exists) 
    //a value key:
    //the official way is like this: (for another, 
    //simpler way, read below.)

    registry::iterator lang=settings["Language"];    
    //Personally I don't use it this way - too much to type...

    //and then you can retrive value of "Language" key 
    //as follows:

    printf("The selected language is: \"%s\"\n",(char*)lang);

    //if you want to modify a value you can do simply like this:

    lang="Russian";

    //Internally this class stores values as 
    //binary data and therefore 
    //you are not limited to strings only.

    // Example:

    ServerAddress MyCoolServer;
    strcpy(MyCoolServer.name,"somewhere.example.com");
    strcpy(MyCoolServer.ip,"192.168.0.1");
    MyCoolServer.port=1024;

    //now let's put this record into registry key "server"
    //it can be simply done like this:

    registry::iterator server=settings["server"];
    server=string((const char *)(&MyCoolServer),sizeof(MyCoolServer));

    //now let's test by retriving the structure back:

    ServerAddress serAdr;
    const string &FromRegistry=server;
    //copy data only if it's of the size of the struct ServerAddress
    if(FromRegistry.length()==sizeof(ServerAddress)){        
        memcpy(&serAdr,FromRegistry.c_str(), sizeof(ServerAddress));
        //now let's see what we have (see a pic below):
        printf("Print out for my server:\n");
        printf("\tserver's host name is:  %s\n",    serAdr.name);
        printf("\tserver's ip address is: %s\n",    serAdr.ip);
        printf("\tserver's port is:       %u\n\n",  serAdr.port);
    }else{
        //error code goes here...
    }
    return 0;
}

Sample Image - long_story.jpg

Pic.1 Output from a more descriptive howto.

I coded these few lines with simplicity in mind. I like the style, when I can assign value to a registry key as to a usual variable. I will describe shortly how I use this code in my projects. I put a member variable reg (instance of registry) to my app's class (there is a reason, read below) that needs access to the registry. Then, in the class' constructor, I instantiate reg variable to the registry home key. Since there is no default constructor in the registry class, you may do it like this:

class my_app{
protected:
    registry reg;
    string UserName;
public:
    my_app() : reg("Software\\my_app"){ // <--
        UserName = reg["UserName"]; //read value
    }
    ~my_app(){
        //store value back to the registry
        reg["UserName"] = UserName;
    }
}

In the preceding line, you actually don't need to create a separate string for UserName - you may have a member variable of registry::iterator type, that will be automatically stored to the registry when your class is destructed (see the final example at the bottom of the page).

And then, whenever you need, you can do like this:

SetLanguage(reg["Lang"]); 
string UserName=reg["User"];

that's really simple...

Background

Now, a little background on how it works internally. This is actually a very lightweight class. It makes less API calls than you would probably do.

For example, the line:

reg["Something"]="nothing";

will only once physically access registry on destruction of the returned key-value to assign value "nothing" to key "something".

Same happens with the next lines (only one API call when the group goes out of scope):

registry::iterator name=settings["UserName"];
name="John";
name="Alex";
name="Someone Else";

Basically, if you intend to use this class, you should be aware that statement reg["something"]="nothing"; doesn't modify anything in registry by itself. The value will be set only when the statement goes out of scope, like this:

{
    reg["UserName"]="Bill";
    printf("The username is: %s\n", (char*)reg["UserName"]); 
    //!!WILL NOT PRINT BILL, 
    //it will print the value that was before the previous line
}//at this point the value Bill is in the registry
printf("The username is: %s\n", (char*)reg["UserName"]); //now it prints Bill;

In order to force it to commit to the registry at some point, you can use flush() method like this:

registry::iterator name=settings["UserName"];
name="John";
name.flush(); //at this point it will write value "John" to registry.

The same applies to reading values - they are retrieved only when you request them, and only once! So, if you know (or think) that something else (perhaps Earth's magnetic field :)) has modified a key's value, you may use refresh() method. But, be carfull!! Anything you have assigned before will not be committed (it's overwritten by the returned value from registry).

And a very important point. When the instance of registry is destructed, or goes out of scope, then all the values you created from it will not have access to the registry. That is, all iterators on a given registry instance should be destructed before their parent registry instance. That's why I use this class as a member of another class. When the parent class is destroyed, it doesn't need access to the registry, neither is it possible at that point.

That's it. Hope it will be useful to someone.

A better example

#include "registry.h"
#include <iostream>


class my_app{
protected:
    registry reg;
    registry::iterator UserName;
public:
    my_app():reg("Software\\Company"),UserName(reg["UserName"]){}
    ~my_app(){}
    void SetUserName(const char * name){
        UserName = name;
    }
    const char * GetUserName(){
        return (const char *) UserName;
    }
};

int main (int, char *){
    my_app App;
    App.SetUserName("Alex");
    cout<<"The username is: "<<App.GetUserName()
        <<endl<<"Thanks for playing..."<<endl;
    return 0;
}

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

About the Author

__PPS__
Canada Canada
Member
Big Grin | :-D

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalnumber of values in a keymemberMbarki25 Apr '07 - 23:33 
I need to know how many values in a key, but i dont find a method to do that.
Thanks
 
Sami
GeneralRe: number of values in a keymember__PPS__27 Apr '07 - 12:20 
this class doesn't have means to iterate/enumerate values in a key. It's just to get/set value of a registry entry
GeneralSource does not work with STLPORTmemberRalfGriggel27 Nov '06 - 4:39 
Problem:
If a registryentry is not available and you run the class the first time the resulting string contains: "\0\0\0\0". As long as you going to interpret std::string as c-string it works fine. But if you use std::string you leave the pathes of the c-string semantics. In fact every std::string does not nessesary mean that it could not handle 0-bytes as part of strings.
So, e.g. if you put an std::string to "\0\0\0\0" as you did in your "Value()" implementation it will put the string to exactly "\0\0\0\0" which is not empty nor "" or whatever.
Actually you did this as the defaultvalue for an entry which is not found in the registry.
Running this small exampl whith STLPORT brings up the error:
 
registry DllReg("SOFTWARE\\MyApp",registry::hkey_local_machine);
string ParRet = "";
bool IsEmpty = ParRet.empty(); // first item
ParRet = (string) DllReg["MySettings"];
IsEmpty = ParRet.empty(); // second item
if(IsEmpty == false)
{
cout << "this string has to be empty !!!";
}

 
On the first item, the string is empty, but on the second item not.
 
The Problem with the implementation of the std::string template class in STLPORT is that it really do it's job. So as long as your sourcecode things in the old c-string way (0-terminated) it won't work at all.
What you have to do to fix it is a small change in the GetValue() method. In the case where the result of "RegQueryValueEx" is not "ERROR_SUCCESS" you have to assing the "_v" varialbe with a new string.
By the way, there is maybe a better way, but i leave it to you to find it.
 
STLPORT could be found under "www.stlport.com".
 
Ralf Griggel

GeneralRe: Source does not work with STLPORTmember__PPS__27 Nov '06 - 5:57 
This class has many problems like this - I'm aware of this. I wrote it years ago and didn't have time to update the code. This class allows you to put basic data, that's it. You may hack the code to add extra functions to test if the value retrived was in the registry or not etc. Or, as you say if RegQueryValueEx's return is tested then it works correctly then just use it like this. I only hope i'll have some time one day to put in this article/class to finish it
Generalsources not working on restricted usermemberdimmudimmu17 Mar '06 - 0:51 
Hi,
 
I tested the code, but it doesn't seem to work on limited/restricted user accounts on WinXP and Win2000. The reg["..."] returna an empty string. The key is read from Local Machine and it works fine on Admin accounts.
 
Any suggestions?
 
Thanks
GeneralRe: sources not working on restricted usermember__PPS__17 Mar '06 - 4:32 
I don't even remember how it works, it's been a long time as I wrote this piece of code. However, as far as I remember

registry reg("Sowtware\\Whatever");

 
will open current_user and not local machine, so it's supposed to work this way. Once you need to read values from local_machine then they will not be read if they have any restrictions, since in this implementation the simplest way is used - every key is open with KEY_ALL_ACCESS flag. And the key cannot be opened with this flag for restricted keys.
 
One possible solution would be to write const and non-const protected functions, so that const versions open keys in read_only mode and do not try to modify them and then for read only you would need to:

const registry reg("Sowtware\\Whatever", hkey_local_machine);

 
which then would had to use const function that would open keys in read only mode... You would need to play with mutable data and const function to make things work correctly. As I don't remember how everything works I don't want to modify it, you may try to do it, it's very easy task.
GeneralUNICODE supportmemberCrystalPaul8 Dec '05 - 17:29 
__PPS__,
 
If you want to support UNICODE users (which I am), you need to replace the standard registry functions with ANSI-specific versions:
 
RegCreateKeyEx with RegCreateKeyExA
RegQueryValueEx with RegQueryValueExA
RegSetValueEx with RegSetValueExA
 
This way it will compile without errors even if UNICODE support is turned on. Should also work with UNICODE off.
 
To convert character strings to wide strings (and vice versa), use the CT2A and CA2T macros. you can also use the CStringA class if you want to retrieve a value that doesn't need to be converted to wide format.
 
Note that if the registry contains wide strings, you will lose that data when retriving it using this code, so use with caution if you might be accessing a registry value that contains UTF-16 characters (you'll lose 'em).

Enjoy,

 
Paul
 
-- modified at 23:29 Thursday 8th December, 2005
GeneralRe: UNICODE supportmember__PPS__8 Dec '05 - 22:32 
CrystalPaul wrote:
If you want to support UNICODE users (which I am), you need to replace the standard registry functions with ANSI-specific versions:
 
RegCreateKeyEx with RegCreateKeyExA
RegQueryValueEx with RegQueryValueExA
RegSetValueEx with RegSetValueExA
 
This way it will compile without errors even if UNICODE support is turned on. Should also work with UNICODE off.

 
Hi Paul,
From the beginning I didn't understand what you mean - I almost never program for windows, so I forgot quite alot now.
You are absolutely right, if unicode is enabled, then there will be expected wide strings instead of 8-byte strings and code will not compile.
 

CrystalPaul wrote:
Note that if the registry contains wide strings, you will lose that data when retriving it using this code, so use with caution if you might be accessing a registry value that contains UTF-16 characters (you'll lose 'em).

 
There will be no such problem, for now this code will simply not compile with unicode enabled Smile | :) Still, windows docs say that query value returns size in bytes, so there should be no losses, other thing is that my code stores all values as binary data, and therefore, all is needed is to replace RegCreateKeyEx with RegCreateKeyExA and others and that's it (but user will need to convert unicode strings to utf-8 in order to store them as const char * strings, or do some other tricks.
 

anyways, I will not do updates for now - have no free time. Mayby some time later. Someone has already pointed out problems with unicode, but I didn't really understood what was the problem, now from your msg it's clear to me.
thanks for your input.
GeneralSetting default value on first use.memberneticous13 Jun '05 - 19:31 
I would like to know how you would set a default value.
As an example I would like the default for the Alpha below to be 255 not 0.
When the app first runs the key/value is created and it is defaulting to 0.
Any thoughts?
 
registry settings("Software\\Company\\Colors");
if(!settings)
return;
 
registry::iterator Red = settings["Red"];
registry::iterator Green = settings["Green"];
registry::iterator Blue = settings["Blue"];
 
registry::iterator Alpha = settings["Alpha"];
 
int m_Alpha = Alpha; // m_Alpha is always 0 if the registry value does not exist.
 
Very nice.
 

GeneralRe: Setting default value on first use.memberneticous14 Jun '05 - 3:01 
Solved:
I made the DWORD disp public so I can test the result like this.
 
registry settings("Software\\Company\\FillColor");
if(!settings)
return;
 
if (settings.disp == REG_CREATED_NEW_KEY)
bSetDefalts = true;
 
.
.
.
.
 
The change is below.
 

struct registry
{
private:
const registry & operator=(const registry & r);
registry(){};
protected:
HKEY _R;
public:
enum Key{
hkey_classes_root=0,
hkey_current_config=1,
hkey_current_user=2,
hkey_local_machine=3,
hkey_users=4,
hkey_dyn_data=5
};
//>>>>>>> disp is made public
DWORD disp;
//>>>>>>>
protected:
void create_key(const char *name, Key key){
HKEY _key;
switch(key){
case hkey_classes_root: _key = HKEY_CLASSES_ROOT; break;
case hkey_current_config: _key = HKEY_CURRENT_CONFIG; break;
case hkey_local_machine: _key = HKEY_LOCAL_MACHINE; break;
case hkey_users: _key = HKEY_USERS; break;
case hkey_dyn_data: _key = HKEY_DYN_DATA; break;
case hkey_current_user:
default: _key = HKEY_CURRENT_USER; break;
}
 
//>>>>>>> disp was here
// DWORD disp;
//>>>>>>>
REG_TRACE(printf("REG_TRACE:\tconstructing registry for \"%s\" ... ",name));
if(ERROR_SUCCESS!=RegCreateKeyEx(_key,name,NULL,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&_R,&disp)){
REG_TRACE(printf("Failed!\n"));
_R=0;
}else{
REG_TRACE(printf("OK\n"));
}
//(disp!=REG_CREATED_NEW_KEY)? existing key: new key;
}
.
.
.
.

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 16 Aug 2004
Article Copyright 2004 by __PPS__
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid