Example #1
0
 private void ExpectKeyPath(char c)
 {
     if (c == '[')
     {
         ++NumberOfClosingBracketsExpected;
         Buffer.Append(c);
     }
     else if (c == ']')
     {
         if (0 == NumberOfClosingBracketsExpected)
         {
             CurrentKey  = Result.FindOrCreateKey(Buffer.ToString());
             ParserState = ExpectCarriageReturn;
         }
         else if (NumberOfClosingBracketsExpected > 0)
         {
             --NumberOfClosingBracketsExpected;
             Buffer.Append(c);
         }
         else
         {
             throw SyntaxError("Too many closing square brackets");
         }
     }
     else
     {
         Buffer.Append(c);
     }
 }
        private void ImportRecursive(RegKeyEntry parent, RegistryKey key, RegKeyEntry relativeKey)
        {
            foreach (var name in key.GetSubKeyNames())
            {
                string keyName = name.ToLower();
                if (relativeKey.Keys.ContainsKey(keyName))
                {
                    RegKeyEntry entry = new RegKeyEntry(parent, name);
                    parent.Keys[name.ToLower()] = entry;
                    try
                    {
                        using (RegistryKey subkey = key.OpenSubKey(name))
                        {
                            ImportRecursive(entry, subkey, relativeKey.Keys[keyName]);
                        }
                    }
                    catch (System.Security.SecurityException)
                    {
                        // ignore
                    }
                }
            }

            foreach (var name in key.GetValueNames())
            {
                parent.Values[name.ToLower()] = new RegValueEntry(key, name);
            }
        }
Example #3
0
 /// <summary>
 /// The constructor needs an expected header identifying the version of the .REG file, as well as import options
 /// </summary>
 /// <param name="expectedHeader">expected header identifying the version of the .REG file</param>
 /// <param name="options">import options</param>
 public RegFileParser(string expectedHeader, RegFileImportOptions options)
 {
     Options  = options;
     Result   = new RegKeyEntry(null, null);
     HeaderId = expectedHeader;
     NumberOfClosingBracketsExpected = 0;
 }
Example #4
0
        /// <summary>
        /// Find or create a subkey relative to this one
        /// </summary>
        /// <param name="path">Subkey path relative to this one</param>
        /// <returns>Newly created subkey</returns>
        public RegKeyEntry FindOrCreateKey(string path)
        {
            Trace.Assert(!string.IsNullOrEmpty(path));

            RegKeyEntry result = this;

            foreach (string token in path.Split('\\'))
            {
                string key = token.ToLower();

                if (result.Keys.ContainsKey(key))
                {
                    RegKeyEntry subkey = result.Keys[key];
                    Trace.Assert(subkey.Parent == result);
                    result = subkey;
                }
                else
                {
                    RegKeyEntry subkey = new RegKeyEntry(result, token);
                    result.Keys[key] = subkey;
                    Trace.Assert(subkey.Parent == result);
                    result = subkey;
                }
            }
            return(result);
        }
Example #5
0
 /// <summary>
 /// This constructor creates a named registry key, relative to an existing parent
 /// </summary>
 /// <param name="parent"></param>
 /// <param name="name"></param>
 public RegKeyEntry(RegKeyEntry parent, string name)
 {
     Parent       = parent;
     Name         = name;
     DefaultValue = null;
     RemoveFlag   = false;
 }
Example #6
0
        /// <summary>
        /// When creating a diff/merge file, asks the key to remove a subkey based on an existing key
        /// </summary>
        /// <param name="removeThis">Existing key</param>
        /// <returns>Key in the diff/merge file with the RemoveFlag set</returns>
        public RegKeyEntry AskToRemoveKey(RegKeyEntry removeThis)
        {
            RegKeyEntry key = AskToAddKey(removeThis);

            key.RemoveFlag = true;
            return(key);
        }
