The main page for the series is here.
Introduction
Say, we need to change an xml config file based on the environment our program will run in. The most straightforward way of achieving that will be passing values which will go to the xml config file through public properties from the command line.
For this example I will be using C# console application. Here is the content of Program.cs file:
using System;using System.Collections.Generic;using System.Text;using System.Configuration;
namespace
and here is the App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Key1" value="" />
<add key="Key2" value="" />
<add key="Key3" value="" />
</appSettings>
</configuration>
Here is the source:
<?
<
<!--
</
Here are commands to build the msi:
candle.exe Minimal.wxslight.exe -out Minimal.msi Minimal.wixobj d:\wix\wixca.wixlib
or if you prefer MSBuild:
While this solution certainly works it has lots of drawbacks. Just to name few - it has problem with maintenace mode (who will provide those values in the command line?) and it creates support issues when amount of parameters will increase.
So, to address those issues we can try another solution which is based on using Custom Tables to store the configuration information.
MSI allows us to create custom tables in order to allow developers create data-driven installation. In Wix we are using <CustomTable> and <Column> elements to describe the layout of the custom table. To add the data to the custom table we are using <Row> and <Data> elements.
Insert this description of our custom table after <Media> element in our Wix source file:
<CustomTable Id="EnvironmentSettings"> <Column Id="Id" Category="Identifier" PrimaryKey="yes" Type="int" Width="4" /> <Column Id="Environment" Category="Text" Type="string" PrimaryKey="no" /> <Column Id="Key" Category="Text" Type="string" PrimaryKey="no" /> <Column Id="Value" Category="Text" Type="string" PrimaryKey="no" /> <Row> <Data Column="Id">1</Data> <Data Column="Environment">Dev</Data> <Data Column="Key">KEY1</Data> <Data Column="Value">Dev1</Data> </Row> <Row> <Data Column="Id">2</Data> <Data Column="Environment">Dev</Data> <Data Column="Key">KEY2</Data> <Data Column="Value">Dev2</Data> </Row> <Row> <Data Column="Id">3</Data> <Data Column="Environment">Dev</Data> <Data Column="Key">KEY3</Data> <Data Column="Value">Dev3</Data> </Row> <Row> <Data Column="Id">4</Data> <Data Column="Environment">Test</Data> <Data Column="Key">KEY1</Data> <Data Column="Value">Test1</Data> </Row> <Row> <Data Column="Id">5</Data> <Data Column="Environment">Test</Data> <Data Column="Key">KEY2</Data> <Data Column="Value">Test2</Data> </Row> <Row> <Data Column="Id">6</Data> <Data Column="Environment">Test</Data> <Data Column="Key">KEY3</Data> <Data Column="Value">Test3</Data> </Row></CustomTable>
Id attribute of the <CustomTable> element defines that the name of our custom table in the installation database will be EnvironmentSettings. Our table will have four columns: Id, Environment, Key, and Value. At least one column must be a primary key and we will be using Id column as a primary key. Environment column will group our Key/Value pairs in the different sets of customization data.
Now, instead of passing Key1, Key2, and Key3 in the command line we need to pass just one parameter - ENVIRONMENT, which will be used in order to determine which set of data to use during installation to update the content of the configuration file. Because of this, we need to remove launch conditions from previous example and use these instead:
Now, when we have data, we need also custom action to set the values of KEY1, KEY2, and KEY3 properties depending on the value passed in the ENVIRONMENT public property. For simplicity sake we will be using VBScript custom action:
Here we store SPScript.vbs internally in installation database as embedded stream and use it as immediate custom action. We also need to schedule this custom action. Because script may fail to set the properties, for example, because of wrong value passed in ENVIRONMENT property in the command line, we want to have a launch condition on this as well. So, we need to schedule it before LaunchConditions and AppSearch actions. In this example I just use sequence number 1:
<InstallExecuteSequence>
<Custom Action="SetProperties" Sequence="1">Not Installed</Custom>
</InstallExecuteSequence>
<!-- In case script will fail --><Condition Message="Script has failed to set up the properties"> Installed OR (KEY1 AND KEY2 AND KEY3)</Condition>
Here is the content of the VBScript custom action file SPScript.vbs:
Function
Environment = Session.Property(
View.Execute
View.Close
End
Script is pretty straightforward. You can find more information on Windows Installer automation here.