Friday, October 16, 2009

WiX - Windows Installer XML

Windows Installer

The Windows Installer is an engine for the installation, maintenance, and removal of software on modern Microsoft Windows systems. The installation information, and often the files themselves, are packaged in installation packages, commonly known as "MSI files", from their default file extension.
Windows Installer is positioned as an alternative to stand-alone executable installer frameworks such as NSIS, and older versions of InstallShield and Wise.
MSI packages can be created using Visual Studio through a Setup project or using third party tools like InstallShield, Wise Installer and WiX.

Windows Installer XML

The Windows Installer XML (WiX, pronounced "wicks"), is a free software toolset that builds Windows Installer (MSI) packages from an XML document. It supports both command-line environment and integrated Visual Studio environment.
The WiX distribution includes Votive, a Visual Studio add-in that allows creating and building WiX setup projects using the Visual Studio IDE. Votive supports syntax highlighting and IntelliSense for .WXS source files and adds a WiX setup project type to Visual Studio.

WiX Download

WiX can be downloaded from http://sourceforge.net/projects/wix/files/. The latest version is 3.0 and the downloaded zip file (wix3.0.5419.0-x86-setup.zip) will contain Wix3.msi. Double click on this after closing all the Visual Studio instances. This will install all the required Wix tools (there are a lot of them and they have interesting names all associated to the wicks of a candle) to %PROGRAMFILES%\ \Windows Installer XML v3\ and the required Visual Studio add-in and project templates.
The bin sub folder under this contains all the tools like candle.exe (compiler and that convert source code into object files), light.exe (linker that take multiple object files and resources to create final setup package), and other tools like dark, light, heat, smoke, torch, etc. The doc sub folder has a detailed help file by name Wix.chm. A detailed tutorial is available at http://wix.sourceforge.net/manual-wix3/main.htm.

Visual Studio WiX Project

First create a sample application to test the WiX. I created a simple Windows Application project. Now add a new WiX Project to the solution (Project Type: WiX, Template: WiX Project) and name it as SampleApp.Setup.
The project will have a default ‘Product.wxs’ file containing the WiX source code to create an MSI package. The WiX source code is nothing but a set of XML tags. Remove the commented tags in the default file and add application exe as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="b464316e-5c06-4cae-be36-0748d51ceb03"
           Name="SampleApp.Setup"
           Language="1033"
           Version="1.0.0.0"
           Manufacturer="SampleApp.Setup"
           UpgradeCode="e0ad76e3-7ad8-436f-b3cd-9f30fcabb1d9">
    <Package InstallerVersion="200"
             Compressed="yes" />

    <Media Id="1"
           Cabinet="media1.cab"
           EmbedCab="yes" />

    <Directory Id="TARGETDIR"
               Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION"
                   Name="SampleApp.Setup">
          <Component Id="ProductComponent"
                     Guid="9102229d-8f5c-474d-be69-00085b3230a2">
            <File Id="SampleApp.exe"
                  Name="SampleApp.exe"
                  Source="$(var.SolutionDir)\SampleApp\$(var.OutDir)SampleApp.exe"
                  KeyPath="yes"
                  Checksum="yes"/>
          </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="ProductFeature"
             Title="SampleApp.Setup"
             Level="1">
      <ComponentRef Id="ProductComponent" />
    </Feature>
  </Product>
</Wix>
Let us now inspect each elements of this file:
WiX: Root element of a WiX installer.
Product: An msi will always contain a single product which is the software we are planning to install. It requires a unique GUID and we can specify other properties like product name, version and manufacturer.
Package: Used to set the packaging properties. Basically we say compression is needed or not.
Media: Describes the installation media for the installer. By default it is a cabinet file named media1.cab which is embedded within the msi file. All the other files will be compressed to this cab file.
Directory: Now we need to specify a group of directory structure that we plan to build on the target machine. All these directories should be within the virtual TARGETDIR directory. In this case, we have the root folder as ‘ProgramFilesFolder’ which is the keyword for system folder ‘%PROGRAMFILES%\’ which will be typically to ‘C:\Program Files\’. Inside this directory we are creating another directory named ‘SampleApp.Setup’. So our final destination folder will be ‘C:\Program Files\SampleApp.Setup\’.
Component: The component is the atomic unit of things to be installed. It consists of resources—files, registry keys, shortcuts or anything else—that should always be installed as a single unit.
File: This represents a file that we need to copy to the target system. Here we are specifying the output exe of our simple application. Note that ‘$(var.SolutionDir)’ is a preprocessor variable that will point to the actual solution directory. Same way, ‘$(var.OutDir)’ represents the project output directory (bin\Debug or bin\Release).
Feature: Features are separated parts of the application that we offer the user to decide whether to install or not. Examples are core files, samples, documentation, etc. Here we have just single feature which is our lone app exe referred by Component Id.
Building this project will create an msi named SampleApp.Setup.msi at the bin\Debug folder of the setup project.
This is the very basic installer without any user interaction options, and it will just show a progress bar of the installation. After completion, you can see that SampleApp .exe is copied to C:\Program Files\SampleApp.Setup\ folder. There are no start menu items or shortcuts created, but there will be an entry in Add or Remove programs list (Programs and Features) from where you can uninstall this program.