Example #7
0
        /// <summary>
        /// This function creates a new RegKeyEntry, that represents the diff information;
        /// assuming that key 1 is the old information, and key 2 the new information
        ///
        /// That means:
        /// - if a key is missing in 1, it is to be added
        /// - if a key is missing in 2, it is to be removed
        /// - if a value is missing in 1, it is to be added
        /// - if a value is missing in 2, it is to be removed
        /// - if a value has changed, use the data from 2
        /// </summary>
        /// <returns>A newly created RegKeyEntry that describes the differences</returns>
        public RegKeyEntry CreateDiffKeyEntry()
        {
            RegKeyEntry result = new RegKeyEntry(null, null);

            foreach (RegKeyEntry keyMissingIn1 in MissingKeysIn1)
            {
                result.AskToAddKey(keyMissingIn1);
            }
            foreach (RegKeyEntry keyMissingIn2 in MissingKeysIn2)
            {
                result.AskToRemoveKey(keyMissingIn2);
            }
            foreach (MissingValue missingValueIn1 in MissingValuesIn1)
            {
                result.AskToAddValue(missingValueIn1.Key, missingValueIn1.Value);
            }
            foreach (MissingValue missingValueIn2 in MissingValuesIn2)
            {
                result.AskToRemoveValue(missingValueIn2.Key, missingValueIn2.Value);
            }
            foreach (DataMismatch dataMismatch in DataMismatches)
            {
                result.AskToAddValue(dataMismatch.Key, dataMismatch.Value2);
            }
            foreach (KindMismatch kindMismatch in KindMismatches)
            {
                result.AskToAddValue(kindMismatch.Key, kindMismatch.Value2);
            }
            return(result);
        }
Example #8
0
 /// <summary>
 /// The constructor creates two named registry keys and compares them
 /// </summary>
 /// <param name="key1">First key</param>
 /// <param name="name1">Name of first key</param>
 /// <param name="key2">Second key</param>
 /// <param name="name2">Name of second key</param>
 /// <param name="aliases">Dictionary of key aliases</param>
 public RegDiff(RegKeyEntry key1, string name1, RegKeyEntry key2, string name2, Dictionary <string, string> aliases)
 {
     Key1    = key1;
     Key2    = key2;
     Name1   = name1;
     Name2   = name2;
     Aliases = aliases;
     CompareRecursive(key1, key2);
 }
Example #9
0
        /// <summary>
        /// The default constructor takes a registry root path encoded as a string (for example, HKEY_LOCAL_MACHINE\Software\Microsoft)
        /// and reads everything under it.
        /// </summary>
        /// <param name="rootPath">Root path</param>
        /// <param name="registryView">Type of registry you want to see (32-bit, 64-bit, default).</param>
        public RegistryImporter(string rootPath, RegistryView registryView)
        {
            Result = new RegKeyEntry(null, rootPath);

            string      rootPathWithoutHive;
            RegistryKey rootKey = Regis3.OpenRegistryHive(rootPath, out rootPathWithoutHive, registryView);

            using (RegistryKey key = rootKey.OpenSubKey(rootPathWithoutHive))
            {
                ImportRecursive(Result, key);
            }
        }
Example #10
0
 /// <summary>
 /// When creating a diff/merge file, asks the key to add a value based on an existing value
 /// </summary>
 /// <param name="key">existing key</param>
 /// <param name="value">existing value</param>
 public void AskToAddValue(RegKeyEntry key, RegValueEntry value)
 {
     key = AskToAddKey(key);
     if (value.IsDefaultValue)
     {
         key.DefaultValue = new RegValueEntry(value);
     }
     else
     {
         string valueName = value.Name.ToLower();
         key.Values[valueName] = new RegValueEntry(value);
     }
 }
        /// <summary>
        /// This constructor creates a registry importer for an existing registry key
        /// </summary>
        /// <param name="existingRegistry">Existing registry key</param>
        /// <param name="registryView">Type of registry you want to see (32-bit, 64-bit, default).</param>
        public RegistryImportRelativeToExistingRegKeyEntry(RegKeyEntry existingRegistry, RegistryView registryView)
        {
            Result = new RegKeyEntry(null, existingRegistry.Path);

            string      rootPath = existingRegistry.Path;
            string      rootPathWithoutHive;
            RegistryKey rootKey = Regis3.OpenRegistryHive(rootPath, out rootPathWithoutHive, registryView);

            using (RegistryKey key = rootKey.OpenSubKey(rootPathWithoutHive))
            {
                ImportRecursive(Result, key, existingRegistry);
            }
        }
