Sunday, November 28, 2010

WiX: Set properties from Custom Action. Source code.

This is to share a source code for the the previous installment. The whole project can be downloaded here.
You register the action in a wxs file:

    <Binary Id="Tools.Wix.Actions.dll" 
SourceFile="..\..\..\Tools\src\Tools.Wix.Actions\bin\Debug\Tools.Wix.Actions.CA.dll" />
    <CustomAction Id="Tools.Wix.Actions.SetProperties" BinaryKey="Tools.Wix.Actions.dll" 
DllEntry="SetInstallEnvironmentProperties" Execute="immediate" />
    

And then define when to run it:

    <InstallExecuteSequence>
      <Custom Action="Tools.Wix.Actions.SetProperties" Before="CostFinalize" />
    </InstallExecuteSequence>

Just to repeat it again, in my case I set it as Before CostFinalize, as part of the job was to configure a property for an INSTALLLOCATION directory.

The code itself is very straightforward:

 
 
using Microsoft.Deployment.WindowsInstaller;
using System;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace Tools.Wix.Actions
{
    public class CustomActions
    {
        [CustomAction]
        public static ActionResult SetInstallEnvironmentProperties(Session session)
        {
            try
            {
                session.Log("Begin CustomActions.SetInstallEnvironmentProperties");
 
                session["INSTALLLOCATION"] = "C:\\FORIS4\\bbb\\";
 
                string environmentPath = session["ENVIRONMENT"];
 
                session.Log("ENVIRONMENT: " + environmentPath);
 
                InstallEnvironment environment = null;
 
                if (!String.IsNullOrEmpty(environmentPath))
                {
                    try
                    {
                        environment = InstallEnvironment.ParseFromFile(environmentPath);
                        session.Log("Environment values:" + 
GetEnvironmentAsStringData(environment).ToString());
 
                        if (environment.Configuration == null)
                        {
                            return ActionResult.Success;
                        }
                        foreach (var entry in environment.Configuration)
                        {
                            session[entry.Name] = entry.Value;
                        }
                    }
                    catch (Exception ex)
                    {
                        HandleException(session, ex);
                        return ActionResult.Failure;
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(session, ex);
            }
 
            return ActionResult.Success;
        }
 
        private static void HandleException(Session session, Exception ex)
        {
            string errorMsg = "Exception during custom action execution:" + ex.ToString();
            session.Log(errorMsg);
            session.Message(InstallMessage.Error, new Record { FormatString = errorMsg });
        }
 
        private static StringBuilder 
GetEnvironmentAsStringData(InstallEnvironment environment)
        {
            var xs = new XmlSerializer(typeof(InstallEnvironment));
            var sb = new StringBuilder();
            using (var sw = new StringWriter(sb))
            {
                xs.Serialize(sw, environment);
            }
            return sb;
        }
    }
}

With addition of classes to create an environment property bag:

using System.Collections.Generic;
using System;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
 
namespace Tools.Wix.Actions
{
    [Serializable]
    public class InstallEnvironment
    {
        private List<Entry> _configuration = new List<Entry>();
 
        public string Name { get; set; }
        public string Description { get; set; }
 
        public List<Entry> Configuration
        {
            get { return _configuration; }
            set { _configuration = value; }
        }
 
        public static InstallEnvironment ParseFromFile(string filePath)
        {
            return DeserializeFromFile(filePath, typeof(InstallEnvironment)) as InstallEnvironment;
        }
 
        private static object DeserializeFromFile(string path, Type type)
        {
            object ctc = null;
            if (path != null || path != String.Empty)
            {
                XmlTextReader reader = null;
 
                try
                {
                    var xs = new XmlSerializer(type);
                    reader = new XmlTextReader(path);
                    reader.Normalization = false;
 
                    ctc = xs.Deserialize(reader);
                    // Complete post deserialization work.
                    var idc = ctc as IDeserializationCallback;
                    if (idc != null)
                    {
                        idc.OnDeserialization(null);
                    }
                }
                catch (Exception e)
                {
                    throw new Exception
                        (
                        "Exception ocurred while reading type " + type.FullName + 
"from the file " + path + "\r\n" + e +
                        "\r\nType:" + e.GetType().Name,
                        e
                        );
                }
                finally
                {
                    if (reader != null) reader.Close();
                }
            }
            return ctc;
        }
    }
}

And

using System;
using System.Xml.Serialization;
 
namespace Tools.Wix.Actions
{
    [Serializable]
    public class Entry
    {
        [XmlAttribute]
        public string Name { get; set; }
        [XmlAttribute]
        public string Value { get; set; }
        [XmlAttribute]
        public string Description { get; set; }
    }
}

Here is the sample environment file:

<?xml version="1.0" encoding="utf-16"?>
<InstallEnvironment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Test environment</Name>
  <Description>Environment for a test platform</Description>
  <Configuration>
    <Entry Name="INSTALLLOCATION" Value="c:\test5\bbb\" 
Description="Location where to install the application" />
    <Entry Name="TESTURL" Value="http://test.com/" Description="Test url" />
  </Configuration>
</InstallEnvironment>

Hope someone can find it useful Smile.
Yours, Stan

No comments: