Here's a very good registry implementation that mirrors the full set in .NET. Credit goes to Elia Karagiannis.
// This code was developed by Elia Karagiannis of LeadingEdgeSolutions L.L.C.
// Feel free to use this code however you may please but keep these comments as part of the code.
// Thanks go out to Chris Tacke (with help from Neil Cowburn)
using System;
using System.Runtime.InteropServices;
using System.Collections;
namespace LeadingEdgeSolutions
{
namespace Utilities
{
/// <summary>
/// Represents a key level node in the Windows registry. This class is a registry encapsulation.
/// </summary>
public sealed class RegistryKey : IDisposable
{
private bool m_bReadOnly = true;
private uint m_uiHKey = 0;
private string m_sName = null;
/// <summary>
/// This method should only be called by the Registry class to set up the root keys!
/// </summary>
/// <param name="rootKey"></param>
internal RegistryKey(uint rootKey, string rootKeyName)
{
m_uiHKey = RegistryImpl.OpenKey(rootKey, "");
m_bReadOnly = true;
m_sName = rootKeyName;
}
internal RegistryKey(uint rootKey, string rootKeyName, string subkey)
{
RegistryImpl.KeyDisposition disp = 0;
m_uiHKey = RegistryImpl.CreateKey(rootKey, subkey, null, ref disp);
m_bReadOnly = false;
m_sName = rootKeyName + "\\" + subkey;
}
/// <summary>
/// Closes the key and flushes it to disk if the contents have been modified.
/// </summary>
~RegistryKey()
{
Dispose();
}
/// <summary>
/// Closes the key and flushes it to disk if the contents have been modified.
/// </summary>
public void Close()
{
if(this.m_uiHKey != 0)
{
RegistryImpl.RegCloseKey(m_uiHKey);
m_uiHKey = 0;
}
}
/// <summary>
/// Creates a new subkey or opens an existing subkey. The string subKey is not case-sensitive.
/// </summary>
/// <param name="subkey">Name or path of subkey to create or open. </param>
/// <returns>Returns the subkey, or null if the operation failed.</returns>
public RegistryKey CreateSubKey(string subkey)
{
AssertIsOpen();
return new RegistryKey(this.m_uiHKey, this.Name, subkey);
}
/// <summary>
/// Deletes the specified subkey. The string subkey is not case-sensitive.
/// </summary>
/// <param name="subkey">Name of the subkey to delete.</param>
/// <param name="throwOnMissing">Indicates whether an exception should be raised
/// if the specified subkey cannot be found. If this argument is true and the
/// specified subkey does not exist then an exception is raised. If this argument
/// is false and the specified subkey does not exist, then no action is taken
/// </param>
public void DeleteSubKey(string subkey, bool throwOnMissing)
{
AssertIsOpen();
// make sure we are not read only
if(m_bReadOnly)
throw new UnauthorizedAccessException("The subkey cannot be deleted. The registry key was opened as read only");
// do the check to see if the key actually exists
uint retKey = 0;
int retVal = RegistryImpl.RegOpenKeyEx(this.m_uiHKey, subkey, 0, 0, ref retKey);
if(retVal != 0)
{
if(throwOnMissing)
throw new Exception("The subkey " + subkey + " cannot be deleted because it is missing.");
return;
}
// check and see if the subkey to delete has any subkeys of its own
char[] chNull = null;
int iNull = 0;
int cSubKey = 1;
RegistryImpl.RegQueryInfoKey(retKey, chNull, ref iNull, 0, ref cSubKey, ref iNull, ref iNull, ref iNull,
ref iNull, ref iNull, 0, 0);
if(cSubKey != 0)
{
RegistryImpl.RegCloseKey(retKey);
throw new InvalidOperationException("The specified subkey has child keys");
}
// we are all good, delete the key
RegistryImpl.RegDeleteKey(this.m_uiHKey, subkey);
}
/// <summary>
/// Deletes the specified subkey. The string subkey is not case-sensitive.
/// </summary>
/// <param name="subkey">Name of the subkey to delete.</param>
public void DeleteSubKey(string subkey)
{
// just call the overloaded function
DeleteSubKey(subkey, false);
}
/// <summary>
/// Deletes a subkey and any child subkeys recursively. The string subKey is not case-sensitive.
/// </summary>
/// <param name="subkey">Subkey to delete</param>
public void DeleteSubKeyTree(string subkey)
{
AssertIsOpen();
// make sure we are not read only
if(m_bReadOnly)
throw new UnauthorizedAccessException("The subkey cannot be deleted. The registry key was opened as read only");
// delete the key
RegistryImpl.RegDeleteKey(this.m_uiHKey, subkey);
}
/// <summary>
/// Deletes the specified value from this key. The string subKey is not case sensitive.
/// </summary>
/// <param name="val">Name of the value to delete.</param>
/// <param name="throwOnMissing">Indicates whether an exception should be raised
/// if the specified value cannot be found. If this argument is true and the specified
/// value does not exist then an exception is raised. If this argument is false and
/// the specified value does not exist, then no action is taken</param>
public void DeleteValue(string val, bool throwOnMissing)
{
AssertIsOpen();
// make sure we are not read only.
if(m_bReadOnly)
throw new UnauthorizedAccessException("The value cannot be deleted. The registry key was opened as read only");
if(throwOnMissing)
{
// do the check to see if the key actually exists
int type = 0;
int size = 0;
byte[] data = null;
if(RegistryImpl.RegQueryValueEx(this.m_uiHKey, val, 0, ref type, data, ref size) != 0)
throw new Exception("The value " + val + " cannot be deleted because it is missing.");
}
RegistryImpl.RegDeleteValue(this.m_uiHKey, val);
}
/// <summary>
/// Deletes the specified value from this key. The string subKey is not case sensitive.
/// </summary>
/// <param name="val">Name of the value to delete..</param>
public void DeleteValue(string val)
{
// just call the overloaded function
DeleteValue(val, false);
}
/// <summary>
/// Retrieves the count of subkeys at the base level, for the current key.
/// </summary>
public int SubKeyCount
{
get
{
AssertIsOpen();
int Subkeys = 0;
int nullStuff = 0;
char[] test = null;
// query the registry key for the number of subkeys
int retVal = RegistryImpl.RegQueryInfoKey(this.m_uiHKey, test, ref nullStuff, 0, ref Subkeys, ref nullStuff, ref nullStuff,
ref nullStuff, ref nullStuff, ref nullStuff, 0, 0);
return Subkeys;
}
}
/// <summary>
/// Retrieves an array of strings that contains all the subkey names.
/// </summary>
/// <returns>An array of strings that contains the names of the subkeys for the current key.</returns>
public string[] GetSubKeyNames()
{
AssertIsOpen();
// create an arraylist to temporarily hold our subkey names
ArrayList subKeyNames = new ArrayList();
int nullStuff = 0;
int iMaxSubKeyLen = 255;
char[] chNull = null;
int iSubKeyLen = 0;
char[] chSubKeyName = new char[iMaxSubKeyLen];
for(int x = 0; x < this.SubKeyCount; x++)
{
// call RegEnumKeyEx for as many subkeys there are
iSubKeyLen = iMaxSubKeyLen;
RegistryImpl.RegEnumKeyEx(this.m_uiHKey, x, chSubKeyName, ref iSubKeyLen, 0, chNull, ref nullStuff, 0);
subKeyNames.Add(new string(chSubKeyName, 0, iSubKeyLen));
}
// return the array as a string Array
return (string[])subKeyNames.ToArray(typeof(string));
}
/// <summary>
/// Retrieves the count of values in the key.
/// </summary>
public int ValueCount
{
get
{
AssertIsOpen();
int icValueKeys = 0;
int nullStuff = 0;
char[] test = null;
// query the registry for the number of values in the current key.
int retVal = RegistryImpl.RegQueryInfoKey(this.m_uiHKey, test, ref nullStuff, 0, ref nullStuff, ref nullStuff, ref nullStuff,
ref icValueKeys, ref nullStuff, ref nullStuff, 0, 0);
return icValueKeys;
}
}
/// <summary>
/// Retrieves an array of strings that contains all the value names associated with this key.
/// </summary>
/// <returns>An array of strings that contains the value names for the current key.</returns>
public string[] GetValueNames()
{
AssertIsOpen();
// array list to temporarily hold the names of the values
ArrayList valueNames = new ArrayList();
int iMaxValueNameLen = 255;
byte[] byData = null;
int iDataLen = 0;
int iType = 0;
int iValueNameLen = 0;
char[] chValueName = new char[iMaxValueNameLen];
for(int x = 0; x < this.ValueCount; x++)
{
// call RegEnumKeyEx for all the values in the
iValueNameLen = iMaxValueNameLen;
RegistryImpl.RegEnumValue(this.m_uiHKey, x, chValueName, ref iValueNameLen, 0, ref iType, ref byData, ref iDataLen);
valueNames.Add(new string(chValueName, 0, iValueNameLen));
}
return (string[])valueNames.ToArray(typeof(string));
}
/// <summary>
/// Retrieves the specified value, or the default value you provide if the specified value is not found.
/// </summary>
/// <param name="val">Name of the value to retrieve.</param>
/// <param name="initialValue">Value to return if name does not exist.</param>
/// <returns>The data associated with name, or defaultValue if name is not found.</returns>
public object GetValue(string val, object initialValue)
{
AssertIsOpen();
int iType = 0;
int icData = 0;
byte[] byData = null;
// read in the value information, if no value exists, just return the initial value specified
if(RegistryImpl.RegQueryValueEx(this.m_uiHKey, val, 0, ref iType, byData, ref icData) != 0)
return initialValue;
byData = new byte[icData];
if(RegistryImpl.RegQueryValueEx(this.m_uiHKey, val, 0, ref iType, byData, ref icData) != 0)
return initialValue;
// Switch the type we have, and convert to the proper type
switch(iType)
{
case RegistryImpl.REG_BINARY:
return byData;
case RegistryImpl.REG_DWORD:
return System.BitConverter.ToInt32(byData, 0);
case RegistryImpl.REG_SZ:
return System.Text.UnicodeEncoding.Unicode.GetString(byData, 0, icData);
case RegistryImpl.REG_EXPAND_SZ:
return System.Text.UnicodeEncoding.Unicode.GetString(byData, 0, icData);
default:
throw new Exception("Unknown registry type!");
}
}
/// <summary>
/// Retrieves the data associated with the specified value, or null if the value does not exist.
/// </summary>
/// <param name="val">Name of the value to retrieve.</param>
/// <returns>The data associated with name , or null if the value does not exist.</returns>
public object GetValue(string val)
{
// just call the overloaded function
return GetValue(val, null);
}
/// <summary>
/// Sets the specified value. The string subKey is not case sensitive.
/// </summary>
/// <param name="valName">Name of value to store data in.</param>
/// <param name="valData">Data to store.</param>
public void SetValue(string valName, object valData)
{
AssertIsOpen();
// find out what type of data we are setting, and call the appropriate RegSetValueEx function
if(valData is string)
{
char[] chData = ((string)valData).ToCharArray();
byte[] byValData = new byte[System.Text.UnicodeEncoding.Unicode.GetByteCount(chData) + 1];
byValData = System.Text.UnicodeEncoding.Unicode.GetBytes(chData);
byValData[byValData.GetUpperBound(0)] = 0;
RegistryImpl.RegSetValueEx(this.m_uiHKey, valName, 0, RegistryImpl.REG_SZ, byValData, byValData.GetLength(0));
}
else if(valData is byte[])
{
RegistryImpl.RegSetValueEx(this.m_uiHKey, valName, 0, RegistryImpl.REG_BINARY, (byte[])valData, ((byte[])valData).GetLength(0));
}
else if(valData is int)
{
byte[] byValData = System.BitConverter.GetBytes((int)valData);
RegistryImpl.RegSetValueEx(this.m_uiHKey, valName, 0, RegistryImpl.REG_DWORD, byValData, byValData.GetLength(0));
}
else
{
throw new Exception("Unknown type to set!");
}
}
/// <summary>
/// Retrieves a specified subkey.
/// </summary>
/// <param name="subkey">Name or path of subkey to open.</param>
/// <param name="writeable">Set to true if you need write access to the key.</param>
/// <returns>The subkey requested, or null if the operation failed.</returns>
public RegistryKey OpenSubKey(string subkey, bool writeable)
{
AssertIsOpen();
this.m_bReadOnly = !writeable;
return null;
}
/// <summary>
/// Retrieves a subkey as read-only.
/// </summary>
/// <param name="subkey">Name or path of subkey to open.</param>
/// <returns>The subkey requested, or null if the operation failed.</returns>
public RegistryKey OpenSubKey(string subkey)
{
// just call the overloaded function
return OpenSubKey(subkey, false);
}
/// <summary>
/// Retrieves the name of the key.
/// </summary>
public string Name
{
get
{
return m_sName;
}
}
#region Implementation of IDisposable
public void Dispose()
{
this.Close();
}
#endregion
private void AssertIsOpen()
{
if(this.m_uiHKey == 0)
throw new System.IO.IOException("The RegistryKey on which this method is being invoked is closed");
}
/// <summary>
/// Retrieves a string representation of this key.
/// </summary>
/// <returns>A string representing the key. If the specified key is invalid (cannot be found) then a null value is returned.</returns>
public override string ToString()
{
return Name;
}
}
/// <summary>
/// This class holds root RegistryKeys for the system registry. Use these keys to manipulate the registry.
/// </summary>
public sealed class Registry
{
private static RegistryKey m_CurrentUser = new RegistryKey((uint)RegistryImpl.RootKey.HKEY_CURRENT_USER, "HKEY_CURRENT_USER");
private static RegistryKey m_Users = new RegistryKey((uint)RegistryImpl.RootKey.HKEY_USERS, "HKEY_USERS");
private static RegistryKey m_LocalMachine = new RegistryKey((uint)RegistryImpl.RootKey.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE");
private static RegistryKey m_ClassesRoot = new RegistryKey((uint)RegistryImpl.RootKey.HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT");
public static RegistryKey CurrentUser
{
get
{
return m_CurrentUser;
}
}
public static RegistryKey Users
{
get
{
return m_Users;
}
}
public static RegistryKey LocalMachine
{
get
{
return m_LocalMachine;
}
}
public static RegistryKey ClassesRoot
{
get
{
return m_ClassesRoot;
}
}
}
/// <summary>
/// Internal helper class for registry access.
/// </summary>
internal sealed class RegistryImpl
{
public enum RootKey : uint
{
HKEY_CLASSES_ROOT = 0x80000000,
HKEY_CURRENT_USER = 0x80000001,
HKEY_LOCAL_MACHINE = 0x80000002,
HKEY_USERS = 0x80000003
}
public enum KeyDisposition : int
{
REG_CREATED_NEW_KEY = 1,
REG_OPENED_EXISTING_KEY = 2
}
internal const int REG_SZ = 1;
internal const int REG_EXPAND_SZ = 2;
internal const int REG_BINARY = 3;
internal const int REG_DWORD = 4;
#region DLL Imports
[DllImport("coredll.dll", EntryPoint="RegOpenKeyEx")]
public static extern int RegOpenKeyEx(uint hKey, string lpSubKey, int ulOptions, int samDesired, ref uint phkResult);
[DllImport("coredll", EntryPoint="RegCreateKeyEx")]
public static extern int RegCreateKeyEx(uint hKey, string lpSubKey, int lpReserved, string lpClass, int dwOptions, int samDesired, ref int lpSecurityAttributes, ref uint phkResult, ref int lpdwDisposition);
[DllImport("coredll.dll", EntryPoint="RegEnumKeyEx")]
public static extern int RegEnumKeyEx(uint hKey, int iIndex, char[] sKeyName,
ref int iKeyNameLen, int iReservedZero, char[] sClassName, ref int iClassNameLen, int iFiletimeZero);
[DllImport("coredll.dll", EntryPoint="RegEnumValue")]
public static extern int RegEnumValue(uint hKey, int iIndex, char[] sValueName,
ref int iValueNameLen, int iReservedZero, ref int iType, ref byte[] byData, ref int iDataLen);
[DllImport("coredll.dll", EntryPoint="RegQueryInfoKey")]
public static extern int RegQueryInfoKey(uint hKey, char[] lpClass, ref int lpcbClass,
int reservedZero, ref int cSubkey, ref int iMaxSubkeyLen, ref int lpcbMaxSubkeyClassLen,
ref int cValueNames, ref int iMaxValueNameLen, ref int iMaxValueLen, int securityDescriptorZero, int lastWriteTimeZero);
[DllImport("coredll.dll", EntryPoint="RegQueryValueEx")]
public static extern int RegQueryValueEx(uint hKey, string lpValueName, int lpReserved, ref int lpType, byte[] lpData, ref int lpcbData);
[DllImport("coredll.dll", EntryPoint="RegSetValueExW")]
public static extern int RegSetValueEx(uint hKey, string lpValueName, int lpReserved, int lpType, byte[] lpData, int lpcbData);
[DllImport("coredll.dll", EntryPoint="RegCloseKey")]
public static extern int RegCloseKey(uint hKey);
[DllImport("coredll.dll", EntryPoint="RegDeleteKey")]
public static extern int RegDeleteKey(uint hKey, string keyName);
[DllImport("coredll.dll", EntryPoint="RegDeleteValue")]
public static extern int RegDeleteValue(uint hKey, string valueName);
#endregion
/// <summary>
/// Helper to allow me to easily open a key
/// </summary>
/// <param name="RootKey"></param>
/// <param name="SubKey"></param>
/// <returns></returns>
public static uint OpenKey(uint RootKey, string SubKey)
{
uint hKey = 0;
int ret = 0;
try
{
RegOpenKeyEx(RootKey, SubKey, 0, 0, ref hKey);
}
catch(Exception ex)
{
Console.WriteLine("Exception opening key: " + ex.Message);
throw;
}
if(ret != 0)
Console.WriteLine("Failed to open key: " + ret.ToString());
return hKey;
}
/// <summary>
/// Helper to allow me to easily Create a key
/// </summary>
/// <param name="RootKey"></param>
/// <param name="SubKey"></param>
/// <param name="KeyClass"></param>
/// <param name="Disposition"></param>
/// <returns></returns>
public static uint CreateKey(uint RootKey, string SubKey, string KeyClass, ref KeyDisposition Disposition)
{
uint hKey = 0;
int disp = 0;
int reserved = 0;
int ret = 0;
try
{
ret = RegCreateKeyEx(RootKey, SubKey, 0, KeyClass, 0, 0, ref reserved, ref hKey, ref disp);
}
catch(Exception ex)
{
Console.WriteLine("Exception creating key: " + ex.Message);
throw;
}
if(ret != 0)
Console.WriteLine("Failed to create key: " + ret.ToString());
else
Disposition = (KeyDisposition)disp;
return hKey;
}
}
}
}