Example #12
0
        /// <summary>
        /// This method imports a registry key from the text of a .REG file
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public RegKeyEntry Parse(string content)
        {
            Parse(content, ExpectHeader);

            if ((Result.Keys.Count == 1) && (Result.Values.Count == 0))
            {
                foreach (string key in Result.Keys.Keys)
                {
                    Result = Result.Keys[key];
                    break;
                }
            }
            return(Result);
        }
Example #13
0
        /// <summary>
        /// This function creates a new RegKeyEntry, that represents the merge information;
        /// assuming that key 1 is the old information, and key 2 the new information
        ///
        /// That means:
        /// - includes all information from key 2
        /// - if a key is missing in 2, it is to be removed
        /// - if a value is missing in 2, it is to be removed
        /// - if a value has changed, use the data from 2
        /// </summary>
        /// <returns>A newly created RegKeyEntry that describes the merge information</returns>
        public RegKeyEntry CreateMergeKeyEntry()
        {
            RegKeyEntry result = new RegKeyEntry(Key2);

            foreach (RegKeyEntry keyMissingIn2 in MissingKeysIn2)
            {
                result.AskToRemoveKey(keyMissingIn2);
            }
            foreach (MissingValue missingValueIn2 in MissingValuesIn2)
            {
                result.AskToRemoveValue(missingValueIn2.Key, missingValueIn2.Value);
            }
            return(result);
        }
Example #14
0
 /// <summary>
 /// Export the given registry key to a file
 /// </summary>
 /// <param name="key">Registry key previously imported (or constructed in memory)</param>
 /// <param name="filename">Filename to save the key in</param>
 /// /// <param name="options">Export options</param>
 public void Export(RegKeyEntry key, string filename, RegFileExportOptions options)
 {
     using (StreamWriter sw = new StreamWriter(File.Open(filename, FileMode.Create), FileEncoding))
     {
         try
         {
             Export(key, sw, options);
         }
         finally
         {
             sw.Close();
         }
     }
 }
Example #15
0
        /// <summary>
        /// Export the key to a given output stream
        /// </summary>
        /// <param name="key">Existing key</param>
        /// <param name="file">Output stream</param>
        /// <param name="options">Export options</param>
        public void Export(RegKeyEntry key, TextWriter file, RegFileExportOptions options)
        {
            XmlWriterSettings settings = new XmlWriterSettings();

            settings.Indent = true;

            XmlWriter Writer = XmlWriter.Create(file, settings);

            Writer.WriteStartElement("registry");
            Writer.WriteAttributeString("version", "2");

            WriteXmlFileFormat(Writer, key);

            Writer.WriteEndElement();
            Writer.Close();
        }
Example #16
0
        private static void WriteXmlFileFormat(XmlWriter Writer, RegKeyEntry key)
        {
            List <string> names;

            if (key.RemoveFlag)
            {
                if (key.Name != null)
                {
                    Writer.WriteStartElement("remove-key");
                    Writer.WriteAttributeString("name", key.Name);
                    Writer.WriteEndElement();
                }
            }
            else
            {
                if (key.Name != null)
                {
                    Writer.WriteStartElement("key");
                    Writer.WriteAttributeString("name", key.Name);
                }

                names = key.Keys.Keys.ToList <string>();
                names.Sort();
                foreach (string name in names)
                {
                    WriteXmlFileFormat(Writer, key.Keys[name]);
                }

                if (key.DefaultValue != null)
                {
                    WriteXmlFileFormat(Writer, key.DefaultValue);
                }

                names = key.Values.Keys.ToList <string>();
                names.Sort();
                foreach (string name in names)
                {
                    WriteXmlFileFormat(Writer, key.Values[name]);
                }
                if (key.Name != null)
                {
                    Writer.WriteEndElement();
                }
            }
        }