WiX User Interface

Adding a standard user interface to the installer is very easy with WiX. First add a reference to WixUIExtension.dll (available in’ %PROGRAMFILES%\ Windows Installer XML v3\bin\’ folder) in the setup project. Now add the following line just above the Product closing tag.
<UIRef Id="WixUI_Minimal"/>
UIRef tells WiX which UI style should be used and we have chosen the ‘Minimal’ with basic options. That’s it. Now build the project and run the installer. It will have a default user interface with license page, progress page and a finish page.
If you re-run the installer after installing it once, the UI will change to provide a ‘Repair’ or ‘Remove’ option. ‘Remove’ can be used to uninstall the program.
Providing the user with an option to choose the installation directory is also very easy. Instead of the UIRef element used above, add the following two lines:
<Property Id="WIXUI_INSTALLDIR"
              Value="INSTALLLOCATION" />
<UIRef Id="WixUI_InstallDir"/>
Here we have chosen the ‘InstallDir’ UI style and the selected directory is assigned to the ‘INSTALLLOCATION’ value which we are referring within the directory structure. Now when you run this new installer, the user will have an option to choose the installation directory.
Other UIRef include WixUI_FeatureTree (allows features to be selected) and WixUI_Mondo (provides typical, custom and complete install options).

WiX User Interface Customization

