/// <summary> /// Decompile the Registry table. /// </summary> /// <param name="table">The table to decompile.</param> private void DecompileRegistryTable(Table table) { foreach (Row row in table.Rows) { if (("-" == Convert.ToString(row[3]) || "+" == Convert.ToString(row[3]) || "*" == Convert.ToString(row[3])) && null == row[4]) { Wix.RegistryKey registryKey = new Wix.RegistryKey(); registryKey.Id = Convert.ToString(row[0]); Wix.RegistryRootType registryRootType; if (this.GetRegistryRootType(row.SourceLineNumbers, table.Name, row.Fields[1], out registryRootType)) { registryKey.Root = registryRootType; } registryKey.Key = Convert.ToString(row[2]); switch (Convert.ToString(row[3])) { case "+": registryKey.ForceCreateOnInstall = Wix.YesNoType.yes; break; case "-": registryKey.ForceDeleteOnUninstall = Wix.YesNoType.yes; break; case "*": registryKey.ForceDeleteOnUninstall = Wix.YesNoType.yes; registryKey.ForceCreateOnInstall = Wix.YesNoType.yes; break; } this.core.IndexElement(row, registryKey); } else { Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Id = Convert.ToString(row[0]); Wix.RegistryRootType registryRootType; if (this.GetRegistryRootType(row.SourceLineNumbers, table.Name, row.Fields[1], out registryRootType)) { registryValue.Root = registryRootType; } registryValue.Key = Convert.ToString(row[2]); if (null != row[3]) { registryValue.Name = Convert.ToString(row[3]); } if (null != row[4]) { string value = Convert.ToString(row[4]); if (value.StartsWith("#x", StringComparison.Ordinal)) { registryValue.Type = Wix.RegistryValue.TypeType.binary; registryValue.Value = value.Substring(2); } else if (value.StartsWith("#%", StringComparison.Ordinal)) { registryValue.Type = Wix.RegistryValue.TypeType.expandable; registryValue.Value = value.Substring(2); } else if (value.StartsWith("#", StringComparison.Ordinal) && !value.StartsWith("##", StringComparison.Ordinal)) { registryValue.Type = Wix.RegistryValue.TypeType.integer; registryValue.Value = value.Substring(1); } else { if (value.StartsWith("##", StringComparison.Ordinal)) { value = value.Substring(1); } if (0 <= value.IndexOf("[~]", StringComparison.Ordinal)) { registryValue.Type = Wix.RegistryValue.TypeType.multiString; if ("[~]" == value) { value = string.Empty; } else if (value.StartsWith("[~]", StringComparison.Ordinal) && value.EndsWith("[~]", StringComparison.Ordinal)) { value = value.Substring(3, value.Length - 6); } else if (value.StartsWith("[~]", StringComparison.Ordinal)) { registryValue.Action = Wix.RegistryValue.ActionType.append; value = value.Substring(3); } else if (value.EndsWith("[~]", StringComparison.Ordinal)) { registryValue.Action = Wix.RegistryValue.ActionType.prepend; value = value.Substring(0, value.Length - 3); } string[] multiValues = NullSplitter.Split(value); foreach (string multiValue in multiValues) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = multiValue; registryValue.AddChild(multiStringValue); } } else { registryValue.Type = Wix.RegistryValue.TypeType.@string; registryValue.Value = value; } } } else { registryValue.Type = Wix.RegistryValue.TypeType.@string; registryValue.Value = String.Empty; } this.core.IndexElement(row, registryValue); } } }
/// <summary> /// Converts the registry values to WiX regisry key element. /// </summary> /// <param name="sr">The registry file stream.</param> /// <param name="component">A WiX component reference.</param> /// <param name="root">The root key.</param> /// <param name="line">The current line.</param> private void ConvertValues(StreamReader sr, ref Wix.Component component, Wix.RegistryRootType root, string line) { string name = null; string value = null; Wix.RegistryValue.TypeType type; Wix.RegistryKey registryKey = new Wix.RegistryKey(); registryKey.Root = root; registryKey.Key = line; while (this.GetValue(sr, ref name, ref value, out type)) { Wix.RegistryValue registryValue = new Wix.RegistryValue(); ArrayList charArray; // Don't specifiy name for default attribute if (!string.IsNullOrEmpty(name)) { registryValue.Name = name; } registryValue.Type = type; switch (type) { case Wix.RegistryValue.TypeType.binary: registryValue.Value = value.Replace(",", string.Empty).ToUpper(); break; case Wix.RegistryValue.TypeType.integer: registryValue.Value = Int32.Parse(value, NumberStyles.HexNumber).ToString(); break; case Wix.RegistryValue.TypeType.expandable: charArray = this.ConvertCharList(value); value = string.Empty; // create the string, remove the terminating null for (int i = 0; i < charArray.Count; i++) { if ('\0' != (char)charArray[i]) { value += charArray[i]; } } registryValue.Value = value; break; case Wix.RegistryValue.TypeType.multiString: charArray = this.ConvertCharList(value); value = string.Empty; // Convert the character array to a string so we can simply split it at the nulls, ignore the final null null. for (int i = 0; i < (charArray.Count - 2); i++) { value += charArray[i]; } // Although the value can use [~] the preffered way is to use MultiStringValue string[] parts = value.Split("\0".ToCharArray()); foreach (string part in parts) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = part; registryValue.AddChild(multiStringValue); } break; case Wix.RegistryValue.TypeType.@string: // Remove \\ and \" value = value.ToString().Replace("\\\"", "\""); value = value.ToString().Replace(@"\\", @"\"); // Escape [ and ] value = value.ToString().Replace(@"[", @"[\[]"); value = value.ToString().Replace(@"]", @"[\]]"); // This undoes the duplicate escaping caused by the second replace value = value.ToString().Replace(@"[\[[\]]", @"[\[]"); // Escape $ value = value.ToString().Replace(@"$", @"$$"); registryValue.Value = value; break; default: throw new ApplicationException(String.Format("Did not recognize the type of reg value on line {0}", this.currentLineNumber)); } registryKey.AddChild(registryValue); } // Make sure empty keys are created if (null == value) { registryKey.ForceCreateOnInstall = Wix.YesNoType.yes; } component.AddChild(registryKey); }
/// <summary> /// Creates the shim component. /// </summary> /// <returns>Component for the shim.</returns> private Wix.Component GenerateShimComponent() { Wix.Component shimComponent = new Wix.Component(); if (Guid.Empty == this.shimGuid) { this.shimGuid = Guid.NewGuid(); } shimComponent.Id = "ThisApplicationShimDllComponent"; shimComponent.Guid = this.shimGuid.ToString("B"); Wix.File file = new Wix.File(); file.Id = "ThisApplicationShimDll"; file.Name = String.Concat(Path.GetFileNameWithoutExtension(this.entryFileRelativePath), "Shim.dll"); file.Vital = Wix.YesNoType.yes; file.KeyPath = Wix.YesNoType.yes; file.Source = this.shimPath; shimComponent.AddChild(file); // Add the CLSID and ProgId to the component. Wix.Class classId = new Wix.Class(); classId.Id = this.ShimClsid.ToString("B"); classId.Context = Wix.Class.ContextType.InprocServer32; if (null != this.Description && String.Empty != this.Description) { classId.Description = this.Description; } classId.ThreadingModel = Wix.Class.ThreadingModelType.apartment; file.AddChild(classId); Wix.ProgId progId = new Wix.ProgId(); progId.Id = this.ShimProgid; progId.Description = "Connect Class"; classId.AddChild(progId); // Add the Addin to the extended Office applications. foreach (OfficeAddinFabricator.OfficeApplications extendedOfficeApp in this.extendedOfficeApplications) { Wix.RegistryKey registryKey = new Wix.RegistryKey(); registryKey.Root = Wix.RegistryRootType.HKMU; registryKey.Key = String.Format("Software\\Microsoft\\Office\\{0}\\Addins\\{1}", OfficeAddinFabricator.OfficeApplicationStrings[(int)extendedOfficeApp], this.ShimProgid); shimComponent.AddChild(registryKey); Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Name = "Description"; registryValue.Value = "[ProductName] v[ProductVersion]"; registryValue.Type = Wix.RegistryValue.TypeType.@string; registryKey.AddChild(registryValue); registryValue = new Wix.RegistryValue(); registryValue.Name = "FriendlyName"; registryValue.Value = "[ProductName]"; registryValue.Type = Wix.RegistryValue.TypeType.@string; registryKey.AddChild(registryValue); registryValue = new Wix.RegistryValue(); registryValue.Name = "LoadBehavior"; registryValue.Value = "3"; registryValue.Type = Wix.RegistryValue.TypeType.integer; registryKey.AddChild(registryValue); } return(shimComponent); }
/// <summary> /// Harvest a registry key. /// </summary> /// <param name="registryKey">The registry key to harvest.</param> /// <param name="registryValues">The collected registry values.</param> private void HarvestRegistryKey(RegistryKey registryKey, ArrayList registryValues) { // harvest the sub-keys foreach (string subKeyName in registryKey.GetSubKeyNames()) { using (RegistryKey subKey = registryKey.OpenSubKey(subKeyName)) { this.HarvestRegistryKey(subKey, registryValues); } } string[] parts = GetPathParts(registryKey.Name); Wix.RegistryRootType root; switch (parts[0]) { case "HKEY_CLASSES_ROOT": root = Wix.RegistryRootType.HKCR; break; case "HKEY_CURRENT_USER": root = Wix.RegistryRootType.HKCU; break; case "HKEY_LOCAL_MACHINE": // HKLM\Software\Classes is equivalent to HKCR if (1 < parts.Length && parts[1].StartsWith(HKCRPathInHKLM, StringComparison.OrdinalIgnoreCase)) { root = Wix.RegistryRootType.HKCR; parts[1] = parts[1].Remove(0, HKCRPathInHKLM.Length); if (0 < parts[1].Length) { parts[1] = parts[1].TrimStart('\\'); } if (String.IsNullOrEmpty(parts[1])) { parts = new [] { parts[0] }; } } else { root = Wix.RegistryRootType.HKLM; } break; case "HKEY_USERS": root = Wix.RegistryRootType.HKU; break; default: // TODO: put a better exception here throw new Exception(); } // harvest the values foreach (string valueName in registryKey.GetValueNames()) { Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Action = Wix.RegistryValue.ActionType.write; registryValue.Root = root; if (1 < parts.Length) { registryValue.Key = parts[1]; } if (null != valueName && 0 < valueName.Length) { registryValue.Name = valueName; } object value = registryKey.GetValue(valueName); if (value is byte[]) // binary { StringBuilder hexadecimalValue = new StringBuilder(); // convert the byte array to hexadecimal foreach (byte byteValue in (byte[])value) { hexadecimalValue.Append(byteValue.ToString("X2", CultureInfo.InvariantCulture.NumberFormat)); } registryValue.Type = Wix.RegistryValue.TypeType.binary; registryValue.Value = hexadecimalValue.ToString(); } else if (value is int) // integer { registryValue.Type = Wix.RegistryValue.TypeType.integer; registryValue.Value = ((int)value).ToString(CultureInfo.InvariantCulture); } else if (value is string[]) // multi-string { registryValue.Type = Wix.RegistryValue.TypeType.multiString; if (0 == ((string[])value).Length) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = String.Empty; registryValue.AddChild(multiStringValue); } else { foreach (string multiStringValueContent in (string[])value) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = multiStringValueContent; registryValue.AddChild(multiStringValue); } } } else if (value is string) // string, expandable (there is no way to differentiate a string and expandable value in .NET 1.1) { registryValue.Type = Wix.RegistryValue.TypeType.@string; registryValue.Value = (string)value; } else { // TODO: put a better exception here throw new Exception(); } registryValues.Add(registryValue); } // If there were no subkeys and no values, we still need an element for this empty registry key. // But specifically avoid SOFTWARE\Classes because it shouldn't be harvested as an empty key. if (parts.Length > 1 && registryKey.SubKeyCount == 0 && registryKey.ValueCount == 0 && !String.Equals(parts[1], HKCRPathInHKLM, StringComparison.OrdinalIgnoreCase)) { Wix.RegistryValue emptyRegistryKey = new Wix.RegistryValue(); emptyRegistryKey.Root = root; emptyRegistryKey.Key = parts[1]; emptyRegistryKey.Type = Wix.RegistryValue.TypeType.@string; emptyRegistryKey.Value = String.Empty; emptyRegistryKey.Action = Wix.RegistryValue.ActionType.write; registryValues.Add(emptyRegistryKey); } }
/// <summary> /// Creates the shim component. /// </summary> /// <returns>Component for the shim.</returns> private Wix.Component GenerateShimComponent() { Wix.Component shimComponent = new Wix.Component(); if (Guid.Empty == this.shimGuid) { this.shimGuid = Guid.NewGuid(); } shimComponent.Id = "ThisApplicationShimDllComponent"; shimComponent.Guid = this.shimGuid.ToString("B"); Wix.File file = new Wix.File(); file.Id = "ThisApplicationShimDll"; file.Name = String.Concat(Path.GetFileNameWithoutExtension(this.entryFileRelativePath), "Shim.dll"); file.Vital = Wix.YesNoType.yes; file.KeyPath = Wix.YesNoType.yes; file.Source = this.shimPath; shimComponent.AddChild(file); // Add the CLSID and ProgId to the component. Wix.Class classId = new Wix.Class(); classId.Id = this.ShimClsid.ToString("B"); classId.Context = Wix.Class.ContextType.InprocServer32; if (null != this.Description && String.Empty != this.Description) { classId.Description = this.Description; } classId.ThreadingModel = Wix.Class.ThreadingModelType.apartment; file.AddChild(classId); Wix.ProgId progId = new Wix.ProgId(); progId.Id = this.ShimProgid; progId.Description = "Connect Class"; classId.AddChild(progId); // Add the Addin to the extended Office applications. foreach (OfficeAddinFabricator.OfficeApplications extendedOfficeApp in this.extendedOfficeApplications) { Wix.RegistryKey registryKey = new Wix.RegistryKey(); registryKey.Root = Wix.RegistryRootType.HKMU; registryKey.Key = String.Format("Software\\Microsoft\\Office\\{0}\\Addins\\{1}", OfficeAddinFabricator.OfficeApplicationStrings[(int)extendedOfficeApp], this.ShimProgid); shimComponent.AddChild(registryKey); Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Name = "Description"; registryValue.Value = "[ProductName] v[ProductVersion]"; registryValue.Type = Wix.RegistryValue.TypeType.@string; registryKey.AddChild(registryValue); registryValue = new Wix.RegistryValue(); registryValue.Name = "FriendlyName"; registryValue.Value = "[ProductName]"; registryValue.Type = Wix.RegistryValue.TypeType.@string; registryKey.AddChild(registryValue); registryValue = new Wix.RegistryValue(); registryValue.Name = "LoadBehavior"; registryValue.Value = "3"; registryValue.Type = Wix.RegistryValue.TypeType.integer; registryKey.AddChild(registryValue); } return shimComponent; }
/// <summary> /// Converts the registry values to WiX regisry key element. /// </summary> /// <param name="sr">The registry file stream.</param> /// <param name="component">A WiX component reference.</param> /// <param name="root">The root key.</param> /// <param name="line">The current line.</param> private void ConvertValues(StreamReader sr, ref Wix.Component component, Wix.RegistryRootType root, string line) { string name = null; string value = null; Wix.RegistryValue.TypeType type; Wix.RegistryKey registryKey = new Wix.RegistryKey(); registryKey.Root = root; registryKey.Key = line; while (this.GetValue(sr, ref name, ref value, out type)) { Wix.RegistryValue registryValue = new Wix.RegistryValue(); ArrayList charArray; // Don't specifiy name for default attribute if (!string.IsNullOrEmpty(name)) { registryValue.Name = name; } registryValue.Type = type; switch (type) { case Wix.RegistryValue.TypeType.binary: registryValue.Value = value.Replace(",", string.Empty).ToUpper(); break; case Wix.RegistryValue.TypeType.integer: registryValue.Value = Int32.Parse(value, NumberStyles.HexNumber).ToString(); break; case Wix.RegistryValue.TypeType.expandable: charArray = this.ConvertCharList(value); value = string.Empty; // create the string, remove the terminating null for (int i = 0; i < charArray.Count; i++) { if ('\0' != (char)charArray[i]) { value += charArray[i]; } } registryValue.Value = value; break; case Wix.RegistryValue.TypeType.multiString: charArray = this.ConvertCharList(value); value = string.Empty; // Convert the character array to a string so we can simply split it at the nulls, ignore the final null null. for (int i = 0; i < (charArray.Count - 2); i++) { value += charArray[i]; } // Although the value can use [~] the preffered way is to use MultiStringValue string[] parts = value.Split("\0".ToCharArray()); foreach (string part in parts) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = part; registryValue.AddChild(multiStringValue); } break; case Wix.RegistryValue.TypeType.@string: // Remove \\ and \" value = value.ToString().Replace("\\\"", "\""); value = value.ToString().Replace(@"\\", @"\"); // Escape [ and ] value = value.ToString().Replace(@"[", @"[\[]"); value = value.ToString().Replace(@"]", @"[\]]"); // This undoes the duplicate escaping caused by the second replace value = value.ToString().Replace(@"[\[[\]]", @"[\[]"); // Escape $ value = value.ToString().Replace(@"$", @"$$"); registryValue.Value = value; break; default: throw new ApplicationException(String.Format("Did not recognize the type of reg value on line {0}", this.currentLineNumber)); } registryKey.AddChild(registryValue); } // Make sure empty keys are created if (null == value) { registryKey.Action = Wix.RegistryKey.ActionType.createAndRemoveOnUninstall; } component.AddChild(registryKey); }
/// <summary> /// Harvest a registry key. /// </summary> /// <param name="registryKey">The registry key to harvest.</param> /// <param name="registryValues">The collected registry values.</param> private void HarvestRegistryKey(RegistryKey registryKey, ArrayList registryValues) { // harvest the sub-keys foreach (string subKeyName in registryKey.GetSubKeyNames()) { using (RegistryKey subKey = registryKey.OpenSubKey(subKeyName)) { this.HarvestRegistryKey(subKey, registryValues); } } string[] parts = GetPathParts(registryKey.Name); Wix.RegistryRootType root; switch (parts[0]) { case "HKEY_CLASSES_ROOT": root = Wix.RegistryRootType.HKCR; break; case "HKEY_CURRENT_USER": root = Wix.RegistryRootType.HKCU; break; case "HKEY_LOCAL_MACHINE": // HKLM\Software\Classes is equivalent to HKCR if (1 < parts.Length && parts[1].StartsWith(HKCRPathInHKLM, StringComparison.OrdinalIgnoreCase)) { root = Wix.RegistryRootType.HKCR; parts[1] = parts[1].Remove(0, HKCRPathInHKLM.Length); if (0 < parts[1].Length) { parts[1] = parts[1].TrimStart('\\'); } if (String.IsNullOrEmpty(parts[1])) { parts = new [] { parts[0] }; } } else { root = Wix.RegistryRootType.HKLM; } break; case "HKEY_USERS": root = Wix.RegistryRootType.HKU; break; default: // TODO: put a better exception here throw new Exception(); } // harvest the values foreach (string valueName in registryKey.GetValueNames()) { Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Action = Wix.RegistryValue.ActionType.write; registryValue.Root = root; if (1 < parts.Length) { registryValue.Key = parts[1]; } if (null != valueName && 0 < valueName.Length) { registryValue.Name = valueName; } object value = registryKey.GetValue(valueName); if (value is byte[]) // binary { StringBuilder hexadecimalValue = new StringBuilder(); // convert the byte array to hexadecimal foreach (byte byteValue in (byte[])value) { hexadecimalValue.Append(byteValue.ToString("X2", CultureInfo.InvariantCulture.NumberFormat)); } registryValue.Type = Wix.RegistryValue.TypeType.binary; registryValue.Value = hexadecimalValue.ToString(); } else if (value is int) // integer { registryValue.Type = Wix.RegistryValue.TypeType.integer; registryValue.Value = ((int)value).ToString(CultureInfo.InvariantCulture); } else if (value is string[]) // multi-string { registryValue.Type = Wix.RegistryValue.TypeType.multiString; if (0 == ((string[])value).Length) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = String.Empty; registryValue.AddChild(multiStringValue); } else { foreach (string multiStringValueContent in (string[])value) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = multiStringValueContent; registryValue.AddChild(multiStringValue); } } } else if (value is string) // string, expandable (there is no way to differentiate a string and expandable value in .NET 1.1) { registryValue.Type = Wix.RegistryValue.TypeType.@string; registryValue.Value = (string)value; } else { // TODO: put a better exception here throw new Exception(); } registryValues.Add(registryValue); } // If there were no subkeys and no values, we still need an element for this empty registry key. // But specifically avoid SOFTWARE\Classes because it shouldn't be harvested as an empty key. if (parts.Length > 1 && registryKey.SubKeyCount == 0 && registryKey.ValueCount == 0 && !String.Equals(parts[1], HKCRPathInHKLM, StringComparison.OrdinalIgnoreCase)) { Wix.RegistryValue emptyRegistryKey = new Wix.RegistryValue(); emptyRegistryKey.Root = root; emptyRegistryKey.Key = parts[1]; emptyRegistryKey.Type = Wix.RegistryValue.TypeType.@string; emptyRegistryKey.Value = String.Empty; emptyRegistryKey.Action = Wix.RegistryValue.ActionType.write; registryValues.Add(emptyRegistryKey); } }
/// <summary> /// Harvest a registry key. /// </summary> /// <param name="registryKey">The registry key to harvest.</param> /// <param name="registryValues">The collected registry values.</param> private void HarvestRegistryKey(RegistryKey registryKey, ArrayList registryValues) { // harvest the sub-keys foreach (string subKeyName in registryKey.GetSubKeyNames()) { using (RegistryKey subKey = registryKey.OpenSubKey(subKeyName)) { this.HarvestRegistryKey(subKey, registryValues); } } string[] parts = GetPathParts(registryKey.Name); Wix.RegistryRootType root; switch (parts[0]) { case "HKEY_CLASSES_ROOT": root = Wix.RegistryRootType.HKCR; break; case "HKEY_CURRENT_USER": root = Wix.RegistryRootType.HKCU; break; case "HKEY_LOCAL_MACHINE": root = Wix.RegistryRootType.HKLM; break; case "HKEY_USERS": root = Wix.RegistryRootType.HKU; break; default: // TODO: put a better exception here throw new Exception(); } // harvest the values foreach (string valueName in registryKey.GetValueNames()) { Wix.RegistryValue registryValue = new Wix.RegistryValue(); registryValue.Action = Wix.RegistryValue.ActionType.write; registryValue.Root = root; if (1 < parts.Length) { registryValue.Key = parts[1]; } if (null != valueName && 0 < valueName.Length) { registryValue.Name = valueName; } object value = registryKey.GetValue(valueName); if (value is byte[]) // binary { StringBuilder hexadecimalValue = new StringBuilder(); // convert the byte array to hexadecimal foreach (byte byteValue in (byte[])value) { hexadecimalValue.Append(byteValue.ToString("X2", CultureInfo.InvariantCulture.NumberFormat)); } registryValue.Type = Wix.RegistryValue.TypeType.binary; registryValue.Value = hexadecimalValue.ToString(); } else if (value is int) // integer { registryValue.Type = Wix.RegistryValue.TypeType.integer; registryValue.Value = ((int)value).ToString(CultureInfo.InvariantCulture); } else if (value is string[]) // multi-string { registryValue.Type = Wix.RegistryValue.TypeType.multiString; foreach (string multiStringValueContent in (string[])value) { Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); multiStringValue.Content = multiStringValueContent; registryValue.AddChild(multiStringValue); } } else if (value is string) // string, expandable (there is no way to differentiate a string and expandable value in .NET 1.1) { registryValue.Type = Wix.RegistryValue.TypeType.@string; registryValue.Value = (string)value; } else { // TODO: put a better exception here throw new Exception(); } registryValues.Add(registryValue); } }
/// <summary> /// Mutate the components. /// </summary> private void MutateComponents() { foreach (Wix.Component component in this.components) { SortedList indexedElements = CollectionsUtil.CreateCaseInsensitiveSortedList(); SortedList indexedRegistryValues = CollectionsUtil.CreateCaseInsensitiveSortedList(); // index all the File elements foreach (Wix.File file in component[typeof(Wix.File)]) { indexedElements.Add(String.Concat("file/", file.Id), file); } // group all the registry values by the COM element they would correspond to and // create a COM element for each group foreach (Wix.RegistryValue registryValue in component[typeof(Wix.RegistryValue)]) { if (Wix.RegistryValue.ActionType.write == registryValue.Action && Wix.RegistryRootType.HKCR == registryValue.Root && Wix.RegistryValue.TypeType.@string == registryValue.Type) { string index = null; string[] parts = registryValue.Key.Split('\\'); // create a COM element for COM registration and index it if (1 <= parts.Length) { if (0 == String.Compare(parts[0], "AppID", true)) { // only work with GUID AppIds here if (2 <= parts.Length && parts[1].StartsWith("{") && parts[1].EndsWith("}")) { index = String.Concat(parts[0], '/', parts[1]); if (!indexedElements.Contains(index)) { Wix.AppId appId = new Wix.AppId(); appId.Id = parts[1].ToUpper(CultureInfo.InvariantCulture); indexedElements.Add(index, appId); } } } else if (0 == String.Compare(parts[0], "CLSID", true)) { if (2 <= parts.Length) { index = String.Concat(parts[0], '/', parts[1]); if (!indexedElements.Contains(index)) { Wix.Class wixClass = new Wix.Class(); wixClass.Id = parts[1].ToUpper(CultureInfo.InvariantCulture); indexedElements.Add(index, wixClass); } } } else if (0 == String.Compare(parts[0], "Component Categories", true)) { // TODO: add support for this to the compiler } else if (0 == String.Compare(parts[0], "Interface", true)) { if (2 <= parts.Length) { index = String.Concat(parts[0], '/', parts[1]); if (!indexedElements.Contains(index)) { Wix.Interface wixInterface = new Wix.Interface(); wixInterface.Id = parts[1].ToUpper(CultureInfo.InvariantCulture); indexedElements.Add(index, wixInterface); } } } else if (0 == String.Compare(parts[0], "TypeLib")) { if (3 <= parts.Length) { // use a special index to ensure progIds are processed before classes index = String.Concat(".typelib/", parts[1], '/', parts[2]); if (!indexedElements.Contains(index)) { try { // TODO: properly handle hexadecimal in version Version version = new Version(parts[2]); Wix.TypeLib typeLib = new Wix.TypeLib(); typeLib.Id = parts[1].ToUpper(CultureInfo.InvariantCulture); typeLib.MajorVersion = version.Major; typeLib.MinorVersion = version.Minor; indexedElements.Add(index, typeLib); } catch // not a valid type library registry value { index = null; } } } } else if (parts[0].StartsWith(".")) { // extension } else // ProgId (hopefully) { // use a special index to ensure progIds are processed before classes index = String.Concat(".progid/", parts[0]); if (!indexedElements.Contains(index)) { Wix.ProgId progId = new Wix.ProgId(); progId.Id = parts[0]; indexedElements.Add(index, progId); } } } // index the RegistryValue element according to the COM element it corresponds to if (null != index) { SortedList registryValues = (SortedList)indexedRegistryValues[index]; if (null == registryValues) { registryValues = CollectionsUtil.CreateCaseInsensitiveSortedList(); indexedRegistryValues.Add(index, registryValues); } registryValues.Add(String.Concat(registryValue.Key, '/', registryValue.Name), registryValue); } } } // set various values on the COM elements from their corresponding registry values Hashtable indexedProcessedRegistryValues = new Hashtable(); foreach (DictionaryEntry entry in indexedRegistryValues) { Wix.ISchemaElement element = (Wix.ISchemaElement)indexedElements[entry.Key]; string parentIndex = null; SortedList registryValues = (SortedList)entry.Value; // element-specific variables (for really tough situations) string classAppId = null; bool threadingModelSet = false; foreach (Wix.RegistryValue registryValue in registryValues.Values) { string[] parts = registryValue.Key.ToLower(CultureInfo.InvariantCulture).Split('\\'); bool processed = false; if (element is Wix.AppId) { Wix.AppId appId = (Wix.AppId)element; if (2 == parts.Length) { if (null == registryValue.Name) { appId.Description = registryValue.Value; processed = true; } } } else if (element is Wix.Class) { Wix.Class wixClass = (Wix.Class)element; if (2 == parts.Length) { if (null == registryValue.Name) { wixClass.Description = registryValue.Value; processed = true; } else if (0 == String.Compare(registryValue.Name, "AppID", true)) { classAppId = registryValue.Value; processed = true; } } else if (3 == parts.Length) { Wix.Class.ContextType contextType = Wix.Class.ContextType.None; switch (parts[2]) { case "inprochandler": if (null == registryValue.Name) { if (null == wixClass.Handler) { wixClass.Handler = "1"; processed = true; } else if ("2" == wixClass.Handler) { wixClass.Handler = "3"; processed = true; } } break; case "inprochandler32": if (null == registryValue.Name) { if (null == wixClass.Handler) { wixClass.Handler = "2"; processed = true; } else if ("1" == wixClass.Handler) { wixClass.Handler = "3"; processed = true; } } break; case "inprocserver": contextType = Wix.Class.ContextType.InprocServer; break; case "inprocserver32": contextType = Wix.Class.ContextType.InprocServer32; break; case "localserver": contextType = Wix.Class.ContextType.LocalServer; break; case "localserver32": contextType = Wix.Class.ContextType.LocalServer32; break; case "progid": if (null == registryValue.Name) { Wix.ProgId progId = (Wix.ProgId)indexedElements[String.Concat(".progid/", registryValue.Value)]; // verify that the versioned ProgId appears under this Class element // if not, toss the entire element if (null == progId || wixClass != progId.ParentElement) { element = null; } else { processed = true; } } break; case "typelib": if (null == registryValue.Name) { foreach (DictionaryEntry indexedEntry in indexedElements) { string key = (string)indexedEntry.Key; Wix.ISchemaElement possibleTypeLib = (Wix.ISchemaElement)indexedEntry.Value; if (key.StartsWith(".typelib/") && 0 == String.Compare(key, 9, registryValue.Value, 0, registryValue.Value.Length, true)) { // ensure the TypeLib is nested under the same thing we want the Class under if (null == parentIndex || indexedElements[parentIndex] == possibleTypeLib.ParentElement) { parentIndex = key; processed = true; } } } } break; case "version": if (null == registryValue.Name) { wixClass.Version = registryValue.Value; processed = true; } break; case "versionindependentprogid": if (null == registryValue.Name) { Wix.ProgId progId = (Wix.ProgId)indexedElements[String.Concat(".progid/", registryValue.Value)]; // verify that the version independent ProgId appears somewhere // under this Class element - if not, toss the entire element if (null == progId || wixClass != progId.ParentElement) { // check the parent of the parent if (null == progId || null == progId.ParentElement || wixClass != progId.ParentElement.ParentElement) { element = null; } } processed = true; } break; } if (Wix.Class.ContextType.None != contextType) { wixClass.Context |= contextType; if (null == registryValue.Name) { if ((registryValue.Value.StartsWith("[!") || registryValue.Value.StartsWith("[#")) && registryValue.Value.EndsWith("]")) { parentIndex = String.Concat("file/", registryValue.Value.Substring(2, registryValue.Value.Length - 3)); processed = true; } } else if (0 == String.Compare(registryValue.Name, "ThreadingModel", true)) { Wix.Class.ThreadingModelType threadingModel; switch (registryValue.Value.ToLower(CultureInfo.InvariantCulture)) { case "apartment": threadingModel = Wix.Class.ThreadingModelType.apartment; processed = true; break; case "both": threadingModel = Wix.Class.ThreadingModelType.both; processed = true; break; case "free": threadingModel = Wix.Class.ThreadingModelType.free; processed = true; break; case "neutral": threadingModel = Wix.Class.ThreadingModelType.neutral; processed = true; break; case "rental": threadingModel = Wix.Class.ThreadingModelType.rental; processed = true; break; case "single": threadingModel = Wix.Class.ThreadingModelType.single; processed = true; break; default: continue; } if (!threadingModelSet || wixClass.ThreadingModel == threadingModel) { wixClass.ThreadingModel = threadingModel; threadingModelSet = true; } else { element = null; break; } } } } } else if (element is Wix.Interface) { Wix.Interface wixInterface = (Wix.Interface)element; if (2 == parts.Length && null == registryValue.Name) { wixInterface.Name = registryValue.Value; processed = true; } else if (3 == parts.Length) { switch (parts[2]) { case "proxystubclsid": if (null == registryValue.Name) { wixInterface.ProxyStubClassId = registryValue.Value.ToUpper(CultureInfo.InvariantCulture); processed = true; } break; case "proxystubclsid32": if (null == registryValue.Name) { wixInterface.ProxyStubClassId32 = registryValue.Value.ToUpper(CultureInfo.InvariantCulture); processed = true; } break; case "nummethods": if (null == registryValue.Name) { wixInterface.NumMethods = Convert.ToInt32(registryValue.Value, CultureInfo.InvariantCulture); processed = true; } break; case "typelib": if (0 == String.Compare("Version", registryValue.Name, true)) { parentIndex = String.Concat(parentIndex, registryValue.Value); processed = true; } else if (null == registryValue.Name) // TypeLib guid { parentIndex = String.Concat(".typelib/", registryValue.Value, '/', parentIndex); processed = true; } break; } } } else if (element is Wix.ProgId) { Wix.ProgId progId = (Wix.ProgId)element; if (null == registryValue.Name) { if (1 == parts.Length) { progId.Description = registryValue.Value; processed = true; } else if (2 == parts.Length) { if (0 == String.Compare(parts[1], "CLSID", true)) { parentIndex = String.Concat("CLSID/", registryValue.Value); processed = true; } else if (0 == String.Compare(parts[1], "CurVer", true)) { // this registry value should usually be processed second so the // version independent ProgId should be under the versioned one parentIndex = String.Concat(".progid/", registryValue.Value); processed = true; } } } } else if (element is Wix.TypeLib) { Wix.TypeLib typeLib = (Wix.TypeLib)element; if (null == registryValue.Name) { if (3 == parts.Length) { typeLib.Description = registryValue.Value; processed = true; } else if (4 == parts.Length) { switch (parts[3].ToLower(CultureInfo.InvariantCulture)) { case "flags": int flags = Convert.ToInt32(registryValue.Value, CultureInfo.InvariantCulture); if (0x1 == (flags & 0x1)) { typeLib.Restricted = Wix.YesNoType.yes; } if (0x2 == (flags & 0x2)) { typeLib.Control = Wix.YesNoType.yes; } if (0x4 == (flags & 0x4)) { typeLib.Hidden = Wix.YesNoType.yes; } if (0x8 == (flags & 0x8)) { typeLib.HasDiskImage = Wix.YesNoType.yes; } processed = true; break; case "helpdir": if (registryValue.Value.StartsWith("[") && (registryValue.Value.EndsWith("]") || registryValue.Value.EndsWith("]\\"))) { typeLib.HelpDirectory = registryValue.Value.Substring(1, registryValue.Value.LastIndexOf(']') - 1); processed = true; } break; } } else if (5 == parts.Length && 0 == String.Compare("win32", parts[4], true)) { typeLib.Language = Convert.ToInt32(parts[3], CultureInfo.InvariantCulture); if ((registryValue.Value.StartsWith("[!") || registryValue.Value.StartsWith("[#")) && registryValue.Value.EndsWith("]")) { parentIndex = String.Concat("file/", registryValue.Value.Substring(2, registryValue.Value.Length - 3)); } processed = true; } } } // index the processed registry values by their corresponding COM element if (processed) { indexedProcessedRegistryValues.Add(registryValue, element); } } // parent the COM element if (null != element) { if (null != parentIndex) { Wix.IParentElement parentElement = (Wix.IParentElement)indexedElements[parentIndex]; if (null != parentElement) { parentElement.AddChild(element); } } else { component.AddChild(element); } // special handling for AppID since it doesn't fit the general model if (null != classAppId) { Wix.AppId appId = (Wix.AppId)indexedElements[String.Concat("AppID/", classAppId)]; // move the Class element under the AppId (and put the AppId under its old parent) if (null != appId) { // move the AppId element ((Wix.IParentElement)appId.ParentElement).RemoveChild(appId); ((Wix.IParentElement)element.ParentElement).AddChild(appId); // move the Class element ((Wix.IParentElement)element.ParentElement).RemoveChild(element); appId.AddChild(element); } } } } // remove the RegistryValue elements which were converted into COM elements // that were successfully nested under the Component element foreach (DictionaryEntry entry in indexedProcessedRegistryValues) { Wix.ISchemaElement element = (Wix.ISchemaElement)entry.Value; Wix.RegistryValue registryValue = (Wix.RegistryValue)entry.Key; while (null != element) { if (element == component) { ((Wix.IParentElement)registryValue.ParentElement).RemoveChild(registryValue); break; } element = element.ParentElement; } } } }