Note: A full example can be found in the sources
directory from the current repo.
Before anything, WiX Toolset must be installed and integrated with Visual Studio.
Install WiX Toolset build tools and the Visual Studio extension from https://wixtoolset.org/releases/
.
Note: WiX Toolset can be used also independently of Visual Studio, from command line.
Before starting to create our bundle, let's create two dummy MSI projects that will be, later, included in the bundle:
Installer1.msi
Installer2.msi
Each installer will deploy a single text dummy file.
The focus of the current tutorial is the bundle itself. Therefore, for more details about creating the dummy installers see the sources and the My First Installer pill.
Note: The two MSI files created by these projects are two normal installers that can also be installed independently.
Further, we will see how they can be integrated in a bundle.
A bundle, unlike the MSI is a WiX concept and it is an executable file that contains and installs a number of packages (MSI or EXE). In this tutorial we will see how to include MSI packages into the bundle.
Add a new "Bootstrapper Project for WiX v3" project in the solution:
The bootstrapper application is the one that coordinates the whole bundle execution. It may or may not provide a GUI.
WiX Toolset provides a few bootstrapper applications that can be used out of the box. For this tutorial, we will use the WixStandardBootstrapperApplication.RtfLicense
that displays a default GUI with a single page containing a dummy license agreement.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Bundle ...>
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" />
...
</Bundle>
</Wix>
This is how the bundle's GUI looks like:
Create two variables for the messages that will be used as arguments for the MSI packages:
<Variable
Name="Message1"
Type="string"
Value="This is the default message from bundle prepared for Installer 1." />
<Variable
Name="Message2"
Type="string"
Value="This is the default message from bundle prepared for Installer 2." />
By default, the variables are private. Unlike the MSI, where the properties are made public by writing their names in all uppercase, in a bundle making the variables public is done explicitly, using the Overridable
attribute. This attribute is defined in an extension library that must be manually referenced.
Right click the project -> Add Reference -> Browse -> select the file "c:\Program Files (x86)\WiX Toolset v3.11\bin\WixBalExtension.dll" -> Add -> OK
<Wix
xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
...
</Wix>
This is needed in order to use, later, the Overridable
attribute.
<Variable
Name="Message1"
Type="string"
Value="This is the default message from bundle prepared for Installer 1."
bal:Overridable="yes" />
<Variable
Name="Message2"
Type="string"
Value="This is the default message from bundle prepared for Installer 2."
bal:Overridable="yes" />
This attribute is allowing the variable to be provided from command line.
In this tutorial, we have two WiX projects Installer1
and Installer2
that builds the two MSI files.
Right-click on the bundle project -> Add -> Reference to reference them from the bundle.
Note: If the MSI files are created by a third party or by a project from another solution, there is no need to do anything here. Just make sure you know the path to the MSI files. Usually we copy all the external MSI files in a directory inside the solution directory and use them from there, to always use paths relative to the solution's root directory.
Note: If referenced installer projects are renamed, the reference must be manually deleted and recreated.
If the MSI files is generated by projects from the current solution and it is referenced by the bundle, as it is the situation in our tutorial, we can use the variables TargetDir
and TargetName
to obtain the actual path to the generated MSI files.
<PackageGroup Id="Installer1PackageGroup">
<MsiPackage
SourceFile="$(var.Installer1.TargetDir)$(var.Installer1.TargetName).msi"
DisplayName="Installer 1">
<MsiProperty Name="MESSAGE" Value="[Message1]" />
</MsiPackage>
</PackageGroup>
Note: If referenced installer projects are renamed, the variables used here must be replaced with the new names, too.
Note: If external MSIs need to be included in the bundle, the full or relative path must be specified instead.
The <MsiProperty>
tag is used to set a property for the MSI. Add as many as needed.
In a similar way, create the second package:
<PackageGroup Id="Installer2PackageGroup">
<MsiPackage
SourceFile="$(var.Installer2.TargetDir)$(var.Installer2.TargetName).msi"
DisplayName="Installer 2">
<MsiProperty Name="MESSAGE" Value="[Message2]" />
</MsiPackage>
</PackageGroup>
<Wix
xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle ...>
<Chain>
<PackageGroupRef Id="Installer1PackageGroup" />
<PackageGroupRef Id="Installer2PackageGroup" />
</Chain>
</Bundle>
</Wix>
Important: The bundle will install the MSI packages in the order in which they are added to the Chain
element.
Build the entire solution.
Run the bundle with the command:
BundleWithMsiPackages.exe /l install.log Message1="Custom message for the Installer 1" Message2="Custom message for the Installer 2"
The bundle will display the following GUI:
Each MSI installer is installed in its own directory just as it would be installed if executed independently.
In "Programs and Features" only the bundle is displayed. No record for the individual MSI installers is added.
The logs are automatically generated in the user's %TEMP%
directory with the name:
BundleName_yyyyMMddHHmmss.log
The log files for each package (MSI) is generated in the same location with the name
BundleName_yyyyMMddHHmmss_#_PackageId.log
A custom location for the log file can be specified at run time using the /l
parameter. For example:
MyBundle.exe /l "log.txt"
More details can be found at FireGiant:
If the installer is executed again, the bundle automatically detects that it is already installed and offers the repair and uninstall options: