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

How to Use an XML Database as a Registry for Storing Information in a Hierarchical Structure

By , 14 Sep 2011
 
Example
          Application

Introduction

Basically, this library and example Windows Forms Application demonstrates how to use an XML database as a registry for storing information in a hierarchical structure.
It (presumably) works in a similar fashion to the Microsoft Windows Registry, which I often get frustrated interacting with all of the time.

Paths are separated by single forward slashes ('/'), and whether or not one specifies the root ('/Base'), the code will implicitly acknowledge the use of such a contraction.
SubKeys act like folders, whilst Keys are used to store textual information. An example of a registry path is as follows: "/Base/Registration/Credentials".

Using the Code

Creating a Key and Assigning it a Value

SetKeyValue(string Path, string Key, string Value, bool CreateSubKeyTree)

This method not only works as an instruction, but it also returns a boolean result indicating whether the attempt was successful or not - just in case error handling is important in one's application.

If a key is found to not exist, one is automatically created in its respective place.

Specifying the 'CreateSubKeyTree' variable as true means that if a certain branch of SubKeys is missing, i.e., you want to place a key in the path "/The/Quick/Brown/Fox" and only "/The/Quick" has been created thus far, the remaining SubKeys ".../Brown/Fox" will be created (and nothing already existing will be overwritten).

Retrieving a Value from a Key

GetKeyValue(string Path, string Key)

Similarly, the GetKeyValue method above also returns an evaluation of its success: a null value indicating a failure to find the specified key.
This also makes it a useful way of checking if a key already exists, although this is essentially redundant because instructions such as SetKeyValue create one under a given name if non-existent anyway.

Deleting a Key and its Value

DeleteKey(string Path, string Key, bool DeleteEmptySubKey)

This method follows the same intuitive format.
One can also specify 'DeleteEmptySubKey' as true to get rid of any empty SubKey left behind.

Loading a Database

A temporary database (titled a 'hive') is created as part of the code - and it only saves or loads data when instructed to.

ImportRegistryHive(string FilePath) loads a database into memory, after checking it for internal consistency.

If one wishes to check if a specified database is safe to use before carrying out a set of actions, simply use the following code:

if(RegistryFunctions.ImportRegistryHive(myPath))
{
    MessageBox.Show("Safe and Loaded");
    //...
}

else
{
    MessageBox.Show("Corrupted or Unavailable");
    //...
}

The contents of the file are loaded into a dummy database and evaluated before overwriting the one that is held in memory.

Saving a Database

Saving is almost identical to the above, as one calls the ExportRegistryHive(string FilePath) method.

This method, like the others, returns a boolean value indicating whether the attempt was successful or not.

If a specified file already exists, it becomes overwritten - which means that any obsolete or corrupted data is replaced with the more recent data.

Encryption

The library now contains methods for encrypting and decrypting SubKeys and their contents, as follows:

EncryptSubKeyPath(string SubKeyPath, bool IncludeParent, string EncryptionKey)
DecryptSubKeyPath(string SubKeyPath, bool IncludeParent, string EncryptionKey)

Triple DES cryptography is used because it is by far the easiest to implement.

To demonstrate how the encryption works, take the following XML code...

<?xml version="1.0"?>
<Base>
  <SubKey Name="Credentials">
    <SubKey Name="Profiles">
      <SubKey Name="NoName">
        <Entry Name="UserName" Content="something" />
        <Entry Name="Password" Content="something" />
      </SubKey>
    </SubKey>
  </SubKey>
  <SubKey Name="Window">
    <Entry Name="CurrentState" Content="Minimised" />
  </SubKey>
</Base

Now, if we call...

RegistryFunctions.EncryptSubKeyPath("/Credentials/Profiles", false, "lemons");

... we should get...

<?xml version="1.0" encoding="utf-16"?>
<Base>
  <SubKey Name="Credentials">
    <SubKey Name="Profiles">
      <SubKey Name="F048X3K3d94=">
        <Entry Name="pRfkO00g3pwBzJdw4zVn9g==" Content="SulCTPlr1KLvLh8++z20sg==" />
        <Entry Name="GuPl0IqW9SUBzJdw4zVn9g==" Content="SulCTPlr1KLvLh8++z20sg==" />
      </SubKey>
    </SubKey>
  </SubKey>
  <SubKey Name="Window">
    <Entry Name="CurrentState" Content="Minimised" />
  </SubKey>
</Base>      

As can be seen, the sensitive information has been rendered unreadable to anyone casually perusing the settings file.

This took a lot of hard work to implement, because I faced the challenge of having to find a way to run through the SubKey path recursively - from the greatest depth right up to the parent node.
However, I eventually accomplished this by using a Dictionary: associating a path with a number indicating how many SubKeys it was descended from.

