Sie sind auf Seite 1von 9

Windows

Installer XML 101: Building Installers


with WiX
Presented by Chris Schiffhauer

Overview

Windows Installer XML (WiX, pronounced wicks) is a free software toolset that builds Windows
Installer (MSI) packages from an XML document. It supports a command-line environment and later
gained support as an add-in to Visual Studio 2008 and 2010. Starting with Visual Studio 2012, it came
bundled as the only Microsoft blessed installer technology.

While Microsoft has given WiX its blessing in name, it has done little to support it. WiX has been
Microsofts in-house installer technology since 1999, and has taken incremental steps in releasing it
publicly. Its creator, Rob Mensching, has official duties at Microsoft and supports development of WiX in
his spare time.

An official tutorial has long served WiX developers at http://wix.tramontana.co.hu/tutorial. It was
created in 2009 when WiX was at version 3.0. This walkthrough is based on that tutorial, but updated for
WiX version 3.7 and considerably pared down.

This walkthrough follows the source code available for download from my website at
http://www.schiffhauer.com/downloads/WiX-Solution.zip.

The solution contains a Sources folder, which contains four files:
FoobarAppl10.exe our main executable
Helper.dll a required DLL file
Manual.pdf an optional instruction manual the user may install
Exclam.ico an icon we will use to signal an alert to the user during installation

It also contains four small WiX Setup Projects and a Custom Action Project.

If you havent before, please also install the WiX Toolset v3.7 from http://wixtoolset.org/. The WiX
source is also good to have on hand.

Building a Simple Installer

The WiX Starter project is an example of a new project as Visual Studio creates it. It consists simply of a
Product.wxs template file.



In the WiX.01 project, we will write the markup necessary to build an installer that installs the main
executable, the required DLL, and the instruction manual. The installer will not yet have a user interface,
but will simply perform the installation when launched.

<Product Id="*" Name="Foobar" Language="1033" Version="1.0.0.0" Manufacturer="Chris's Manufacturing
Conglomerate" UpgradeCode="1af8578c-7b9c-4f09-ade0-011a92803559">

WiX compiles the Product Id from an asterisk into a Guid, which handles upgrade logic. You can also
insert your own Guid value.

Weve also changed the name and added a Manufacturer, which is a required field.

<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Keywords="tutorial,


walkthrough" Description="A completed WiX 3.7 Setup Project" Comments="This is the completed version of
the WiX Starter project." Manufacturer="Chris's Manufacturing Conglomerate" />

Since the optional Keywords, Description, Comments, and Manufacturer values are shown in the MSI
package properties in Windows, Ive included them in my markup.

InstallScope=perMachine installs the package for all users. User installation can be customized using
InstallScope=perUser.

InstallerVersion=200 indicates that the minimum version of WindowsInstaller to be used is v2.0.
Compressed=yes indicates to use compressed (.cab) installer files.

<Feature Id="Complete" Title="Foobar" Level="1">

<!-- ComponentGroupRef: Each ComponentGroup below must have a reference -->

<ComponentGroupRef Id="ProductComponents" />

<ComponentGroupRef Id="Shortcuts"/>
</Feature>


Weve named our single feature Foobar, and added a reference to a Shortcuts component group that well
create further below.

<Fragment>


Our first fragment lays out the directory structure we want to install. It makes use of predefined folder
IDs as well as folders that we define.

<Directory Id="TARGETDIR" Name="SourceDir">

TARGETDIR is a predefined value that is the root directory of WiX Setup Projects. Every directory we
create is a subelement of this directory Id. (Be aware that directories that are defined but never have any
components assigned will not be created at all. This befuddles many developers attempting to create
empty directories with the installation and is a good candidate for the custom actions well look at later.)

<Directory Id="ProgramFilesFolder">

ProgramFilesFolder usually resolves to C:\Program Files (x86)\, but can also resolve to C:\Program
Files\ if the system is 32-bit or youre installing a 64-bit application.

ProgramMenuFolder is a predefined folder for start menu shortcuts, and DesktopFolder is a predefined
folder for desktop shortcuts.

Our second fragment describes our software components. It contains the two component groups
referenced above.

The ProductComponents group is assigned to the INSTALLFOLDER directory, which weve defined as
C:\Program Files (x86)\WixTutorial. Our main executable, helper DLL, and instruction manual will be
installed there. Shortcuts to the main executable will be created in the ProgramMenuDir and
DesktopFolder directories. The shortcuts Advertise=yes tells the installer to disable users ability to
edit the shortcut. Also, if the shortcuts destination no longer exists, the installation is considered broken
and the installer will launch to try to repair itself.

<ComponentGroup Id="Shortcuts" Directory="ProgramMenuDir">

<Component Id="ProgramMenuShortcut">


<RemoveFolder Id="ProgramMenuDir" On="uninstall" />


<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Type="string"
Value="" KeyPath="yes" />

</Component>
</ComponentGroup>


The Shortcuts group contains logic to remove the start menu item when the application is uninstalled,
and creates a registry entry as the KeyPath to detect the components install status. So if the installer sees
that this registry value exists, it expects the entire feature to be installed. The only time a component does
not require a KeyPath is if it has a single File element, which then defaults as the KeyPath.

<Fragment>

<Icon Id="FoobarIcon.exe" SourceFile="../Sources/FoobarAppl10.exe" />
</Fragment>


Our final fragment sets the icon, whose Ids extension is required by WiX to match the source files
extension.

At this point, the application can be built. A WiX.01.msi installer will be outputted to \Introduction to
WiX\WiX.01 - Completed\bin\Debug. If this is launched, the user will see the installers progress but will
be unable to stop or interact with it.

Adding a User Interface

Now well close the WiX.01 project files and look at the WiX.02 project. In this project, well add the
markup necessary for a user interface, and create a feature tree so the usual can select the features to
install.

To start, Ive added a reference to the WixUIExtension library. We will find this library, among others, in
the WiX Toolset we installed earlier. The path is generally C:\Program Files (x86)\WiX Toolset v3.7\bin.



In the Product.wxs Feature elements, were adding Title and Description attributes to each Feature
element. We also broke the manual into its own component group called DocumentationComponents, so
that it can be optionally included or excluded by the user.

We wont be using WiXs Mondo UI, which provides Typical, Custom, and Complete options, but lets take
a quick look at the Feature elements Level property. Weve set the ProductComponent and Shortcuts
groups to Level=1, and the DocumentationComponents group to Level=1000. Any Level of 1-3 will
default to a Typical installation, while anything higher is omitted from a Typical installation but is
available in Custom or Complete options. Our feature tree will reveal both features but default the Level
1000 group to be excluded.


<UIRef Id="MyWixUI_FeatureTree" />

The UIRef tag within the Product element references a new fragment at the bottom of the file, which links
to a new Installer file well create shortly. Lets first look at the new fragment:

<Fragment>

<UI Id="MyWixUI_FeatureTree">


<UIRef Id="WixUI_FeatureTree" />


<UIRef Id="WixUI_ErrorProgressText" />


<DialogRef Id="UserRegistrationDlg" />


<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog"
Value="UserRegistrationDlg" Order="3">LicenseAccepted = "1"</Publish>


<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog"
Value="UserRegistrationDlg" Order="2">1</Publish>

</UI>

