Click here to Skip to main content
15,885,915 members
Articles / Desktop Programming / Win32
Tip/Trick

Registry Export File (.reg) Parser

Rate me:
Please Sign up or sign in to vote.
4.83/5 (20 votes)
20 Feb 2017CPOL3 min read 103.6K   3.2K   62   28
A class to read and parse reg files

Introduction

You may already - like me some time ago - have needed a possibility to read and to analyze an exported .reg file, in order to compare the given registry values or to import only few of the exported registry values.

In my case, I had to read a reg file of ca. 30 MB size, then compare all values with the existing registry values and change some of them - some kind of backup/restore solution for poor men ;-). I spent some time searching on the Internet for a ready-to-use solution, but all that I found was either another language (C++, VBS) or something not really efficient. So I decided to write my own DLL library to read and parse a reg file.

Background

I already used the .NET Regex class in some projects and was really amazed at how smart the Regex methods have been coded, working much quicker as for example the String class methods. My read function needs less than 5 seconds to read my 30 MB reg file on a standard 3Ghz PC (Windows 7).

The main class is the RegFileObject class.

RegFileParser/RegFileObject.png

The raw content of the .reg file is stored in the private field content. The parsed registry values are stored in the RegValues generic dictionary. The dictionary keys are the registry keys found in the .reg file. The dictionary values are again dictionaries with registry values names as dictionary keys and registry values data as dictionary values.

The RegValueObject class diagram:

RegFileParser/RegValueObject.png

The program parses the reg file in two steps:

  1. Detecting all registry keys and the text content between each registry key pair and saving them as Dictionary<string,string>
  2. Iterating through each found text content and parsing registry value names and values

The first parsing function is the NormalizeKeysDictionary function. It "slices" the raw text content of the whole reg file using a search pattern ^[\t ]*\\[.+\\][\r\n]+. It returns the output as a dictionary with found matches as dictionary keys and the whole raw text content between the current match end and next match begin as dictionary value.

C#
/// <summary>
/// Creates a flat Dictionary using given search pattern
/// </summary>
/// <param name="content">The content string to be parsed</param>
/// <returns>A Dictionary with retrieved keys and remaining content</returns>
private Dictionary<String, String> NormalizeKeysDictionary(String content)
{
  string searchPattern = "^[\t ]*\\[.+\\][\r\n]+";
  MatchCollection matches = Regex.Matches(content, searchPattern, RegexOptions.Multiline);

  Int32 startIndex = 0;
  Int32 lengthIndex = 0;
  Dictionary<String, String> dictKeys = new Dictionary<string, string>();

  foreach (Match match in matches)
  {
    try
    {
      //Retrieve key
      String sKey = match.Value;
      //change proposed by Jenda27
      //if (sKey.EndsWith("\r\n")) sKey = sKey.Substring(0, sKey.Length - 2);
      while (sKey.EndsWith("\r\n"))
      {
        sKey = sKey.Substring(0, sKey.Length - 2);
      }
      if (sKey.EndsWith("=")) sKey = sKey.Substring(0, sKey.Length - 1);
      sKey = StripeBraces(sKey);
      if (sKey == "@")
        sKey = "";
      else
        sKey = StripeLeadingChars(sKey, "\"");

      //Retrieve value
      startIndex = match.Index + match.Length;
      Match nextMatch = match.NextMatch();
      lengthIndex = ((nextMatch.Success) ? nextMatch.Index : content.Length) - startIndex;
      String sValue = content.Substring(startIndex, lengthIndex);
      //Removing the ending CR
      //change suggested by Jenda27
      //if (sValue.EndsWith("\r\n")) sValue = sValue.Substring(0, sValue.Length - 2);
      while (sValue.EndsWith("\r\n"))
      {
        sValue = sValue.Substring(0, sValue.Length - 2);
      }
      //fix for the double key names issue
      //dictKeys.Add(sKey, sValue);
      if (dictKeys.ContainsKey(sKey))
      {
        string tmpcontent = dictKeys[sKey];
        StringBuilder tmpsb = new StringBuilder(tmpcontent);
        if (!tmpcontent.EndsWith(Environment.NewLine)) tmpsb.AppendLine();
        tmpsb.Append(sValue);
        dictKeys[sKey] = tmpsb.ToString();
      }
      else
      {
        dictKeys.Add(sKey, sValue);
      }
    }
    catch (Exception ex)
    {
      throw new Exception(String.Format
                         ("Exception thrown on processing string {0}", match.Value), ex);
    }
  }
  return dictKeys;
}

