New information has been added to this article since publication.
Refer to the Editor's Update below.

Bootstrapper

Use the Visual Studio 2005 Bootstrapper to Kick-Start Your Installation

Sean Draine

This article discusses:

  • Rich client application deployment
  • Easy dependency management
  • Deploying redistributable components
  • Deploying the .NET Framework
  • Bootstrapper extensibility
This article uses the following technologies:
Visual Studio, Visual Basic, .NET

Contents

An Integrated Installer for Applications and Prerequisite Components
The Bootstrapper User Experience
Using the Bootstrapper in Visual Studio
Configuring the Bootstrapper
A Closer Look at the Product Manifest
A Closer Look at the Package Manifest
Custom Redistributables in Visual Studio
Generating the Bootstrapper Setup.exe with MSBuild
Conclusion

If you have ever written an application intended to run on nonstandardized desktops (or, as some would say, PCs in the wild), you understand that one of the biggest challenges is installing the components required by your application onto the target computers. If any component is missing, your application will most likely fail, typically with a cryptic error message about a missing DLL file that will send your customers straight to your support desk.

As an example, consider a gaming application written in Visual Basic® 2005. The app won't run, of course, unless the .NET Framework 2.0 is installed. In order to present rich and speedy graphics, the application would likely depend on the managed DirectX® libraries. Consider also that the game may use a SQL Server™ 2005 Express Edition database to store game state and history.

To install the game, the user must install the .NET Framework 2.0, DirectX 9.0 for Managed Code, and SQL Server 2005 Express Edition before installing the actual application. This involves a number of complicated steps. First, the user has to check whether each component is already installed on the machine. Then they must manually download and run the installers for each missing component, selecting the appropriate choices in the installation UI. If any reboots are required, they must restart the machine and resume from where they left off. If the components need to be installed in a particular order, there are additional opportunities for things to go wrong. This is a poor user experience, to say the least, and many users would give up.

The Visual Studio® 2005 Bootstrapper solves this problem by allowing you to provide an easy, integrated way to install all of the different prerequisite pieces required by your application.

An Integrated Installer for Applications and Prerequisite Components

The Visual Studio 2005 Bootstrapper lets you provide users with a simple, automated way to detect, download, and install an application and its prerequisites. It serves as a single installer that integrates the separate installers for all the components making up an application, and provides a wealth of features and services.

Minimal initial download size The Bootstrapper is a small executable that weighs in at a mere 222 KB, and takes about one minute to download on a 28.8 Kbps modem. It therefore adds only a small amount of overhead to the installation process, which can save the user from having to download much larger redistributables if he does not need them.

Minimal platform requirements Since the purpose of the Bootstrapper is to easily pre-install application dependencies, it is essential that it have few, if any, dependencies itself. The only hard dependency the Bootstrapper has is the C runtime, which is embedded in the Bootstrapper executable through static linking and therefore does not require a separate installation. The Bootstrapper will load other Windows® DLLs such as msi.dll to perform Windows Installer application code checking, and Wintrust.dll to verify component signatures. These DLLs, however, come pre-installed with Windows 98 and higher.

Platform detection The Bootstrapper can detect whether a component is supported on the target computer's operating system. If the component will not run in the given environment, the Bootstrapper notifies the user and ends the installation before downloading the component. For example, if someone attempts to install the .NET Framework on a machine running Windows 95, the Bootstrapper will alert the user up front, before they have downloaded the 20MB .NET Framework redistributable.

Component detection and download as required A key design goal for the Bootstrapper is to download and install only those components not previously installed. This saves you from having to add deployment servers and subjecting users to lengthy, unnecessary downloads. The Visual Studio 2005 Bootstrapper offers flexible options for detecting whether a component is already installed. Detection can be based on a search through the registry, file system, or the Global Assembly Cache (GAC) (due in Beta 2). It can also be based on the results of a custom executable if more complicated detection logic is required. If a component is detected, the Bootstrapper does not download and install that component.

Handling reboots For some components, a reboot may be required to complete the installation. For example, installing Windows Installer 2.0 will cause a reboot on down-level Windows systems. The Bootstrapper allows graceful handling of reboots because components can specify whether the Bootstrapper should reboot immediately after the component is installed, for example if the installation of subsequent components would otherwise fail. Components can also instruct the Bootstrapper to defer reboots until all components have been installed. This way, the user only has to reboot once, even if more than one of the installed components requires a reboot. When a reboot is required, the Bootstrapper prompts the user to restart the computer and writes a callback to itself to the RunOnce key in the Windows registry. When the computer is restarted, Windows reinvokes the Bootstrapper, which resumes the installation from where it left off.

License agreements Most components include an End-User License Agreement (EULA) that serves to protect the component vendors' intellectual property. The Bootstrapper can be configured to present license agreements for individual components. It can also present a single shared license agreement for multiple components in order to minimize the number of pop-ups the user has to click through.

Install from Web, file shares, and local drives Applications today are distributed through a variety of media, including the Web, network file shares, local drives (CD-ROM, DVD), USB storage devices and Secure Digital (SD) memory cards. The Bootstrapper is designed to handle any of these scenarios. In addition, it can be configured to download prerequisite components from the same location as the app installer, or for purposes of load balancing, to obtain redistributables from a different server.

Extensible to custom redistributables The Bootstrapper is a generic installer that can be configured to bootstrap virtually any redistributable that is packaged as a Windows Installer file or as an executable program. It is programmed through a set of simple XML manifests that specify all the metadata required for the Bootstrapper to manage the installation of the component. The metadata includes the required set of files, platform and component dependencies, detection logic, command-line arguments, return code semantics, the EULA, and more. The Visual Studio build system then writes the metadata into the Bootstrapper executable, resulting in a single setup program that can bootstrap the desired set of prerequisite components for the application.

The Bootstrapper User Experience

The first step for the user is to launch the Visual Studio 2005 Bootstrapper executable, called setup.exe. Setup.exe can be launched from the local file system, a network file share, or a through a hyperlink to a Web address.

Setup.exe first detects whether any of the prerequisites are already installed. If any of the prerequisites are missing, the Bootstrapper shows an install dialog box that lists those components and displays their license agreement. Otherwise, if all of the prerequisites are detected, the Bootstrapper simply launches the application installer. Using the example of the game application described earlier, if the user has already installed DirectX 9.0 for Managed Code but not the .NET Framework 2.0 or SQL Server 2005 Express Edition, the bootstrapper dialog would display only the latter two components in the list of items to be installed.

Once the user accepts the agreement, the download and installation process begins. Otherwise, the installation is canceled and the Bootstrapper displays a progress bar to show how much of the process was completed.

When all of the prerequisites have been installed, the Bootstrapper kicks off the installation process of the application itself. Having launched the application installer, it gets out of the way and leaves it to the application installer to display any UI and perform the necessary installation operations for the application. The Bootstrapper does not dictate which installer technology the app must use; it can use Windows Installer, ClickOnce, or any other technology. In the case of installing a ClickOnce application, the user would see the standard ClickOnce installation dialog box, enabling him to install and run his app.

Using the Bootstrapper in Visual Studio

Visual Studio 2005 deployment tools for ClickOnce and Windows Installer include a UI that enables easy configuration of the Bootstrapper. Figure 1 shows the Prerequisites page of the application, which can be accessed from the property pages for Windows Application, Console Application, and Setup projects.

Figure 1 Visual Studio Prerequisite Dialog

Figure 1** Visual Studio Prerequisite Dialog **

The Prerequisites page allows you to select which components your application requires. Visual Studio 2005 will include several frequently used components in the box, including the .NET Framework 2.0, J# Runtime 2.0, SQL Server 2005 Express Edition, Microsoft® Data Access Components (MDAC) 2.8, and Windows Installer 2.0. The Bootstrapper is also extensible, so you can add additional Microsoft and third-party components as needed.

Configuring the Bootstrapper

The Bootstrapper offers a couple of options for where redistributable files can be located. By default, the Bootstrapper assumes that all files, including the redistributables, the Bootstrapper setup.exe itself, and the actual application files, are located together under the same folder. This is ideal for applications that will be installed from local disk (CD-ROM, USB device) or a network share. This option also supports the application publisher who wants to serve up everything from a single deployment server. Alternatively, if the publisher wants to balance the load across different servers, or maintain a central location for prerequisite components that can be shared by multiple applications, the Bootstrapper can be configured to download any prerequisite files from a separate location.

