Tuesday, 1 May 2012

WiX Burn – tips/tricks

This post outlines some of the things I have found out since working with the new Burn code, unless otherwise stated this refers to version 3.6.2830.0 or later. For my requirements I have only needed to create single file installation setups, so none of these examples will include download code. I keep the source to resources in a folder called “Resource” so in the examples below "Resource\" is a reference to this folder.

In many cases this information has been gleaned from the WiX Users mail list. If you spot any errors or can suggest improvements please let me know.

Basic Template

This is my outline framework that I have been using to create a burn project:
<?xml version="1.0" encoding="utf-8"?>

<?ifndef Version?>
<?define Version = "1.0.0.0" ?>
<?endif ?>

<Wix RequiredVersion="3.6.2830.0" xmlns="http://schemas.microsoft.com/wix/2006/wi
"
     xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
  <Bundle Name="My Application" Version="$(var.Version)" Manufacturer="ACME" UpgradeCode="PUT-GUID-HERE
"
    HelpUrl="http://www.nowhere.com
"
    Copyright="Copyright© 2012, ACME" IconSourceFile="Resource\Setup.ico
"
    SplashScreenSourceFile="Resource\SplashScreen.bmp
"
    AboutUrl="http://www.nowhere.com
"
    Condition="((VersionNT >= v5.1) AND (ServicePackLevel >= 3)) OR ((VersionNT >= v5.2) AND (ServicePackLevel >= 2)) OR (VersionNT >= v6.0)">

    <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense" />
    <WixVariable Id="WixStdbaLicenseUrl" Value="" />
    <WixVariable Id="WixStdbaLogo" Value="Resource\logoSmall.png" />
    <Variable Name="InstallFolder" Type="string" Value="[ProgramFilesFolder]ACME\My App" />

    <Chain>
      <PackageGroupRef Id="Netfx4Full" />

      <RollbackBoundary />
     
      <MsiPackage
        Id="Setup
"
        Compressed="yes
"
        SourceFile="$(var.Setup.TargetPath)
"
        Vital="yes">
        <MsiProperty Name="INSTALLLOCATION" Value="[InstallFolder]" />
      </MsiPackage>
    </Chain>
  </Bundle>
</Wix
>

Notes:

  • This template reference the .Net 4 framework, for this I just copied the WiX source but set ExePackage/@Compresses=”yes” to embed the framework in my installation.
  • The Bundle/@Condition sets the install to only run on Windows XP SP3, Windows 2003 SP2 or later.
  • The WixStdbaLicenseUrl is set to an empty string to hide the license URL hyperlink as it is not require for my installation.
  • By referencing my WiX MSI setup project in the burn project I can reference the MSI using the syntax SourceFile="$(var.Setup.TargetPath)" where Setup is the name of my project.
  • The preprocessor variable for the version allows me to set the version from my build environment.

No License UI

To create an installation without a license URL link use the following:
       <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense" /> 
    <WixVariable Id="WixStdbaLicenseUrl" Value="" />

Hyperlink to local License file

If you want to have a link to local license file as part of your installation save the license file as an html document and then reference it as follows:
    <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense">
      <Payload SourceFile="Resource\License.htm" />
    </BootstrapperApplicationRef>
    <WixVariable Id="WixStdbaLicenseUrl" Value="License.htm" />

This embeds the file license.htm file within the bundle and extracts it when the installation is run, if the user clicks the hyperlink then the file is loaded in the default browser.

RichText box license dialog

To create an installation with the license displayed in a richtext control use the following:
    <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" />
    <WixVariable Id="WixStdbaLicenseRtf" Value="Resource\License.rtf" />

Custom UI

If you want to customise the standard UIs copy them from the WiX source and reference them like this:
    <WixVariable Id="WixStdbaThemeXml" Value="Resource\HyperlinkTheme.xml" />
    <WixVariable Id="WixStdbaThemeWxl" Value="Resource\HyperlinkTheme.wxl" />

.Net Framework 3.5 SP1 Install

