Tuesday, April 6, 2010

XML text replacement

A new product I was working on required a UI prompt then replace in a config file, this case using xml. In the past I have tried using the XML File Changes view in InstallShield 2009 (and before) and had limited success. The task of supplying a property instead of a static value was simple enough, but the issue I kept encountering was additional decoration that was being placed on the xml file, which in turn caused the problems when trying to load the xml file at a later time, usually by the intended application.

In the past I had found some batch util that did text replace, but I found I was not comfortable putting this in my installer because batch files are like a black hole that get no reporting back to the log file. After the success with my previous post I felt confident that I could find something in c# that would allow this and reporting back to the log, plus it allowed me to learn a little more c#.

After searching I found a suitable sample. What this offered was to treat the xml file like a flat file because all I wanted was to replace a matched phrase with the value supplied by the user during install. This used the System.Text.RegularExpressions namespace to do the matching. What this sample does is load the file at once and do a match across the entire file. Because of this, it is only recommended for small files. Larger files will cause this to fail. With that in mind, here is my code used:

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Deployment.WindowsInstaller;

namespace TextReplaceUtil
{
public class CustomActions
{
[CustomAction]
public static ActionResult TextReplaceAll(Session session)
{
string AutoLogPrefix = "### COMPANY MESSAGE ### ";

// setup variables using properties from MSI database
// this will come from a deferred action, which will be comma delimited
// format of "What to find", "What to replace with", "absolute path + name of file"
string CustomActionData = session["CustomActionData"];
string[] MSIProperties = CustomActionData.Split(',');
string TextToReplace = MSIProperties[0];
string ReplaceWith = MSIProperties[1];
string FilePath = MSIProperties[2];

session.Log(AutoLogPrefix + "Begin TextReplaceAll action, values passed are: " + CustomActionData);

try
{
StreamReader reader = new StreamReader(FilePath);
string content = reader.ReadToEnd();
reader.Close();

content = Regex.Replace(content, TextToReplace, ReplaceWith);
StreamWriter writer = new StreamWriter(FilePath);
writer.Write(content);
writer.Close();
session.Log(AutoLogPrefix + "Successfully wrote to file " + FilePath);
}

catch(Exception e)
{
session.Log(AutoLogPrefix + "exception thrown was " + e.Message);
return ActionResult.Failure;
}
return ActionResult.Success;
}
}
}



Just like before, you use the .ca.dll after it is built. In this case you also need to create a set property action in addition to the MSI DLL action to build up the CustomActionData value that we will be using here. The comment in the code indicates the format of what is expected and the order it should arrive in. With a utility like this, you can use this for any need for text replacement in your installers.

No comments:

Post a Comment