The second parsing function is the NormalizeValuesDictionary function. It returns the output as a dictionary with value names as dictionary keys and the value content as dictionary value.

C#
/// <summary>
/// Creates a flat Dictionary using given searcn pattern
/// </summary>
/// <param name="content">The content string to be parsed</param>
/// <returns>A Dictionary with retrieved keys and remaining content</returns>
private Dictionary<String, String> NormalizeValuesDictionary(String content)
{
    string searchPattern = @"^[\t ]*("".+""|@)=(""[^""]*""|[^""]+)";
    MatchCollection matches = Regex.Matches(content, searchPattern, RegexOptions.Multiline);

    Dictionary<String, String> dictKeys = new Dictionary<string, string>();

    foreach (Match match in matches)
    {
        try
        {
            //Retrieve key
            String sKey = match.Groups[1].Value;

            //Retrieve value
            String sValue = match.Groups[2].Value;

            //Removing the ending CR
            while (sKey.EndsWith("\r\n"))
            {
                sKey = sKey.Substring(0, sKey.Length - 2);
            }

            if (sKey == "@")
                sKey = "";
            else
                sKey = StripeLeadingChars(sKey, "\"");

            while (sValue.EndsWith("\r\n"))
            {
                sValue = sValue.Substring(0, sValue.Length - 2);
            }

            if (dictKeys.ContainsKey(sKey))
            {
                string tmpcontent = dictKeys[sKey];
                StringBuilder tmpsb = new StringBuilder(tmpcontent);
                if (!tmpcontent.EndsWith(Environment.NewLine)) tmpsb.AppendLine();
                tmpsb.Append(sValue);
                dictKeys[sKey] = tmpsb.ToString();
            }
            else
            {
                dictKeys.Add(sKey, sValue);
            }
        }
        catch (Exception ex)
        {
            throw new Exception(String.Format
                ("Exception thrown on processing string {0}", match.Value), ex);
        }
    }
    return dictKeys;
}

Limitations

My .reg file parser doesn't support the remove directives (-[HKEY_LOCAL_MACHINE\SOFTWARE\TestSoftware]), as it was not in focus of my project.

I have surely not discovered all issues on parsing f. ex. very "exotic" .reg files, but for my project, it was absolutely sufficient. The simplicity of the hereby presented parsing routines allow however to easily enhance the library by new functions.

Using the Code

The .reg file will be read on creating an instance of the RegFileObject class:

C#
RegFileObject regfile = new RegFileObject(@"C:\Temp\test.reg"); 

The searched registry value can be simply accessed by typing:

C#
RegValueObject tempValue = regfile.RegValues
    [@"HKEY_LOCAL_MACHINE\SOFTWARE\TestSoftware"]["TestValue"]; 

and the value data by querying the property Value:

C#
String tempData = tempValue.Value; 

or directly:

C#
RegFileObject regfile = new RegFileObject(@"C:\Temp\test.reg");
String tempData = regfile.RegValues
    [@"HKEY_LOCAL_MACHINE\SOFTWARE\TestSoftware"]["TestValue"].Value; 

The RegValues dictionary can be very simply enumerated using the foreach or for statements.

History

  • 8th November, 2010: Initial release
  • 14th April, 2011: Fixed some typos
  • 18th December, 2014: Fixed multiple empty lines bug (thanks to Jenda27)
  • 3rd August, 2016: Fixed both mentioned issues (with the '=' character in the key name and double key names)

License

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