You can customize some visual aspects of the user interface by simply providing replacement files. To add your own license file for example, add a license rtf file to the setup project and then add the below line just above Produc> closing tag:
<WixVariable Id="WixUILicenseRtf" Value="license.rtf" />
Other such options are:
<WixVariable Id="WixUIBannerBmp" Value="mybanner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="mydialog.bmp" />
Dialog is the first install page’s background image (493 x 312 pixels bmp) and Banner is the top banner image on subsequent pages (493 x 58 pixels bmp).
Download a complete Visual Studio 2008 Solution with sample WiX project from here: wixsample.zip. And here is the complete WiX code for that:
<?xml version="1.0" encoding="UTF-8"?>
<!--Author: Sameer C Thiruthikad | Last Modified: 2009-Oct-16-->
<!--Defining some constants-->
<?define Property_ProductName = "My Sample App" ?>
<?define Property_Manufacturer = "My Company" ?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Name='$(var.Property_ProductName)'
           Id='{C24FBDCE-AD5D-4f0d-AD4B-9E75EB78D231}'
           UpgradeCode='{DCE43671-9C09-4fb0-AC05-34D4AF71563E}'
           Language='1033'
           Codepage='1252'
           Version='1.0.0'
           Manufacturer='$(var.Property_Manufacturer)'>

    <!--These properties will appear in Windows Explorer-->
    <Package Id='*'
             Keywords='Installer'
             Description="$(var.Property_ProductName) Installer"
             Comments='$(var.Property_Manufacturer) $(var.Property_ProductName)'
             Manufacturer='$(var.Property_Manufacturer)'
             InstallerVersion='100'
             Languages='1033'
             Compressed='yes'
             SummaryCodepage='1252' />
    
    <Media Id="1"
           Cabinet="media1.cab"
           EmbedCab="yes" />

    <!--Defining target directory structure-->
    <Directory Id='TARGETDIR'
               Name='SourceDir'>
      <!--Primary install folder: %PROGRAMFILES%\My Company\My Sample App\ ; Sub folders: Resources-->
      <Directory Id='ProgramFilesFolder'
                 Name='PFiles'>
        <Directory Id='ManufacturerFolder'
                   Name='$(var.Property_Manufacturer)'>
          <!--Application files to the primary install fodler-->
          <Directory Id='INSTALLDIR'
                     Name='$(var.Property_ProductName)'>

            <Component Id='MyCompany.MyApp'
                       Guid='{36EF4819-D4C6-4bc6-AA7D-4D41DC2E2342}'>
              <File Id='SampleApp.exe'
                    Name='SampleApp.exe'
                    DiskId='1'
                    Source='$(var.SolutionDir)\SampleApp\$(var.OutDir)SampleApp.exe'
                    KeyPath='yes'>
                <Shortcut Id="ProgramMenu.Shortcut.App"
                          Directory="ProgramMenuSubFolder"
                          Name="$(var.Property_ProductName)"
                          WorkingDirectory='INSTALLDIR'
                          Icon="AppIcon.ico"
                          IconIndex="0"
                          Advertise="yes" />
                <Shortcut Id="Desktop.Shortcut.App"
                          Directory="DesktopFolder"
                          Name="$(var.Property_ProductName)"
                          WorkingDirectory='INSTALLDIR'
                          Icon="AppIcon.ico"
                          IconIndex="0"
                          Advertise="yes" />
              </File>
            </Component>

            <Component Id='MyCompany.MyApp.Helpers'
                       Guid='{C60F6F97-CBC1-4ee7-89D8-3D4E5F72DD00}'>
              <File Id='HelperDLL'
                    Name='Helper.dll'
                    DiskId='1'
                    Source='$(var.SolutionDir)\SampleApp\$(var.OutDir)Helper.dll'
                    KeyPath='yes' />
            </Component>

            <Component Id='MyCompany.MyApp.Manual'
                       Guid='{DB40393C-2D35-4007-A0EF-E0846F1A70E4}'>
              <File Id='Manual'
                    Name='Manual.pdf'
                    DiskId='1'
                    Source='$(var.SolutionDir)\SampleApp\$(var.OutDir)Manual.pdf'
                    KeyPath='yes'>
                <Shortcut Id="ProgramMenu.Shortcut.Manual"
                          Directory="ProgramMenuSubFolder"
                          Name="Instruction Manual"
                          Advertise="yes" />
              </File>
            </Component>

            <!--Resources to the Resources sub folder-->
            <Directory Id="AppResources"
                       Name="Resources" >
              <Component Id="MyCompany.MyApp.Resources"
                         Guid="{F25B9D53-9924-4a6c-B0F7-C5188943F474}">
                <File Id="image.jpg"
                      Name="image.jpg"
                      Source="$(var.SolutionDir)\SampleApp\$(var.OutDir)Resources\image.jpg"
                      Checksum="yes"/>
              </Component>
            </Directory>
          </Directory>
        </Directory>
      </Directory>

      <!--Define Start menu folder-->
      <Directory Id="ProgramMenuFolder"
                 Name="Programs">
          <Directory Id="ProgramMenuSubFolder"
                     Name="$(var.Property_Manufacturer) $(var.Property_ProductName)">
            <Component Id="MyCompany.MyApp.Shortcut"
                       Guid="{3C08CF93-5203-49fe-B545-005C225DA334}">
              <!--Any extra folder we creare under user profile need to be marked for deletion-->
              <RemoveFolder Id='ProgramMenuSubFolder'
                            On='uninstall' />
              <!--This is also required?!-->
              <RegistryValue Root='HKCU'
                             Key='Software\[Manufacturer]\[ProductName]'
                             Type='string'
                             Value=''
                             KeyPath='yes' />
            </Component>
          </Directory>
      </Directory>

      <!--Define Desktop folder-->
      <Directory Id="DesktopFolder"
                 Name="Desktop" />
    </Directory>

    <!--Register all components under a single feature-->
    <Feature Id='Complete'
             Level='1'>
      <ComponentRef Id='MyCompany.MyApp' />
      <ComponentRef Id='MyCompany.MyApp.Helpers' />
      <ComponentRef Id='MyCompany.MyApp.Manual' />
      <ComponentRef Id='MyCompany.MyApp.Shortcut' />
      <ComponentRef Id='MyCompany.MyApp.Resources' />
    </Feature>
    
    <!-- The icon that appears in Add & Remove Programs. -->
    <Property Id="ARPPRODUCTICON"
              Value="appicon.ico" />

    <!--UI style is set to InstallDir (User will have directory selection)-->
    <UIRef Id="WixUI_InstallDir"/>
    <Property Id="WIXUI_INSTALLDIR"
              Value="INSTALLDIR" />

    <!--Sepcify license file-->
    <WixVariable Id="WixUILicenseRtf"
                 Value="license.rtf" />

    <!--Specify initial page's background image-->
    <WixVariable Id="WixUIDialogBmp"
                 Value="welcome.bmp" />

    <!--Specify subsequent page's banner image-->
    <WixVariable Id="WixUIBannerBmp"
                 Value="banner.bmp" />

    <!--Define the icon used for shortcuts and installer-->
    <Icon Id="AppIcon.ico"
          SourceFile="appicon.ico" />
  </Product>
</Wix>
                            

No comments:

Post a Comment