Example #17
0
        /// <summary>
        /// This constructor takes an existing registry root key and a path relative to that key. You acn use this
        /// if you've already split the path and identified the root key in a step before that (this is what Regdiff does)
        /// </summary>
        /// <param name="rootKey">Registry key identifing root (e.g. HKEY_LOCAL_MACHINE)</param>
        /// <param name="rootPath">Relative registry path (e.g. "Software\Microsoft")</param>
        public RegistryImporter(RegistryKey rootKey, string rootPath)
        {
            Result = new RegKeyEntry(null, rootKey.Name);

            RegKeyEntry parent = Result;

            foreach (string token in rootPath.Split('\\'))
            {
                RegKeyEntry next = new RegKeyEntry(parent, token);
                parent.Keys[token.ToLower()] = next;
                parent = next;
            }

            using (RegistryKey key = rootKey.OpenSubKey(rootPath))
            {
                ImportRecursive(parent, key);
            }
        }
Example #18
0
        private void CompareByteArrays(RegKeyEntry key, RegValueEntry value1, RegValueEntry value2)
        {
            byte[] a = value1.AsByteArray();
            byte[] b = value2.AsByteArray();

            if (a.Length != b.Length)
            {
                DataMismatches.Add(new DataMismatch(key, value1, value2));
            }
            else
            {
                for (int i = 0; i < a.Length; ++i)
                {
                    if (a[i] != b[i])
                    {
                        DataMismatches.Add(new DataMismatch(key, value1, value2));
                    }
                }
            }
        }
Example #19
0
        private void CompareStringArrays(RegKeyEntry key, RegValueEntry value1, RegValueEntry value2)
        {
            string[] a = value1.Value as string[];
            string[] b = value2.Value as string[];

            if (a.Length != b.Length)
            {
                DataMismatches.Add(new DataMismatch(key, value1, value2));
            }
            else
            {
                for (int i = 0; i < a.Length; ++i)
                {
                    if (!a[i].Equals(b[i]))
                    {
                        DataMismatches.Add(new DataMismatch(key, value1, value2));
                    }
                }
            }
        }
Example #20
0
 private void CompareValues(RegKeyEntry key, RegValueEntry value1, RegValueEntry value2)
 {
     if (value1.Kind != value2.Kind)
     {
         KindMismatches.Add(new KindMismatch(key, value1, value2));
     }
     else
     {
         if (value1.Value is byte[])
         {
             CompareByteArrays(key, value1, value2);
         }
         else if (value1.Value is string[])
         {
             CompareStringArrays(key, value1, value2);
         }
         else if (!value1.Value.Equals(value2.Value))
         {
             DataMismatches.Add(new DataMismatch(key, value1, value2));
         }
     }
 }
Example #21
0
 /// <summary>
 /// Copy constructor: takes an existing RegKeyEntry and creates a full clone of it
 /// </summary>
 /// <param name="objectSrc"></param>
 public RegKeyEntry(RegKeyEntry objectSrc)
 {
     Name   = objectSrc.Name;
     Parent = objectSrc.Parent;
     foreach (string subkeyName in objectSrc.Keys.Keys)
     {
         Keys[subkeyName]        = new RegKeyEntry(objectSrc.Keys[subkeyName]);
         Keys[subkeyName].Parent = this;
     }
     foreach (string valueName in objectSrc.Values.Keys)
     {
         Values[valueName] = new RegValueEntry(objectSrc.Values[valueName]);
     }
     if (objectSrc.DefaultValue == null)
     {
         DefaultValue = null;
     }
     else
     {
         DefaultValue = new RegValueEntry(objectSrc.DefaultValue);
     }
 }
Example #22
0
        /// <summary>
        /// When creating a diff/merge file, asks the key to create a subkey based on an existing key
        /// </summary>
        /// <param name="addThis">Existing key</param>
        /// <returns>Key in the diff/merge file</returns>
        public RegKeyEntry AskToAddKey(RegKeyEntry addThis)
        {
            RegKeyEntry key = FindOrCreateKey(addThis.Path);

            foreach (string subkeyName in addThis.Keys.Keys)
            {
                key.Keys[subkeyName]        = new RegKeyEntry(addThis.Keys[subkeyName]);
                key.Keys[subkeyName].Parent = key;
            }
            foreach (string valueName in addThis.Values.Keys)
            {
                key.Values[valueName] = new RegValueEntry(addThis.Values[valueName]);
            }
            if (addThis.DefaultValue == null)
            {
                key.DefaultValue = null;
            }
            else
            {
                key.DefaultValue = new RegValueEntry(addThis.DefaultValue);
            }
            return(key);
        }