To install the .Net Framework v3.5 SP1 I use this:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
  <Fragment>
    <util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\Net Framework Setup\NDP\v3.5" Value="Version" Variable="Netfx35Version" />

    <PackageGroup Id="Netfx35">
      <ExePackage Id="Netfx35
"
        Cache="no
"
        Compressed="yes
"
        PerMachine="yes
"
        Permanent="yes
"
        Vital="yes
"
        Name="Redist\dotnetfx35.exe
"
        SourceFile="..\Redist\dotnetfx35.exe
"
        InstallCommand="/q /norestart /lang:ENU
"
        RepairCommand="/q /norestart /lang:ENU
"
        UninstallCommand="/q /norestart /lang:ENU
"
        InstallCondition="NOT Netfx35Version OR (Netfx35Version &lt; v3.5.30729.1)" 
        DetectCondition="Netfx35Version AND (Netfx35Version &gt;= v3.5.30729.1)">
        <ExitCode Value ="3010" Behavior="forceReboot" />
      </ExePackage>
    </PackageGroup>
  </Fragment>
</Wix
>

Signing a package

This is the approach I use (http://wix.sourceforge.net/manual-wix3/insignia.htm):

  1. insignia -ib Setup.exe -o engine.exe
  2. signtool engine.exe (extra parameters excluded for simplicity)
  3. insignia -ab engine.exe Setup.exe -o Setup.exe
  4. signtool Setup.exe

You can also use modify the wixproj to override the SignBundleEngine and SignBundle targets (sample copied from this thread http://sourceforge.net/mailarchive/message.php?msg_id=29179087):

<Target Name="SignBundleEngine">
  <Exec Command="&quot;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\signtool.exe&quot; sign /f privatekey.pfx /p mypassword /t http://timestamp.comodoca.com/authenticode &quot;@(SignBundleEngine)&quot;" />
</Target>
<Target Name="SignBundle" >
  <Exec Command="&quot;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\signtool.exe&quot; sign /f privatekey.pfx /p mypassword /t http://timestamp.comodoca.com/authenticode &quot;@(SignBundle)&quot;" />
</Target>

I found this problematic because:

  1. My certificate is not in the certificate store so I have to use signtool (as above).
  2. Signtool is not in the path or referenced via an environment variable so you have to know the path to the location of the Platform SDK you have installed and hard code it.
  3. You signing password is hard coded in the wixproj file.

Edit 07/05/2012: ARPSYSTEMCOMPONENT not required as this supplied by burn.

55 comments:

VKC said...

Can I put feature selection via checkboxes using the theme file approach ?

Neil Sleightholm said...

You can put checkboxes on the options dialog which may help if not you have to write a custom BA. I have a csutom BA that I will publish when I have time that allows radio buttons and checkboxes on the main page.

VKC said...

Yes, even I think that I need a functionality of custom BA which gets all the values from a WPF UI and then proceeds with the installation. It will be great if you can post some example related to this.

I have followed wix-users list, but couldn't progress much.

Andy said...
This comment has been removed by the author.
Andy said...
This comment has been removed by the author.
Neil Sleightholm said...

I assume this is with the WiX BA I posted. I have seen this problem and can't always reproduce it. I'll take another look and see if I can find out why it happens.

Neil Sleightholm said...

Andy, not sure I understand your second question. Is this related to my WiX BA post rather than this one? Have you tried the Codeplex version?

Andy said...

Apologies - I'd just realised I'd posted on the wrong page before you replied. It is relating to the Wix Extended BA - which I downloaded at lunch today from codeplex.
Please feel fee to delete these posts and I can post them in the correct place if it helps (sorry... too much coffee I think...)

Goran said...

Hi Andy, thanks for the useful tips. In your article you are defining a install folder in the BA. MSI package also contains an installation folder. How can we synchronize the two, meaning that if we change the location in BA, MSI package install folder should also be updated?

Neil Sleightholm said...

The line:
MsiProperty Name="INSTALLLOCATION" Value="[InstallFolder]
passes the folder to the MSI as a property.

Anonymous said...

Hi, I don't understand! How do you set the version from your build environment? Can you please explain further? Thanks!

Neil Sleightholm said...

If depends how you are doing the build but using candle directly it would be with the option -DVersion=1.2.3.4

Anonymous said...

Is it possible for BURN to read the environment variables on the target machine and pass them as property to an underlying MSI?

Neil Sleightholm said...

I am not aware of a way of doing this but you can do it in the MSI using the [%ENV] method. May be you can do the same within burn.

Anonymous said...

Thanks for the reply. I think I found the answer, short answer is we cannot use Env variables directly in burn.

Yes I know that we can read it in MSI, but I am dealing with vendor MSI for which I am creating a wrapper. I tried using [%env] but that doesnt work in burn. THey use something called WIX burn builtin variables. These are all environment variables, but they read all that from the code (burn.sln -> engine -> variable.cpp). I am looking at this code now to see if I can add some custom env variables.

P.S: mean while we are using registry to set the values.

Dan said...

Neil,
I am a beginner with Burn and Wix and I would like to make a bundle with no Burn UI (just use the msi UI). I have not found a way to do that. Any suggestions?
Thanks, Dan

Neil Sleightholm said...

That isn't possible and I don't really see how it could work.

Anonymous said...

can you please explain why it wont work?

Neil Sleightholm said...

Burn is typically used to install pre-reqs (e.g. .NET framework) and then run your install. The Burn UI is used to let the user decide to install and also show progress, the MSI UI would be too late in that process.

Anonymous said...

You got confused between myself and Dan. I was talking about environment variables. Please use an @ tag going forward :)

Neil Sleightholm said...

@Anonymous - why what won't work? (Don't mind using @ tag but it helps if people aren't Anonymous!)

Arun George said...

well it was all a confusion. Ignore my earlier question.

Does the below paragraph makes sense? Is it the right path to go forward?

Yes I know that we can read it in MSI, but I am dealing with vendor MSI for which I am creating a wrapper. I tried using [%env] but that doesnt work in burn. THey use something called WIX burn builtin variables. These are all environment variables, but they read all that from the code (burn.sln -> engine -> variable.cpp). I am looking at this code now to see if I can add some custom env variables.

Neil Sleightholm said...

That sounds perfectly reasonable, I would suggest it would be implemented using the [%ENV] format.

As an alternative it might be simpler to read them from the registry using a RegistrySearch.

User Variables: HKEY_CURRENT_USER\Environment

System Variables:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment

Arun George said...

THanks! That was helpful.

Roopa Prabhu said...

How does one display a ProgressText/ProgressBar that shows which Package in the Chain is currently being executed?

Neil Sleightholm said...

As far as I am aware that is part of the standard BA and is displayed but above the progress bar.

Anonymous said...

Neil, any idea how I can display localized license agreements using the local URL method?

Neil Sleightholm said...

I think you need something like this <Payload Compressed="yes" Name="1031\license.rtf" SourceFile="Resources\1033\license.rtf" /> in your BootstrapperApplicationRef where the number corresponds to the language id.

Indu said...

Hi Neil,

I have done the UI customizations as suggested by you. But when I added a checkbox in the Install page, I am not able to get the value of the checked state to set the Install condition for my MSI package. Is this possible?You were mentioning something about the custom BA.Can you please provide some samples?Please help.

Thanks in advance.

Neil Sleightholm said...

I don't think this is possible with the standard BA (yet). Take a look at the my extended BA http://wixextba.codeplex.com/ there are some examples the show how to use it.

Indu said...

Thanks a lot Neil. It worked. :)