<Property Id="PIDTemplate"><![CDATA[12345<### ###>@@@@@]]></Property>

<Property Id="PIDKEY" Hidden="yes" />
</Fragment>


The two UIRef elements refer to predefined UI elements. The DialogRef Id is a reference to the
UserRegistrationDlg dialog we will create.

What follows are two Publish elements, which publish events when we click on controls like Next or Back
buttons. We will use them to insert our UserRegistrationDlg between the predefined
LicenseAgreementDlg and CustomizeDlg dialogs.

The first event overrides LicenseAgreementDlgs Next button to point to our UserRegistrationDlg instead
of the default CustomizeDlg. It also prevents the user from continuing unless the License Agreement is
accepted. The second Publish element links the subsequent CustomizeDlgs Back button back to our
UserRegistrationDlg dialog.

Finally, we define two properties. The first defines a format that WiX will use to create two text boxes of
three characters each, which only accept numbers. Well use the second property to validate the users
product key.

Creating UserRegistrationDlg.wxs


Add an Installer File called UserRegistration.wxs. The initial template looks like this:

To the Fragment element I have added a UI element with a child Dialog element, which in turn has many
Control element children. Take a look at UserRegistration.wxs in the WiX.02 project. The markup is
mostly self-explanatory.

<Dialog Id="UserRegistrationDlg" Width="370" Height="270" Title="[ProductName] Setup" NoMinimize="yes">

The Dialog elements Title contains [ProductName], which simply refers back to the Product Name in
Product.wxs.

<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="Back">



<Publish Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
</Control>
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">

<Publish Event="ValidateProductID" Value="0">1</Publish>

<Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">CostingComplete = 1</Publish>

<Publish Event="NewDialog" Value="CustomizeDlg">ProductID</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes"
Text="Cancel">

<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>


As in Product.wxs, here we define the events to publish on Back, Next, and Cancel buttons. The Back
button returns to the predefined LicenseAgreementDlg. The Next button validates the product key (for
now were defaulting this to 1 or true), allows Windows to verify costing, then opens the predefined
CustomizeDlg. The ProductID property we defined in Product.wxs must exist, and we will create a custom
action below.

At this point, you can compile and launch the application. We now have a working installer with a user
interface including a useful feature tree.

Creating Custom Actions

Lets close the WiX.02 project files and open WiX.03. In this project, well add a custom action, and update
our installer to implement it.

Ive also created a CheckPID Custom Action Project in C#. You can also create custom actions in VB or
C++.


The CustomAction.cs in my provided CheckPID project is only minimally different from the base
template. After naming my ActionResult method to CheckPID, Ive added two lines of logic:

string Pid = session["PIDKEY"];
session["PIDACCEPTED"] = Pid.StartsWith("1") ? "1" : "0";


The first line accepts the session property PIDKEY that we defined in Product.wxs. The second sets a new
PIDACCEPTED property to true if the key starts with a 1, or false if otherwise.

Remember to add your new Custom Action Project as a project reference to your Setup project.

Now, returning to Product.wxs, well add a new fragment to link it to the custom action.

<Fragment>

<Binary Id="CheckPID.CA" SourceFile="..\CheckPID\bin\Debug\CheckPID.CA.dll" />

<CustomAction Id="CheckingPID" BinaryKey="CheckPID.CA" DllEntry="CheckPID" />
</Fragment>


When WiX compiles a custom action, it automatically inserts .CA prior to the DLL extension (and
provides poor debugging if we get this wrong). We link our Binary element to the DLL source file, and
attach CustomActions CheckPID method to it.

We also need to update our existing UserRegistrationDlg.wxs file

<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">

<Publish Event="DoAction" Value="CheckingPID">1</Publish>

<Publish Event="SpawnDialog" Value="InvalidPidDlg">PIDACCEPTED = "0"</Publish>

<Publish Event="NewDialog" Value="CustomizeDlg">PIDACCEPTED = "1"</Publish>

</Control>



The first event references the CheckingPID CustomAction we defined alongside the Binary in
Product.wxs, which itself invokes our CheckPID method. The value of 1 tells WiX to always publish this
event.

The second event spawns a new dialog, which well create shortly, if our PIDACCEPTED property is set to
false.

The third event simply links to the predefined CustomizeDlg dialog if the PIDACCEPTED property is set to
true.

All that is left is to create a simple InvalidPidDlg.wxs dialog with a UI and optional Binary tag:

<UI>

<Dialog Id="InvalidPidDlg" Width="260" Height="85" Title="[ProductName] Setup" NoMinimize="yes">


<Control Id="Icon" Type="Icon" X="15" Y="15" Width="24" Height="24" ToolTip="Information
icon" FixedSize="yes" IconSize="32" Text="Exclam.ico" />


<Control Id="Return" Type="PushButton" X="100" Y="62" Width="56" Height="17"
Default="yes" Cancel="yes" Text="Return">



<Publish Event="EndDialog" Value="Return">1</Publish>


</Control>


<Control Id="Text" Type="Text" X="48" Y="10" Width="194" Height="45" TabSkip="no">



<Text>The user key you entered is invalid. Please, enter the key printed on the
label of the jewel case of the installation CD.</Text>


</Control>

</Dialog>
</UI>
<Binary Id="Exclam.ico" SourceFile="../Sources/Exclam.ico" />


In case youve never entered an incorrect product key before, this dialog tells the user that their key is
invalid, and presents them with a single button that closes the dialog and returns to the registration
dialog. We also finally put our Exclam.ico file to use.

In Conclusion

WiX is a powerful installer technology that is made attractive by its XML markup and integrated Visual
Studio support. However its lack of full support at Microsoft, and especially its lack of support resources,
makes it a challenge to get up and running quickly.

The next step is to visit http://wixtoolset.org/ and read through the documentation. It would also be
worthwhile to go through the full tutorial at http://wix.tramontana.co.hu/tutorial. While it is a few years
old it covers the technical nuts and bolts to the extent youll need to progress in your fluency in WiX and
Windows installer in general.

Bon courage !

Das könnte Ihnen auch gefallen