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); } }
/// <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; }
/// <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); }
/// <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; }
/// <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); }
/// <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); }
/// <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); }
/// <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); } }
/// <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); } }
/// <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); }
/// <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); }
/// <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(); } } }
/// <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(); }
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(); } } }
/// <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); } }
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)); } } } }
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)); } } } }
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)); } } }
/// <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); } }
/// <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); }
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); } } }
/// <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); }
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; } } } }
/// <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; }
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]); } } } }