How To: Update the bootstrapper to accept command-line arguments

In the previous blog entry, I went over the design and implementation of a modified bootstrapper which accepts command-line parameters. For example the bootstrapper which shipped with Visual Studio 2005 is not capable of "doing the right thing" for the following command:

setup.exe -qn

With the modifications, it is possile to get the MSI to install silently. Note the qualifier there: if the bootstrapper is installing prerequisites, it is not possible to get those to siliently install: during the design phase, we were told by the crack team of Microsoft lawyers that some UI (especially the EULA) had to be shown to end-users.

There are a few other limitations to the modified bootstrapper. Off-hand, I can think of the following:

  • If the MSI is signed, the signature is NOT verified by the bootstrapper
  • The error handling within the new bootstrapper (including an MSI which fails to initialize properly) is not well-handled
  • The testing of the modified bootstrapper is not up to Microsoft's standards

Without more ado, here are the steps necessary to get the modified bootstrapper up and running:

  1. Download and install the binaries for post-build steps (see the attachment of this post)
  2. Make sure that msbuild is on your path. You can do this by launching devenv from the Visual Studio command prompt
  3. Create your setup project
  4. Copy the template below into a file called "PostBuild.proj" in the same directory as your .vdproj file
  5. Add PostBuild.proj to your setup project
  6. Set Exclude to "True" for PostBuild.proj
  7. Set the PostBuildEvent (in the property grid) to:
    msbuild.exe /p:Configuration="$(Configuration)" /p:BuiltOutputPath="$(BuiltOuputPath)" /p:ProjectDir="$(ProjectDir)\" /p:BuiltOutputDir="$(ProjectDir)$(Configuration)" "$(ProjectDir)\PostBuild.proj"
  8. Turn off prerequisite generation for your setup project
  9. Update the properties in PostBuild.proj to match your particular installer (see images below to see how the MSBuild properties correspond to setup project properties). Do not ignore this step. At the very least, you need to update the ApplicationFile value in the PostBuild.proj file. (I just spent 15 minutes debugging trying to figure out why my extracted bootstrapper stopped working. It turns out it was because of this)
  10. Build your setup project

Setup project property grid

Setup property page

Bootstrapper dialog

Microsoft Data Access Components 2.8

Microsoft.Data.Access.Components.2.8

.NET Framework 2.0

Microsoft.Net.Framework.2.0

Crystal Reports for .NET Framework 2.0

BusinessObjects.CrystalReports.NET.2.0

Microsoft Visual J# .NET Redistributable Package 2.0

Microsoft.JSharp.2.0

Microsoft Visual Studio 2005 Report Viewer

Microsoft.ReportViewer.8.0

Visual C++ Runtime Libraries (x86)

Microsoft.Visual.C++.8.0.x86

Windows Installer 2.0

Microsoft.Windows.Installer.2.0

Windows Installer 3.1

Microsoft.Windows.Installer.3.1

SQL Server 2005 Express Edition

Microsoft.Sql.Server.Express.1.0

Bootstrapper packages and corresponding Product Codes

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="PostBuild"> 
    <Import Project="$(MSBuildExtensionsPath)\\SetupProjects\\SetupProjects.Targets" /> 
 
    <PropertyGroup> 
        <PostBuildDependsOn></PostBuildDependsOn> 
    </PropertyGroup> 
     
    <PropertyGroup> 
        <ApplicationFile>SetupExtractedBootstrapper.msi</ApplicationFile> 
        <ApplicationName>SetupExtractedBootstrapper</ApplicationName> 
        <ApplicationUrl></ApplicationUrl> 
        <ComponentsLocation>HomeSite</ComponentsLocation> 
        <ComponentsUrl></ComponentsUrl> 
        <Culture></Culture> 
        <OutputPath>$(BuiltOutputDir)</OutputPath> 
    </PropertyGroup> 
 
    <ItemGroup> 
        <BootstrapperPackage Include="Microsoft.Net.Framework.2.0" /> 
    </ItemGroup> 
 
    <Target Name="PostBuild" DependsOnTargets="$(PostBuildDependsOn)"> 
        <GenerateBootstrapper ApplicationFile="stub.exe"  
                              ApplicationName="$(ApplicationName)" 
                              ApplicationUrl="" 
                              BootstrapperItems="@(BootstrapperPackage)"  
                              ComponentsLocation="$(ComponentsLocation)" 
                              ComponentsUrl="" 
                              Culture="$(Culture)" 
                              OutputPath="$(OutputPath)" /> 
         
        <GenerateExtractedBootstrapper ApplicationFile="$(ApplicationFile)"  
                                       ApplicationUrl="$(ApplicationUrl)"  
                                       ComponentsUrl="$(ComponentsUrl)"  
                                       OutputPath="$(OutputPath)" 
                                       BootstrapperFile="$(OutputPath)\\setup.exe" 
                                       StubExe="$(MSBuildExtensionsPath)\\SetupProjects\\InstallMsi.exe" /> 
    </Target> 
</Project>

SetupProjects.Tasks-1.0.21129.0.msi