Using the Bootstrapper to wrap a Windows Installer package

This article is to serve as a tutorial for converting a standard Windows Installer package (*.MSI) into a Visual Studio 2005 Generic Bootstrapper package. Doing so will allow you to wrap a program prerequisite in a Bootstrapper package, to allow its deployment alongside any ClickOnce deployment or Setup Project.


First Step: Directory Structure & File Layout

Navigate to your Bootstrapper package’s folder- [VS 2005 install dir]\SDK\v2.0\Bootstrapper\Packages –and create a new folder called "CustomPackage". There we will create our product manifest (product.xml).

<?

xml version="1.0" encoding="utf-8" ?>
<!-- TODO: Set appropriate ProductCode -->
<Product
      xmlns="https://schemas.microsoft.com/developer/2004/01/bootstrapper"
      ProductCode="Your.Custom.Package"
   >
</Product>

You will also need a localized package manifest. Let’s start with an empty English one for now. First create a subfolder called ‘en’ in the "CustomPackage" folder, and then create a package.xml file with the following text:

<?

xml version="1.0" encoding="utf-8" ?>
   <Package
      xmlns="https://schemas.microsoft.com/developer/2004/01/bootstrapper"
      Culture="Culture"
   >
</Package> ****

Second Step: Package Files

Now that the basic structure is laid out, let’s start by adding all applicable package files. That is, files to be deployed or redistributed.

We need to add the Windows Installer MSI as a package file in the product manifest. (If you need some help differentiating to two please look here [https://blogs.msdn.com/chrsmith/articles/Localizing_a_Bootstrapper_manifest.aspx].) Add the following XML snippet to the product.xml under the ‘Product’ element.

<!--

TODO: Reference your MSI package file -->
<PackageFiles>
<PackageFile Name="YourMSI.msi"/>
</PackageFiles>

Now the Bootstrapper manifest ‘knows of’ a file called YourMSI.msi. Make sure that the file is copied into the root Bootstrapper package directory.


Third Step[a]: Commands

The next step, and perhaps the most important, is to actually install your package. To do this we need to add a ‘command’. The Bootstrapper will detect that the package file is an MSI and automatically add the /I switch when it calls msiexec.exe to install the package.

<!--

TODO: Set a command to install your package file -->
<Commands Reboot="Defer">
<Command PackageFile="YourMSI.msi"
            Arguments="">
</Command>
</Commands> ****

Third Step[b]: ExitCodes

The Windows Installer package could indicate a number of things. Did the installation fail? Is a reboot required? The ExitCodes element is used to hook up those numeric exit codes to error messages and actions.

To set the appropriate exit codes, add the following XML snippet.

<!--

TODO: Set a command to install your package file -->
<Commands Reboot="Defer">
<Command PackageFile="YourMSI.msi"
            Arguments="">

<ExitCodes>
<!-- Standard Windows Installer return codes -->
<ExitCode Value="0" Result="Success"/>
<ExitCode Value="1641" Result="SuccessReboot"/>
<ExitCode Value="3010" Result="SuccessReboot"/>
<DefaultExitCode Result="Fail" 
                          FormatMessageFromSystem="false" 
                          String="GeneralFailure" />
</ExitCodes>
</Command>
</Commands> ****

Fourth Step: Error Messages

As you saw in the exit codes field, there are some exit codes we need to define. (If the Bootstrapper uses an undefined error string then no specific message is displayed, just the generic ‘failure occurred’ type of message.)

To define strings you can add a Strings element to either the product or package manifest. I would recommend adding it to your package manifest to make localization easier.

<!--

TODO: Define strings -->
<Strings>
<String Name="DisplayName">Your package name</String>
<String Name="Culture">en</String>
<String Name="GeneralFailure">A fatal error occurred during the installation of [Your Package]</String>

Fifth Step[a]: Install Check

Now your Bootstrapper package is good to go! But there is a bug, it always needs installation regardless of whether or not is on the system. We need to add the logic to detect if the package is installed. So if you run the Bootstrapper again it will skip your custom package’s installation.

To do this, we add an installation check. The most applicable check is the MsiProductCheck, which checks if a Windows Installer package is installed. To add this check, use the following XML snippet:

<!--

TODO: Insert your package's product code -->
<InstallChecks>
<MsiProductCheck Property="IsMSIInstalled" 
                 Product="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"/>
</InstallChecks> ****

Fifth Step[b]:

Install Condition

Now that the property "IsPackageInstalled" is defined, we still need to add some logic to use its value to determine whether or not to install the package. This is done through an InstallCondition’s BypassIf element.

<

InstallConditions>
<!-- Skip installation if MSI is installed on the system -->
<BypassIf Property="IsMSIInstalled"
             Compare="ValueGreaterThan" 
             Value="1"/>
</InstallConditions>

Done! That’s it. Now your package can detect if it is on the system, if not install, and if an error is encountered display the right error. You are the proud owner/creator of a new Bootstrapper package. The sixth and final step is to: enjoy deploying!