Indu said...

Hi,

Just one more doubt..is it possible to add an intermediate UI in between the Install and the Progress page?

Neil Sleightholm said...

Not currently, although it is a common request so I may get around to making it possible.

John M. Cooper said...

For the Package Group for .NET 3.5 SP1, shouldn't the .exe and two .msu's for KB959209 also be installed?

I'm working on adding that myself now . . .

I'll let you know . . .

Neil Sleightholm said...

Possibly but it depends what you are targeting as I think they don't apply to Win7+.

John M. Cooper said...

All server stuff for my particular product. x64 Windows Server 2008 R2.

Going to have to figure out how to do Server 2012 soon as this product definitely will be expected to deploy to it.

Nearly done with my first draft. Got do so testing.

Neil Sleightholm said...

According to http://support.microsoft.com/kb/959209 they don't apply to 2008 R2 (or I am missing your point).

Anonymous said...

I am looking to signed the bundle . but don't know where to put target tag . can you send me an example

Neil Sleightholm said...

I am afraid it is not something I have tried.

Mark Freedman said...

NetFx4Full isn't a valid PackageGroupRef in WinNetFxExtension librar, and shows up as an unresolved reference.

Neil Sleightholm said...

To use one of the standard ones it should be NetFx40Redist.

John Cooper said...

