Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Windows-Installer

Creating a Localized Windows Installer & Bootstrapper: Part 4

4.50/5 (4 votes)
25 Feb 2013CPOL3 min read 37.1K   373  
This series of articles is a complete end-to-end solution for building a localizable Windows Installer & Bootstrapper using some real-world requirements.

Introduction

In Parts 1, 2 and 3, we created a localizable MSI installer using the Wix tools. We also created conditions to ensure that the application can only be installed on Dell hardware running Windows 7 and the Microsoft .NET Framework 4 Client Profile. In our original requirements, we stated that this must be deployable as a single file, and should install any prerequisites, specifically .NET 4. To achieve this, we need to create a bootstrapper that will preform the prerequisites installations from unmanaged code. The bootstrapper will be deployable as a single self-contained EXE with the original MSI file embedded inside.

I investigated many utilities for creating a bootstrapper, including the Wix SETUPBLD tool, and the MSBuild GenerateBootStrapper task. In the end, I settled on a powerful free utility called dotNetInstaller. This allows the creation of highly configurable and localizable bootstrappers which can be compiled from the command line.

DotNetInstaller

DoNetInstaller includes several utilities for authoring and compiling the bootstrapper. The bootstrapper is compiled from a single XML config file by the InstallerLinker.exe tool. To author the configuration file, an editor is provided in the form of InstallerEditor.exe.

Using the editor, language variations can quickly be set up through the UI and prerequisites defined by adding child nodes. Any prerequisite installers can either be embedded directly into the bootstrapper or a URL provided from which they will be downloaded and installed. In our case, we embed the Web Installer for the Microsoft .NET Framework 4 Client Profile. This will launch the installation process and download the necessary components on demand to keep the size of the bootstrapper down.

Once the prerequisites are installed, dotNetInstaller allows us to define a custom command to launch our own embedded MSI file using the complete_command property. We can set up different commands for different languages and therefore explicitly pass a transform to the MSI file after the prerequisite checks. If all prerequisites are found, we can hide the bootstrapper interface altogether and simply launch the MSI file.

DotNetinstaller includes its own auto-language detection based on either user or system settings. To leverage this, we can refer to a #LCID token in the config file that will evaluate to the detected LCID at runtime. This allows us to pass the LCID explicitly to our MSI in order to launch a particular language variation.

Through the command line, we can still inject properties into our MSI for overriding the hardware check for example. The syntax for this would be:

setup.exe /CompleteCommandArgs "SKIPCHECK=1"

The features of dotNetInstaller are too many to cover in this article, but there is plenty of documentation and it's very feature rich.

Build Automation

To complete our bootstrapper, we really want to integrate this into our build script and inject parameters such as product name and version. To achieve this, we can use a NAnt script that performs token replacement on the dotNetInstaller config file prior to compilation. I could have equally used XML manipulation, but NAnt makes the token replacement somewhat easy.

This is implemented using a filterchain with the NAnt copy task as shown below. The original config file is copied to a new location where it is modified and then parsed to the installer linker.

XML
<copy tofile="${installer.dir}\obj\${build.configuration}\Configuration.xml"
          file="${installer.dir}\bootstrapper\Configuration.xml" overwrite="true"
          inputencoding="utf-8"
          outputencoding="utf-8"
          >
      <filterchain>
        <replacetokens>
          <token key="APPLICATION_MANUFACTURER" value="${project.companyname}" />
          <token key="APPLICATION_NAME" value="${app.name}" />
          <token key="APPLICATION_VERSION" value="v${version}" />
          <token key="INSTALLER_VERSION" value="${installer.version}" />
          <token key="INSTALLER_COPYRIGHT" value="Copyright © 
		${datetime::get-year(datetime::now())} ${project.companyname}" />
          <token key="MSI_PATH" value="${installer.dir}\bin\$
				{build.configuration}\Setup.msi" />
        </replacetokens>
        <tabstospaces />
      </filterchain>
    </copy>

Summary

In this final part, we have fulfilled our original requirements and provided a build script for dynamically generating a localizable installer & self-contained bootstrapper. All of the source code and scripts are provided as a download on this page, and I hope it's useful for demonstrating the technologies in a real-world scenario. When building the demo solution, don't forget to replace the GUIDs in the source Config.wsi file with your own unique ids.

History

  • 22nd August, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)