In the case of Web deployment, the user downloads the Bootstrapper setup.exe and then runs it on a local computer, at which point it needs the URL to find the prerequisite files. There is no robust way for an executable to dynamically determine where it was downloaded from, so the origin location is written into the setup.exe file itself. This is important to keep in mind if you are changing deployment servers, because the Visual Studio 2005 Bootstrapper must be reconfigured to download from the new server. Fortunately, this can easily be done through the command-line options available on the Bootstrapper setup.exe. To reconfigure the deployment URL, go to the Windows command prompt and run the setup.exe as follows:

setup.exe –url=https://www.example.com

Here https://www.example.com represents the directory on the new deployment server for the application and its prerequisites.

One of the most powerful features of the Bootstrapper is extensibility. It's a generic installation engine with a flexible configuration system—you can plug into the Bootstrapper just about any installer packaged as a Microsoft Installer (MSI) or executable program. To extend it to a particular component, the developer must author a pair of XML manifests that contain the installation metadata for the component.

The first is the product manifest, which contains any language-neutral metadata for the package. The second is the package manifest, which contains metadata for handling language-specific aspects of the redistributable. A component will have a package manifest for each localized version of that component. The schemas for both of these files are almost identical. The choice of whether to include a given bit of metadata in the product or package manifest depends on how the redistributable has been factored for different languages. The product manifest contains metadata that is common to the different localized versions of the redistributable. The package manifest is used for language-specific metadata, and it typically contains localized error messages.

A Closer Look at the Product Manifest

Figure 2 shows the product manifest for the .NET Framework 2.0. The root tag for the manifest is the <Product> tag, which also specifies the name of the manifest schema. Visual Studio ships a schema file for the Bootstrapper manifests that provides IntelliSense® technology and validation when authoring manifests with the new XML editor.

Figure 2 Product Manifest for the .NET Framework 2.0

<?xml version="1.0" encoding="utf-8" ?> <Product xmlns="https://schemas.microsoft.com/developer/2004/01/bootstrapper" ProductCode="Microsoft.Net.Framework.2.0"> <!-- Defines list of files to be copied on build --> <PackageFiles> <PackageFile Name="dotnetfx.exe"/> <PackageFile Name="dotnetchk.exe"/> </PackageFiles> <InstallChecks> <ExternalCheck Property="DotNetInstalled" PackageFile="dotnetchk.exe" /> <RegistryCheck Property="IEVersion" Key="HKLM\Software\Microsoft\Internet Explorer" Value="Version" /> </InstallChecks> <!-- Defines how to invoke the setup for the .NET Framework redist --> <!-- TODO: EstimatedTempSpace, LogFile, updated EstimatedDiskSpace --> <Commands Reboot="Defer"> <Command PackageFile="dotnetfx.exe" Arguments=' /q:a /c:"install /q /l"' EstimatedInstalledBytes="21000000" EstimatedInstallSeconds="300"> <!-- These checks determine if the package is to be installed --> <InstallConditions> <!-- indicates the .Net Framework is already installed --> <BypassIf Property="DotNetInstalled" Compare="ValueNotEqualTo" Value="0"/> <!-- Block install if user does not have admin privileges --> <FailIf Property="AdminUser" Compare="ValueNotEqualTo" Value="1" String="AdminRequired"/> <!-- Block install on Windows 95 --> <FailIf Property="Version9X" Compare="VersionLessThan" Value="4.10" String="InvalidPlatformWin9<em xmlns="https://www.w3.org/1999/xhtml">x</em>"/> <!-- Block install on Windows NT 4.0 or less --> <FailIf Property="VersionNT" Compare="VersionLessThan" Value="5.00" String="InvalidPlatformWinNT"/> <!-- Block install if Internet Explorer 5.01 or greater is not present --> <FailIf Property="IEVersion" Compare="ValueNotExists" String="InvalidPlatformIE" /> <FailIf Property="IEVersion" Compare="VersionLessThan" Value="5.01" String="InvalidPlatformIE" /> <!-- The package should be installed --> <InstallIf/> </InstallConditions> <ExitCodes> <ExitCode Value="0" Result="Success"/> <ExitCode Value="3010" Result="SuccessReboot"/> <ExitCode Value="4096" Result="Fail" String="ImproperUsage"/> <ExitCode Value="4097" Result="Fail" String="AdminRequired"/> <ExitCode Value="4098" Result="Fail" String="WindowsInstallerComponentFailure"/> <ExitCode Value="4099" Result="Fail" String="WindowsInstallerImproperInstall"/> <ExitCode Value="4100" Result="Fail" String="CreateMutexFailure"/> <ExitCode Value="4101" Result="Fail" String="AnotherInstanceRunning"/> <ExitCode Value="4102" Result="Fail" String="OpenDatabaseFailure"/> <ExitCode Value="4103" Result="Fail" String="ReadDatabaseFailure"/> <ExitCode Value="4111" Result="Fail" String="GetTempDirectoryFailure"/> <ExitCode Value="4113" Result="Fail" String="BetaNDPFailure"/> <ExitCode Value="4115" Result="Fail" String="TempDirectoryTooLong"/> <ExitCode Value="4116" Result="Fail" String="SourceDirectoryTooLong"/> <ExitCode Value="4118" Result="Fail" String="CannotWriteLogFile"/> <ExitCode Value="4119" Result="FailReboot" String="DarwinServiceHung"/> <ExitCode Value="4120" Result="Fail" String="InternalError"/> <DefaultExitCode Result="Fail" FormatMessageFromSystem="true" String="GeneralFailure" /> </ExitCodes> </Command> </Commands> </Product>