For what its worth, here is my 3.5 SP1 file:



















If you've got a convenient place to upload to, I can manage that after hours. Behind lots of firewall right now. I haven't tested it as the 300 MB+ bloat was substantial.

naren said...

Hell Neil,

thanks for the info in this article

I've created a managed bootstrapper using WPF, the bundle installs .net 4.0 as a pre requisite before installing my .msi package, the bootstrapper reboots the system after .net installation . The problem is when i run the bootstrapper in standard user mode after .net installation the bootstrapper reboots the system , but does not resume the installation in standard user mode, however it resume if i login as an admin user.

is there any thing that i should do so that the bootstrapper resuemes after reboot even in standard user mode.

thanks

Neil Sleightholm said...

Does your bootstrapper require elevated rights when you start it? If so you won't be able to run it on reboot. Other than that I would suggest posting a question to wix-users.

Jason said...

I had to add the following to my wixproj file to sign my bundle:

<PropertyGroup>
<SignOutput>true</SignOutput>
</PropertyGroup>

Neil Sleightholm said...

That is ok but doesn't work for me as I don't want to install the certificate.

Tolulope Ogundiji said...

How do I run the insignia

Tolulope Ogundiji said...

How do I run the insignia

Neil Sleightholm said...

It is a command line application supplied with WiX.

catbee said...

Hi Neil, I dont want to hardcode my signing password into wixproject, is that possible? I'm planning to do this.. i'm leaving the password argument blank. When the build happens, will signtool raise one (or perhaps two) prompts for the password when it gets called, near the end of the process?




"signtool location"

timestamp URL>

“certificate_path\cert.pfx"

$(signtool) sign /f $(signkey) /t $(timeStampServer)



true









Neil Sleightholm said...

Yes, see above "This is the approach I use".

Anonymous said...

If there is a later version of a bundled msi installed then the installation continues. How do you stop the bundle from installing if one of the msielements do not or should not install?

Neil Sleightholm said...

I am sorry I don't really understand the question.

Anonymous said...

----------
If there is a later version of a bundled msi installed then the installation continues. How do you stop the bundle from installing if one of the msielements do not or should not install?
----------

if you have a bundle with 3 MsiPackage elements:

<Chain>
<MsiPackage SourceFile="Once v1.msi" DisplayName="Once" Visible="yes" Permanent="no" Vital="yes"/>
<MsiPackage SourceFile="Doce v1.msi" DisplayName="Doce" Visible="yes" Permanent="no" Vital="yes"/>
<MsiPackage SourceFile="Trece v1.msi" DisplayName="Trece" Visible="yes" Permanent="no" Vital="yes"/>
</Chain>


but "Doce v2.msi" has (for some reason) already been installed on the machine. When the Standard Bootstrapper runs with the above setup, then the other two packages are installed but "Doce v1" is skipped.
How do I get the installation to fail with an error message "a later version of Doce has already been installed"? Or at least warn the user instead of just continuing and apparently succeeding

Neil Sleightholm said...

I afraid I don't know, if you could detect the version of the MSI may be via a registry read then you could add a condition. Try posting on wix-users some one else may have an idea.