Initialisation

A new, blank database is required to be initialised before it is able to be used - i.e. the XML Declaration and DocumentElement (root node) needs to be created.

To do this, one ought to call the InitialiseDocument() method.

To incorporate this method into a startup check to see if a database file exists, holding settings etc., one could use the following code:

if(!RegistryFunctions.ImportRegistryHive(myPath))
{
    MessageBox.Show("Settings file not found, or corrupt.");
    RegistryFunctions.InitialiseDocument();
}

Here, if there is failure ('!' meaning a false return value) whilst importing the saved database, a fresh temporary database is created in its place.

Storing and Loading from the Windows Registry

StoreWindowsRegistry(string hive, string path)
LoadWindowsRegistry(string hive, string path)
EraseWindowsRegistryStorage(string hive, string path)

Databases can now be saved to the Windows Registry in the form of Multi-String Values.

Simply specify the registry hive - i.e. "HKLM" or "HKEY_LOCAL_MACHINE" - and the path by using double backslashes - i.e. "test\\testing\\tested".

For example:

StoreWindowsRegistry("HKEY_CURRENT_USER", "Software\\Microsoft")

Other Methods

There are also a few other methods for interacting with the SubKey tree.

These are CreateSubKey, DeleteSubKey, CreateSubKeyTree, DeleteSubKeyPath, and RenameSubKeyByPath.

Also, I have created a method for eradicating empty SubKeys:

RemoveRedundancies()

The above feature, in partnership with the SetKeyValue method, eliminates obsolete SubKeys which don't contain any Key entries within them.

Any tamperings with the database - e.g. bad tag names - should automatically be removed.

To show the effects of the simplification, observe the following XML code...

<?xml version="1.0"?>
<Base>
  <SubKey Name="a">
    <SubKey Name="b">
      <SubKey Name="c">
        <SubKey Name="d">
          <SubKey Name="e" />
          <SubKey Name="x" />
          <SubKey Name="y">
            <SubKey Name="q" />
            <SubKey Name="r" />
            <BadBadBad/>
            <SubKey Name="s" />
          </SubKey>
          <SubKey Name="z" />
        </SubKey>
        <Entry Name="test" Content="test" />
	<BadBadBad/>
      </SubKey>
    </SubKey>
    <SubKey Name="c" />
  </SubKey>
</Base>

As you can see above, there are many paths which lead to nothing.

When, however, the RemoveRedundancies method is called upon, the whole tree is reduced to the following:

<?xml version="1.0"?>
<Base>
  <SubKey Name="a">
    <SubKey Name="b">
      <SubKey Name="c">
        <Entry Name="test" Content="test" />
      </SubKey>
    </SubKey>
  </SubKey>
</Base>

Performing this often will reduce the overall file size, and, of course, there is no perceivable need to leave empty SubKeys when all it takes to reinstate them is to set a key value.

Actual Examples

The following examples exhibit implementations of the methods contained within the RegistryFunctions library, in order to store and retrieve program settings.

String DefaultWindowState =
	RegistryFunctions.GetKeyValue("/Base/Defaults", "Window_State");
if (DefaultWindowState != null)
{
	if (DefaultWindowState == "Minimised")
	{
		//Start with a minimised window.
	}
	else if (DefaultWindowState == "Normal")
	{
		//Start with a normal window.
	}
	else if (DefaultWindowState == "Maximised")
	{
		//Start with a maximised window.
	}
	else
	{
		//Code to execute if an invalid window state is retrieved.
	}
}
else
{
	//No reference created yet, therefore assume defaults.
}

And also...

if(MinimisedWindowState_RadioButton.Checked)
{
	RegistryFunctions.SetKeyValue
		("/Base/Defaults", "Window_State", "Minimised", true);
}
else if(NormalWindowState_RadioButton.Checked)
{
	RegistryFunctions.SetKeyValue
		("/Base/Defaults", "Window_State", "Normal", true);
}
else if(MaximisedWindowState_RadioButton.Checked)
{
	RegistryFunctions.SetKeyValue
		("/Base/Defaults", "Window_State", "Maximised", true);
}

Updates

  • 1st version - Initial version
  • 2nd version - Added code to remove redundant SubKeys
  • 3rd version - Added Triple DES encryption
  • 4th version - Added storage to the Windows Registry

History

  • 20th August, 2011: Initial version
  • 22nd August, 2011: Article updated
  • 13th September, 2011: Article updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Samuel Philip Blackford
United Kingdom United Kingdom
Member
No Biography provided

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

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionExcellentmemberMike_2k13 Oct '11 - 0:11 

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 14 Sep 2011
Article Copyright 2011 by Samuel Philip Blackford
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid