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...
="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...
="1.0" ="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...
="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:
="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")
{
}
else if (DefaultWindowState == "Normal")
{
}
else if (DefaultWindowState == "Maximised")
{
}
else
{
}
}
else
{
}
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