Example #23
0
        private void ImportRecursive(RegKeyEntry parent, RegistryKey key)
        {
            if (key == null)
            {
                return;
            }

            foreach (var name in key.GetSubKeyNames())
            {
                RegKeyEntry entry = new RegKeyEntry(parent, name);
                parent.Keys[name.ToLower()] = entry;
                try
                {
                    using (RegistryKey subkey = key.OpenSubKey(name))
                    {
                        ImportRecursive(entry, subkey);
                    }
                }
                catch (System.Security.SecurityException)
                {
                    // ignore
                }
            }

            foreach (var name in key.GetValueNames())
            {
                if (string.IsNullOrEmpty(name))
                {
                    Trace.Assert(parent.DefaultValue == null);
                    parent.DefaultValue = new RegValueEntry(key, null);
                }
                else
                {
                    parent.Values[name.ToLower()] = new RegValueEntry(key, name);
                }
            }
        }
Example #24
0
 /// <summary>
 /// Export the given registry key to a text stream
 /// </summary>
 /// <param name="key">Registry key previously imported (or constructed in memory)</param>
 /// <param name="file">Output text stream</param>
 /// /// <param name="options">Export options</param>
 public void Export(RegKeyEntry key, TextWriter file, RegFileExportOptions options)
 {
     file.WriteLine(Header);
     file.WriteLine();
     key.WriteRegFileFormat(file, options);
 }