<ProductCode> The <ProductCode> tag specifies a unique ID for the package. This appears in the project file and is used by the build system to determine which components to include in the Bootstrapper. The recommended format for the product code is Publisher.ProductName.Version.

<PackageFiles> The <PackageFiles> section provides a list of all the files in the redistributable. The redistributable can be wrapped up into a single file (as is typical), or it can be a bunch of loose standalone files. The manifest for the .NET Framework specifies just two files. The first is dotnetfx.exe, which is the redistributable file for the Framework. The second is dotnetchk.exe, which is a program the Bootstrapper launches to detect whether the Framework is already installed on the target computer.

<InstallChecks> The <InstallChecks> tag contains all of the detection logic that the Bootstrapper should run prior to downloading and installing the component. The Bootstrapper is capable of a variety of checks that can include searching for files on disk, reading registry keys, searching for MSI product codes, and detecting assemblies in the GAC. If more sophisticated logic is required, the Bootstrapper can launch an external app that returns the detection result to the Bootstrapper through a return code.

In the case of the .NET Framework, the Bootstrapper performs two checks. The first determines if the Framework is already installed by launching dotnetchk.exe and storing the return code value in a property called DotNetInstalled. The second is a simple registry check that obtains the currently installed version of Microsoft Internet Explorer and stores the value in a property called IEVersion. These properties are then used in the <Commands> section, which instructs the Bootstrapper's actions based on the values of these checks.

<Commands> The <Commands> tag specifies all of the actions the Bootstrapper should perform while installing the component. The tag includes a Reboot attribute that specifies how the Bootstrapper should behave if the .NET Framework installer returns a value indicating that a reboot is needed. The attribute has three possible values: Defer, Immediate, and None. Defer prompts the user to reboot after all prerequisites have been installed. Immediate prompts the user to reboot immediately after this component has been installed. None doesn't prompt the user to reboot. The reboot is not critical and can be ignored.

<Command> The <Command> tag dictates a specific action to take during the installation process. Most components will have a single command that simply launches the component installer. In addition to the name of the file to run, the package author can specify command-line arguments, the estimated size of the package (which is used to provide progress UI during download time), and the estimated time the installer needs to run (which is used by the progress UI during install time). For the .NET Framework, the command launches dotnetfx.exe and passes command-line arguments indicating that the installer should be run in silent mode.

<InstallConditions> The <InstallConditions> section specifies the conditions under which the Bootstrapper should install, bypass, or fail the component installation. The <InstallIf> and <FailIf> tags are typically used to express basic requirements that must be met on the target machine in order for the component to be installed successfully. If any of these conditions are not met, the Bootstrapper ends the installation and the user is shown an error message explaining the reason.

