Loren Halvorson's Blog

If your only tool is a hammer...

October 2005 - Posts

Adding UI to your WiX installer

This is part two of my posts on features I recently discovered in WiX. Adding a UI to your installer is extremely simple now. Far easier than the way I used to do it. This technique is mentioned in the excellent WiX tutorial. It is also being used by the Votive install which you can look at in the WiX source on SourceForge.

Here is a sample similar to my previous one that demonstrated XmlFile but this time just adds a UI. If you are interested in seeing this work it only takes a couple minutes to try, get a hold of the latest WiX binaries from SourceForge and follow these steps.

This is just any old file we want to install, save it to a directory.

--sample.config--

<configuration>
  <appSettings>
    <add key="ServiceLocation" value="localhost"/>
  </appSettings>
</configuration>

Then you should create a file named License.rtf using Word or some other RTF editor in the same directory.

Here is the WiX script that includes the UIRef element, save it to the same directory. I've highlighted the new parts.

--wixsetup.wxs--

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">

  <Product Id="88DC12AD-74D8-415a-AFE7-83C1BE05A3EB" Name="WixUI Sample" Language="1033" Version="1.0.0" Manufacturer="WixUI">
    <Package Id="????????-????-????-????-????????????" Description="Sample showing WixUI used in a WiX setup." Comments="Sample showing WixUI used in a WiX setup." InstallerVersion="200" Compressed="yes" />

    <Media Id="1" Cabinet="Product.cab" EmbedCab="yes" />
   
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="WiXSampl" LongName="WixUI WiX Sample">
            <Component Id="ProgramComponent" Guid="3D62CA0A-DE38-4687-95BE-1B200670E35A">
              <File Id="_F86C7B1F61C44480BEFA68A2D045D7BB" Name="SAMPLE.CON" LongName="sample.config" src="sample.config" Vital="yes" DiskId="1"/>
              <File Id="_F86C7B1F61C44480BEFA68A2D045D7BC" Name="License.rtf" LongName="license.rtf" src="license.rtf" Vital="yes" DiskId="1"/>
            </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="Complete" Title="WixUI Sample" Level="1">
      <ComponentRef Id="ProgramComponent" />
    </Feature>
   
    <UIRef Id="WixUI" />

  </Product>
</Wix>


Then run the following batch file also in that same directory to compile the MSI (fix up wix_home to where you unzipped WiX).

--CreateSetup.bat--

@set wix_home=C:\wix
"%wix_home%\candle.exe" wixsetup.wxs
"%wix_home%\light.exe" -out wixsetup.msi wixsetup.wixobj "%wix_home%\ca\wixca.wixlib" "%wix_home%\ui\wixui_featuretree.wixlib"

Viola, you now have a UI. Run wixsetup.msi to see it.

The next obvious question is how do you modify this UI by inserting additional wizard pages, or removing ones that are there, and I apologize, but I haven't learned how to do this yet. But this is a good start. If anyone else has a "recipe" on how to add a new custom page to any of the WixUI flavors (WixUI_Mondo, WixUI_FeatureTree, or WixUI_Minimal), please let me know.

Using WiX to fix up XML files when deploying to different environments

I met Rob Mensching out at PDC and he pointed out a new feature in WiX that can be used to solve the problem of deploying XML configuration files into many different environments. It's called <XmlFile> and allows you to poke values into an XML file while deploying them. This is the same problem I try to solve with the XmlPreprocessor tool.

I was curious how it worked, so I wrote a sample to illustrate.

Let's start out with a configuration file that has a value in it that needs to change depending on the environment into which we are deploying. In this case we want to replace "localhost" with a different value for Dev, Test, and Production.

--sample.config--

<configuration>
  <appSettings>
    <add key="ServiceLocation" value="localhost"/>
  </appSettings>
</configuration>

Here is the WiX script that creates an MSI that uses XmlFile. The value for [SERVICELOCATION] can be passed into the installer on the command line or collected in a UI.

--wixsetup.wxs--

<?xml version="1.0"?>
<Wix xmlns="
http://schemas.microsoft.com/wix/2003/01/wi">

  <Product Id="88DC12AD-74D8-415a-AFE7-83C1BE05A3EB" Name="XmlFile Sample" Language="1033" Version="1.0.0" Manufacturer="XmlFile">
    <Package Id="????????-????-????-????-????????????" Description="Sample showing XmlFile used in a WiX setup." Comments="Sample showing XmlFile used in a WiX setup." InstallerVersion="200" Compressed="yes" />

    <Media Id="1" Cabinet="Product.cab" EmbedCab="yes" />
   
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="WiXSampl" LongName="XmlFile WiX Sample">
            <Component Id="ProgramComponent" Guid="3D62CA0A-DE38-4687-95BE-1B200670E35A">
              <File Id="_F86C7B1F61C44480BEFA68A2D045D7BB" Name="SAMPLE.CON" LongName="sample.config" src="sample.config" Vital="yes" DiskId="1"/>

              <XmlFile Id="ModifyServiceLocation" Action="setValue" ElementPath="/configuration/appSettings/add[@key='ServiceLocation']/@value" File="[INSTALLLOCATION]\sample.config" Value="[SERVICELOCATION]"/>

            </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="Complete" Title="XmlFile Sample" Level="1">
      <ComponentRef Id="ProgramComponent" />
    </Feature>

  </Product>
</Wix>

If you want to try this yourself it only takes a couple minutes, get a hold of the latest WiX binaries from SourceForge, Then run the following batch file to compile the MSI (fix up wix_home to where you unzipped WiX).

--CreateSetup.bat--

set wix_home=C:\Tools\WiX
"%wix_home%\candle.exe" wixsetup.wxs
"%wix_home%\light.exe" -out wixsetup.msi wixsetup.wixobj "%wix_home%\ca\wixca.wixlib"

Deploy the MSI, passing in the parameters like this:

wixsetup.msi SERVICELOCATION=ABCDEFG

After deployment the configuration file will look like this:

--sample.config--

<configuration>
  <appSettings>
    <add key="ServiceLocation" value="ABCDEFG"/>
  </appSettings>
</configuration>

That's good stuff.

More Posts