Example #25
0
        private void ParseXmlContent(string content)
        {
            RegValueEntry     CurrentValue   = null;
            RegKeyEntry       CurrentKey     = null;
            StringBuilder     CurrentContent = null;
            RegValueEntryKind CurrentKind    = RegValueEntryKind.Unknown;
            bool          isBase64Encoding   = false;
            List <string> currentStringList  = new List <string>();

            using (XmlReader reader = XmlReader.Create(new StringReader(content)))
            {
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                    case XmlNodeType.Element:
                        if (reader.Name.Equals("registry"))
                        {
                            string version = reader.GetAttribute("version");
                            if (version == "2")
                            {
                                // ok, this version is supported
                            }
                            else
                            {
                                throw new System.Data.SyntaxErrorException("Unexpected XML format: must be using registry version 2.0 or higher");
                            }
                        }
                        else if (reader.Name.Equals("key"))
                        {
                            string name = reader.GetAttribute("name");
                            if (CurrentKey == null)
                            {
                                Trace.Assert(Result == null);
                                Result     = new RegKeyEntry(null, name);
                                CurrentKey = Result;
                            }
                            else
                            {
                                RegKeyEntry newKey = new RegKeyEntry(CurrentKey, name);
                                CurrentKey.Keys[newKey.Name.ToLower()] = newKey;
                                if (!reader.IsEmptyElement)
                                {
                                    CurrentKey = newKey;
                                }
                            }
                        }
                        else if ((CurrentKind == RegValueEntryKind.MultiSZ) && reader.Name.Equals("line"))
                        {
                            if (reader.IsEmptyElement)
                            {
                                currentStringList.Add("");
                            }
                            else
                            {
                                CurrentContent = new StringBuilder();

                                string encoding = reader.GetAttribute("encoding");
                                isBase64Encoding = (encoding != null) && encoding.Equals("base-64");
                            }
                        }
                        else
                        {
                            try
                            {
                                CurrentKind = (RegValueEntryKind)Enum.Parse(typeof(RegValueEntryKind), reader.Name);
                            }
                            catch (ArgumentException)
                            {
                                throw new System.Data.SyntaxErrorException(
                                          string.Format("ERROR, {0} is not a valid entry in a registry .XML file", reader.Name));
                            }
                            string name = reader.GetAttribute("name");
                            CurrentValue = new RegValueEntry(name);
                            if (name == null)
                            {
                                CurrentKey.DefaultValue = CurrentValue;
                            }
                            else
                            {
                                CurrentKey.Values[name.ToLower()] = CurrentValue;
                            }
                            if (reader.IsEmptyElement)
                            {
                                if (RegValueEntryKind.SZ == CurrentKind)
                                {
                                    CurrentValue.SetStringValue("");
                                }
                                else if (RegValueEntryKind.ExpandSZ == CurrentKind)
                                {
                                    CurrentValue.SetExpandedStringValue("");
                                }
                                else if (RegValueEntryKind.DWord == CurrentKind)
                                {
                                    CurrentValue.SetIntValue(0);
                                }
                                else if (RegValueEntryKind.QWord == CurrentKind)
                                {
                                    CurrentValue.SetLongValue(0);
                                }
                                else if (RegValueEntryKind.MultiSZ == CurrentKind)
                                {
                                    CurrentValue.SetMultiStringValue(new List <string>());
                                }
                                else
                                {
                                    CurrentValue.SetBinaryType(CurrentKind, new byte[] { });
                                }
                                CurrentValue = null;
                            }
                            else
                            {
                                CurrentContent = new StringBuilder();
                                string encoding = reader.GetAttribute("encoding");
                                isBase64Encoding = (encoding != null) && encoding.Equals("base-64");

                                if (CurrentKind == RegValueEntryKind.MultiSZ)
                                {
                                    currentStringList.Clear();
                                }
                                else
                                {
                                    CurrentContent = new StringBuilder();
                                }
                            }
                        }
                        break;

                    case XmlNodeType.Text:
                        if (CurrentContent != null)
                        {
                            CurrentContent.Append(reader.Value);
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (reader.Name.Equals("key"))
                        {
                            Trace.Assert(CurrentKey != null);
                            CurrentKey = CurrentKey.Parent;
                        }
                        else if ((CurrentKind == RegValueEntryKind.MultiSZ) && reader.Name.Equals("line"))
                        {
                            if (isBase64Encoding)
                            {
                                byte[] bytes = Convert.FromBase64String(CurrentContent.ToString());
                                currentStringList.Add(System.Text.Encoding.Unicode.GetString(bytes));
                            }
                            else
                            {
                                currentStringList.Add(CurrentContent.ToString());
                            }
                        }
                        else if (reader.Name.Equals("registry"))
                        {
                        }
                        else if (reader.Name.Equals(CurrentKind.ToString()))
                        {
                            if (RegValueEntryKind.SZ == CurrentKind)
                            {
                                if (isBase64Encoding)
                                {
                                    byte[] bytes = Convert.FromBase64String(CurrentContent.ToString());
                                    CurrentValue.SetStringValue(System.Text.Encoding.Unicode.GetString(bytes));
                                }
                                else
                                {
                                    CurrentValue.SetStringValue(CurrentContent.ToString());
                                }
                            }
                            else if (RegValueEntryKind.ExpandSZ == CurrentKind)
                            {
                                if (isBase64Encoding)
                                {
                                    byte[] bytes = Convert.FromBase64String(CurrentContent.ToString());
                                    CurrentValue.SetExpandedStringValue(System.Text.Encoding.Unicode.GetString(bytes));
                                }
                                else
                                {
                                    CurrentValue.SetExpandedStringValue(CurrentContent.ToString());
                                }
                            }
                            else if (RegValueEntryKind.DWord == CurrentKind)
                            {
                                string temp = CurrentContent.ToString();
                                if (temp.Contains("$$"))
                                {
                                    CurrentValue.SetEscapedIntValue(temp);
                                }
                                else
                                {
                                    CurrentValue.SetIntValue(int.Parse(temp));
                                }
                            }
                            else if (RegValueEntryKind.QWord == CurrentKind)
                            {
                                string temp = CurrentContent.ToString();
                                if (temp.Contains("$$"))
                                {
                                    CurrentValue.SetEscapedLongValue(temp);
                                }
                                else
                                {
                                    CurrentValue.SetLongValue(long.Parse(temp));
                                }
                            }
                            else if (RegValueEntryKind.MultiSZ == CurrentKind)
                            {
                                CurrentValue.SetMultiStringValue(currentStringList);
                                currentStringList.Clear();
                            }
                            else
                            {
                                CurrentValue.SetBinaryType(CurrentKind, DecodeHexByteArray(CurrentContent.ToString()));
                            }
                            CurrentValue = null;
                        }
                        break;
                    }
                }
            }
        }