In the case of the .NET Framework, there are a number of conditions under which installation would fail, such as:

  • The user does not have administrative privileges on the target machine
  • The machine is running Windows 95 or earlier, which is not supported
  • The machine is running Windows NT 4.0 or earlier, which is not supported
  • The machine is not running Internet Explorer 5.01 or greater, which is a prerequisite for the .NET Framework

The manifest may also contain <ByPassIf> tags, which specify the conditions under which the installation of the component should be silently skipped. The most common reason for skipping a component is that the component or a later compatible version has already been installed. The .NET Framework manifest includes a single bypass condition that skips the installation of the component if it has been previously installed.

<ExitCodes> The section inside <ExitCodes> tells the Bootstrapper how to interpret the various codes that dotnetfx.exe might return after executing. Each <ExitCode> tag specifies a return code value, a success state, and the name of a message to display to the user if necessary.

As shown in the example, a return code of zero from dotnetfx.exe indicates that the .NET Framework was successfully installed and no reboots are required. A value of 3010 indicates a successful install with a required reboot, which could happen if you installed the .NET Framework on a machine that is running Windows 98 without Windows Installer 2.0 installed.

The remaining exit codes are special error codes that dotnetfx.exe returns under various failure conditions (for example, if another instance of this program is already running, the Windows Installer service will be unresponsive). Because dotnetfx.exe wraps an Microsoft Installer file, a number of additional MSI failure codes could also be returned. These situations are all handled by the <DefaultExitCode> tag, which uses system APIs to get the underlying message that will ultimately be displayed to the user. This saves the package author from having to repeat standard Windows Installer error codes and messages and saves lots of extra coding.

A Closer Look at the Package Manifest

Figure 3 shows the English package manifest for the .NET Framework 2.0. The English manifest is somewhat simpler than those of other languages because dotnetfx.exe contains all of the English text, whereas the localized satellites must be installed through a separate Language Pack redistributable called langpack.exe. Just as the product manifest was responsible for downloading and launching dotnetfx.exe, the package manifests are responsible for launching the various language packs. Since we have already reviewed the tags that manage launching installers, we will use the English example here.

Figure 3 Bootstrapper Manifest for the .NET Framework English Language Pack

<?xml version="1.0" encoding="utf-8" ?> <Package xmlns="https://schemas.microsoft.com/developer/2004/01/bootstrapper" Name="DisplayName" Culture="Culture" LicenseAgreement="eula.txt"> <PackageFiles> <PackageFile Name="eula.txt"/> </PackageFiles> <!-- Defines a localizable string table for error messages--> <Strings> <String Name="DisplayName">.NET Framework 2.0</String> <String Name="Culture">en</String> <String Name="AdminRequired"> You do not have the permissions required to install .NET Framework 2.0. Please contact your administrator. </String> <String Name="InvalidPlatformWin9<em xmlns="https://www.w3.org/1999/xhtml">x</em>"> Installation of .NET Framework 2.0 is not supported on Windows 95. Contact your application vendor. </String> <String Name="InvalidPlatformWinNT"> Installation of .NET Framework 2.0 is not supported on Windows NT 4.0. Contact your application vendor. </String> <String Name="InvalidPlatformIE"> Installation of .NET Framework 2.0 requires Internet Explorer 5.01 or greater. Contact your application vendor. </String> <String Name="ImproperUsage"> Either the syntax used to specify parameters to Dotnetfx.exe is incorrect, or the parameters are invalid. </String> <String Name="WindowsInstallerComponentFailure"> The Windows Installer installation failed. As a result, the installation of .NET Framework 2.0 cannot proceed. </String> <String Name="WindowsInstallerImproperInstall"> Due to the improper installation of Windows Installer, the installation of .NET Framework 2.0 cannot proceed. </String> <String Name="CreateMutexFailure"> A fatal error occurred and the installation of .NET Framework 2.0 cannot proceed. </String> <String Name="AnotherInstanceRunning"> Another instance of setup is already running. </String> <String Name="OpenDatabaseFailure">Cannot open MSI Database.</String> <String Name="ReadDatabaseFailure"> Cannot read from MSI Database. </String> <String Name="GetTempDirectoryFailure"> Cannot get Temporary directory. </String> <String Name="BetaNDPFailure"> A beta version of the .NET Framework was detected on the computer. You must uninstall any beta versions before attempting to install Dotnetfx.exe. </String> <String Name="TempDirectoryTooLong"> Temporary directory too long. </String> <String Name="SourceDirectoryTooLong"> Source directory too long. </String> <String Name="CannotWriteLogFile">Cannot write to log.</String> <String Name="DarwinServiceHung"> The Darwin service is hung and requires a reboot in order to continue. </String> <String Name="InternalError"> An internal error occurred while trying to initialize the Darwin Service. </String> <String Name="GeneralFailure"> A failure occurred attempting to install .NET Framework 2.0. </String> </Strings> </Package>

