Here is the source code of the utility that generates Wix 3 markup (including binary, dword and multi-string registry values):
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
namespace AbsReg2Wix
{
public class Program
{
#region Constants
private const string NS_URI = "http://schemas.microsoft.com/wix/2006/wi";
private const string RegEditorVersionPattern = @"Windows\sRegistry\sEditor\sVersion\s(?<RegEditorVersion>.*)";
private const string RegKeyPattern = @"\[(?<RegistryHive>[^\\]*)\\(?<RegistryKey>.*)\]";
private const string RegNameValuePattern = "\\\"(?<Name>.*)\\\"=(?<Value>\\\"?[^\\\\\\\"]*)(?<MultiLine>\\\\?)";
private const RegexOptions DefaultRegexOptions = RegexOptions.Multiline |
RegexOptions.IgnorePatternWhitespace |
RegexOptions.CultureInvariant;
#endregion
#region Methods
/// <summary>
/// Main applciation entry point
/// </summary>
/// <param name="args">The args.</param>
private static void Main(string[] args)
{
if (args.Length != 4)
{
PrintUsageInstructions();
return;
}
if (File.Exists(args[1]))
{
ConvertRegistryFileToWix(args[1], args[3]);
Console.WriteLine("Successfully completed conversion.");
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
else
{
Console.WriteLine(@"Input file {0} not found.", args[1]);
}
}
/// <summary>
/// Prints the usage instructions.
/// </summary>
private static void PrintUsageInstructions()
{
Console.WriteLine("Syntax: AbsReg2Wix.exe /in <Input File (.reg)> /out <Output File>");
}
/// <summary>
/// Convert a .reg file into a .wsx file
/// </summary>
/// <param name="inputPath">The input path.</param>
/// <param name="outputPath">The output path.</param>
private static void ConvertRegistryFileToWix(string inputPath, string outputPath)
{
try
{
using (var reader = new StreamReader(inputPath))
{
string regEditorVersion = string.Empty;
bool isRegEditorVersionFound = false;
// Initialize Regex
var regEditorVersionRegex = new Regex(RegEditorVersionPattern, DefaultRegexOptions);
var regKeyRegex = new Regex(RegKeyPattern, DefaultRegexOptions);
var regNameValueRegex = new Regex(RegNameValuePattern, DefaultRegexOptions);
// Create xml document for output
var xDoc = new XmlDocument();
xDoc.AppendChild(xDoc.CreateProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
xDoc.AppendChild(xDoc.CreateComment(
string.Format(
"{0}Following code was generated by AbsReg2Wix tool.{0}Tool Version: {1}{0}Date: {2}{0}Command Line: {3}\n",
"\n\t", Assembly.GetExecutingAssembly().GetName().Version,
DateTime.Now.ToString("F"),
Environment.CommandLine)));
XmlElement includeElement = xDoc.CreateElement("Include", NS_URI);
XmlElement componentElement = null,
regKeyElement = null,
registryValueElement = null;
bool multiLine = false;
var rawValueBuilder = new StringBuilder();
while (!reader.EndOfStream)
{
string regFileLine = reader.ReadLine().Trim();
if (!isRegEditorVersionFound)
{
var regEditorVersionMatch = regEditorVersionRegex.Match(regFileLine);
if (regEditorVersionMatch.Success)
{
regEditorVersion = regEditorVersionMatch.Groups["RegEditorVersion"].Value;
includeElement.AppendChild(
xDoc.CreateComment("Registry Editor Version: " + regEditorVersion));
isRegEditorVersionFound = true;
}
}
var regKeyMatch = regKeyRegex.Match(regFileLine);
// Registry Key line found
if (regKeyMatch.Success)
{
if (componentElement != null)
{
componentElement.AppendChild(regKeyElement);
includeElement.AppendChild(componentElement);
}
componentElement = xDoc.CreateElement("Component", NS_URI);
var idAttr = xDoc.CreateAttribute("Id");
idAttr.Value = "Comp_" + GetMD5HashForString(regFileLine);
componentElement.Attributes.Append(idAttr);
var guidAttr = xDoc.CreateAttribute("Guid");
guidAttr.Value = Guid.NewGuid().ToString();
componentElement.Attributes.Append(guidAttr);
regKeyElement = xDoc.CreateElement("RegistryKey", NS_URI);
var hiveAttr = xDoc.CreateAttribute("Root");
hiveAttr.Value = GetShortHiveName(regKeyMatch.Groups["RegistryHive"].Value);
regKeyElement.Attributes.Append(hiveAttr);
var keyAttr = xDoc.CreateAttribute("Key");
keyAttr.Value = regKeyMatch.Groups["RegistryKey"].Value;
regKeyElement.Attributes.Append(keyAttr);
var actionAttr = xDoc.CreateAttribute("Action");
actionAttr.Value = "createAndRemoveOnUninstall";
regKeyElement.Attributes.Append(actionAttr);
}
var regNameValueMatch = regNameValueRegex.Match(regFileLine);
// Registry Name/Value pair line found
if (regNameValueMatch.Success)
{
registryValueElement = xDoc.CreateElement("RegistryValue", NS_URI);
var nameAttr = xDoc.CreateAttribute("Name");
nameAttr.Value = regNameValueMatch.Groups["Name"].Value;
registryValueElement.Attributes.Append(nameAttr);
var actionAttr = xDoc.CreateAttribute("Action");
actionAttr.Value = "write";
registryValueElement.Attributes.Append(actionAttr);
if (string.IsNullOrEmpty(regNameValueMatch.Groups["MultiLine"].Value))
{
string valueType, actualValue;
ParseRegistryValue(regNameValueMatch.Groups["Value"].Value, out valueType,
out actualValue);
var typeAttr = xDoc.CreateAttribute("Type");
typeAttr.Value = valueType;
registryValueElement.Attributes.Append(typeAttr);
var valueAttr = xDoc.CreateAttribute("Value");
valueAttr.Value = actualValue;
registryValueElement.Attributes.Append(valueAttr);
regKeyElement.AppendChild(registryValueElement);
}
else
{
multiLine = true;
rawValueBuilder.Append(regNameValueMatch.Groups["Value"].Value
.Replace("\\", string.Empty));
}
}
else if (multiLine)
{
if (regFileLine.IndexOf("\\") != -1)
{
rawValueBuilder.Append(regFileLine.Replace("\\", string.Empty));
}
else
{
rawValueBuilder.Append(regFileLine);
string valueType, actualValue;
ParseRegistryValue(rawValueBuilder.ToString(), out valueType, out actualValue);
var typeAttr = xDoc.CreateAttribute("Type");
typeAttr.Value = valueType;
registryValueElement.Attributes.Append(typeAttr);
var valueAttr = xDoc.CreateAttribute("Value");
valueAttr.Value = actualValue;
registryValueElement.Attributes.Append(valueAttr);
regKeyElement.AppendChild(registryValueElement);
rawValueBuilder.Remove(0, rawValueBuilder.Length);
multiLine = false;
}
}
}
if (componentElement != null)
{
componentElement.AppendChild(regKeyElement);
includeElement.AppendChild(componentElement);
}
xDoc.AppendChild(includeElement);
xDoc.Save(outputPath);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// Parses the registry value.
/// </summary>
/// <param name="rawValue">The raw value.</param>
/// <param name="valueType">Type of the value.</param>
/// <param name="actualValue">The actual value.</param>
private static void ParseRegistryValue(string rawValue, out string valueType, out string actualValue)
{
if (rawValue.IndexOf("\"") != -1)
{
valueType = "string";
actualValue = rawValue.Substring(1, rawValue.Length - 2);
}
else if (rawValue.IndexOf("dword:") != -1)
{
valueType = "integer";
actualValue = rawValue.Replace("dword:", string.Empty);
}
else if (rawValue.IndexOf("hex:") != -1)
{
valueType = "binary";
actualValue = rawValue.Replace("hex:", string.Empty)
.Replace(",", string.Empty)
.ToUpper();
}
else if (rawValue.IndexOf("hex(7):") != -1)
{
valueType = "multiString";
string[] hexStrings = rawValue.Replace("hex(7):", string.Empty).Split(',');
var bytes = new byte[hexStrings.Length];
for (int i = 0; i < hexStrings.Length; i++)
{
bytes[i] = byte.Parse(hexStrings[i], NumberStyles.HexNumber);
}
actualValue = Encoding.Unicode.GetString(bytes).Replace("\0", "[~]");
}
else
{
valueType = "string";
actualValue = rawValue;
}
}
/// <summary>
/// Gets the short name of the registry hive.
/// </summary>
/// <param name="fullHiveName">Full name of the hive.</param>
/// <returns></returns>
private static string GetShortHiveName(string fullHiveName)
{
switch (fullHiveName)
{
case "HKEY_LOCAL_MACHINE":
return "HKLM";
case "HKEY_CLASSES_ROOT":
return "HKCR";
case "HKEY_USERS":
return "HKU";
case "HKEY_CURRENT_USER":
return "HKCU";
default:
throw new ArgumentException(string.Format("Registry Hive unsupported by Wix: {0}.",
fullHiveName));
}
}
/// <summary>
/// Gets the MD5 hash for string.
/// </summary>
/// <param name="inputString">The input string.</param>
/// <returns></returns>
private static string GetMD5HashForString(string inputString)
{
MD5 hashAlg = MD5.Create();
byte[] originalInBytes = Encoding.ASCII.GetBytes(inputString);
byte[] hashedOriginal = hashAlg.ComputeHash(originalInBytes);
String outputString = Convert.ToBase64String(hashedOriginal)
.Replace("/", "aa")
.Replace("+", "bb")
.Replace("=", "cc");
return outputString;
}
#endregion
}
}