Example #26
0
 /// <summary>
 /// The constructor takes the content of a .REG file, the expected header (=first line of the .REG file) and import options (if any)
 /// </summary>
 /// <param name="content">Content of the .REG file</param>
 public XmlRegFileImporter(string content)
 {
     Content = content;
     Result  = null;
 }
Example #27
0
        private void CompareRecursive(RegKeyEntry key1, RegKeyEntry key2)
        {
            // Acquire keys and sort them.
            List <string> sortedNames;

            sortedNames = key1.Keys.Keys.ToList();
            sortedNames.Sort();
            foreach (string keyName in sortedNames)
            {
                RegKeyEntry subkey1 = key1.Keys[keyName];
                if (key2.Keys.ContainsKey(keyName))
                {
                    RegKeyEntry subkey2 = key2.Keys[keyName];
                    CompareRecursive(subkey1, subkey2);
                }
                else
                {
                    // two forms are supported: either a single key (as in FOO=BAR), or a complete path (as in HKLM\BLA\BLUB=HKCU\SMA\BU)
                    // we have a mismatch. It may happen that the key needs to be renamed, and then compared again

                    if (Aliases.ContainsKey(keyName.ToLower()))
                    {
                        string aliasedName = Aliases[keyName.ToLower()].ToLower();
                        if (key2.Keys.ContainsKey(aliasedName))
                        {
                            RegKeyEntry subkey2 = key2.Keys[aliasedName];
                            CompareRecursive(subkey1, subkey2);
                        }
                        else
                        {
                            MissingKeysIn2.Add(subkey1);
                        }
                    }
                    else
                    {
                        MissingKeysIn2.Add(subkey1);
                    }
                }
            }


            if (key1.DefaultValue != null)
            {
                Debug.Assert(key1.DefaultValue.IsDefaultValue);
                if (key2.DefaultValue == null)
                {
                    MissingValuesIn2.Add(new MissingValue(key1, key1.DefaultValue));
                }
            }
            else if (key2.DefaultValue != null)
            {
                Debug.Assert(key2.DefaultValue.IsDefaultValue);
                MissingValuesIn1.Add(new MissingValue(key2, key2.DefaultValue));
            }


            sortedNames = key1.Values.Keys.ToList();
            sortedNames.Sort();
            foreach (string valueName in sortedNames)
            {
                RegValueEntry value1 = key1.Values[valueName];
                if (key2.Values.ContainsKey(valueName))
                {
                    CompareValues(key1, value1, key2.Values[valueName]);
                }
                else
                {
                    MissingValuesIn2.Add(new MissingValue(key1, value1));
                }
            }

            sortedNames = key2.Values.Keys.ToList();
            sortedNames.Sort();
            foreach (string valueName in sortedNames)
            {
                RegValueEntry value2 = key2.Values[valueName];
                if (!key1.Values.ContainsKey(valueName))
                {
                    MissingValuesIn1.Add(new MissingValue(key2, value2));
                }
            }

            sortedNames = key2.Keys.Keys.ToList();
            sortedNames.Sort();
            foreach (string keyName in sortedNames)
            {
                if (!key1.Keys.ContainsKey(keyName))
                {
                    if (Aliases.ContainsKey(keyName.ToLower()))
                    {
                        string aliasedName = Aliases[keyName.ToLower()].ToLower();
                        if (!key1.Keys.ContainsKey(aliasedName))
                        {
                            MissingKeysIn1.Add(key2.Keys[keyName]);
                        }
                    }
                    else
                    {
                        MissingKeysIn1.Add(key2.Keys[keyName]);
                    }
                }
            }
        }