<Package> The <Package> tag references the Bootstrapper manifest schema, specifies the display name that should appear in the user interface of tools such as Visual Studio, the culture of the package, and a localized license agreement.

<PackageFiles> The <PackageFiles> section provides a list of all the files in the package. In the case of the .NET Framework English version, there is only one additional file in the package, namely, the license agreement. Other languages would include the langpack.exe file in the list.

<Strings> The <Strings> section is a list of localized messages for the user who is installing the component. Each string has a name that can be referenced elsewhere in the product and package manifests. In most cases, these strings will contain the display name for the component, the culture, and a list of error messages to be displayed if a particular failure occurs.

Custom Redistributables in Visual Studio

Adding your own redistributables to Visual Studio requires that you author the product and package manifests, then copy these files along with your redistributable files into a special folder that Visual Studio has reserved for redistributables that plug into the Bootstrapper. This folder is installed by default like so:

C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bootstrapper

To write an installer that copies a redistributable component and its manifests into this folder, you can programmatically discover the Bootstrapper folder location from the registry keys that are shown in the following:

HKCU\Software\Microsoft\GenericBootstrapper\1.0\Path HKLM\Software\Microsoft\GenericBootstrapper\1.0\Path

Each redistributable component appears in its own subfolder under the packages directory. The product manifest and redistributable files are then placed into this subfolder. Localized versions of the component along with the package manifests are placed in subfolders named after the culture code of that language (EN for English, JP for Japanese, and so on).

Once these files have been copied into the Bootstrapper folder, they automatically appear in the Visual Studio prerequisites dialog box so that the developer can select them by checking a box.

Generating the Bootstrapper Setup.exe with MSBuild

When you have selected a set of components to bootstrap, it is up to Visual Studio and the MSBuild system to configure the Bootstrapper setup.exe to handle those components. Bootstrapper configuration is handled by a special MSBuild task called GenerateBootstrapper. This build task performs a number of steps to produce the setup.exe. First, it obtains a copy of a base setup.exe image and writes the metadata from all of the relevant Bootstrapper manifests directly into the resource header of the setup.exe file. It then writes the URL (if provided) from which the redistributables should be downloaded into the resource header of setup.exe. Last, it copies the Bootstrapper, associated redistributables, and the actual application installer into the output directory.

The GenerateBootstrapper tasks are invoked by the Publish target, which runs whenever an application is published using Visual Studio ClickOnce tools. If Setup projects are used in order to build an MSI file for an application, the Bootstrapper task runs when the MSI file is built. The result is a lightweight setup.exe smart enough to bootstrap the selected components.

Conclusion

The Visual Studio 2005 Bootstrapper is a powerful and flexible new deployment tool that provides an integrated setup experience for an application and its prerequisite components. If you're building applications to run on nonstandardized desktop machines, the Bootstrapper installs all of the components and libraries required by your application, ensuring that at the end of the setup process the app runs smoothly. Especially important is the Visual Studio 2005 Bootstrapper's ability to install the .NET Framework 2.0 onto your customers' machines, thereby removing a major deployment hurdle for Visual Basic .NET and C# client apps.

[Editor's Update - 9/24/2004: Fortunately, if you are writing applications with Visual Studio .NET 2003 today, and you need to bootstrap the .NET Framework 1.1, you do not have to wait until 2005 for a solution. You can download the Visual Studio .NET 2003 Bootstrapper Plug-In, which integrates with Setup and Deployment projects to configure a setup.exe that can bootstrap both MDAC 2.8 and the .NET Framework 1.1.] .

Sean Draine(seandr@microsoft.com) is the deployment Program Manager on the Microsoft Visual Basic team. Before Microsoft, Sean earned a PhD in Cognitive Psychology and founded Millisecond Software, which produces psychological research software.