Written By
Software Developer Lanxess GmbH
Germany Germany
MCTS .NET Framework 2.0 Web Applications
MCTS .NET Framework 2.0 Windows Applications
MCTS .NET Framework 2.0 Distributed Applications
MCTS Windows 7, Configuration
MCITP Enterprise Desktop Administrator on Windows 7

Comments and Discussions

 
NewsMade a modern NuGet out of it Pin
CNefarius29-Dec-22 2:17
CNefarius29-Dec-22 2:17 
GeneralRe: Made a modern NuGet out of it Pin
Henryk Filipowicz1-Jun-23 7:15
Henryk Filipowicz1-Jun-23 7:15 
QuestionBug with Regex & '@' keys Pin
Pickettster31-Jan-22 19:26
Pickettster31-Jan-22 19:26 
GeneralMy vote of 5 Pin
Patrick Vervondel27-Feb-21 3:56
Patrick Vervondel27-Feb-21 3:56 
QuestionSource files missing on Codeproject server Pin
Member 108495213-Aug-16 20:54
Member 108495213-Aug-16 20:54 
AnswerRe: Source files missing on Codeproject server Pin
Chris Kelly8-Aug-16 14:39
professionalChris Kelly8-Aug-16 14:39 
AnswerRe: Source files missing on Codeproject server Pin
Henryk Filipowicz16-Aug-16 2:38
Henryk Filipowicz16-Aug-16 2:38 
GeneralRe: Source files missing on Codeproject server Pin
robinsonra217-Aug-16 8:39
robinsonra217-Aug-16 8:39 
GeneralRe: Source files missing on Codeproject server Pin
robinsonra217-Aug-16 9:35
robinsonra217-Aug-16 9:35 
GeneralRe: Source files missing on Codeproject server Pin
Henryk Filipowicz17-Aug-16 22:52
Henryk Filipowicz17-Aug-16 22:52 
QuestionCompiled regex. Pin
James Curran3-Aug-16 9:04
James Curran3-Aug-16 9:04 
AnswerRe: Compiled regex. Pin
Henryk Filipowicz16-Aug-16 2:33
Henryk Filipowicz16-Aug-16 2:33 
SuggestionYOUR STUFF IS ... !!! Pin
Member 125742458-Jun-16 23:56
Member 125742458-Jun-16 23:56 
GeneralRe: YOUR STUFF IS ... !!! Pin
Henryk Filipowicz3-Aug-16 2:32
Henryk Filipowicz3-Aug-16 2:32 
BugI found a bug Pin
takahirok7-Jan-16 20:22
takahirok7-Jan-16 20:22 
GeneralRe: I found a bug Pin
Henryk Filipowicz3-Aug-16 2:17
Henryk Filipowicz3-Aug-16 2:17 
Questiongood stuff Pin
Jenda2713-Jun-13 14:32
Jenda2713-Jun-13 14:32 
AnswerRe: good stuff Pin
Henryk Filipowicz11-Dec-14 1:31
Henryk Filipowicz11-Dec-14 1:31 
GeneralRe: good stuff Pin
andrewtheart19-Dec-14 18:14
andrewtheart19-Dec-14 18:14 
GeneralRe: good stuff Pin
Henryk Filipowicz12-Feb-15 22:33
Henryk Filipowicz12-Feb-15 22:33 
BugRe: good stuff Pin
Member 1219105520-Feb-17 21:34
Member 1219105520-Feb-17 21:34 
GeneralRe: good stuff Pin
Henryk Filipowicz20-Feb-17 22:20
Henryk Filipowicz20-Feb-17 22:20 
Questionpublishing on my website: http://autoid.de.tl Pin
nxexoxn1-Feb-12 20:44
nxexoxn1-Feb-12 20:44 
AnswerRe: publishing on my website: http://autoid.de.tl Pin
Henryk Filipowicz22-Apr-14 4:31
Henryk Filipowicz22-Apr-14 4:31 
AnswerRe: publishing on my website: http://autoid.de.tl Pin
Zoltán Zörgő18-Dec-14 3:48
Zoltán Zörgő18-Dec-14 3:48 
Hello,

Could you share the modified version if the library? I also need something like that wiith my CF3.5 project.

Thank you,

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.