Packaging of a .NET application on Windows

This post is about how to package .NET application for Windows into an MSI (MicroSoft Installer) package using Wix# tool in command line. Packaging process can be easily automated on continues integration server.

msi_package

Generally application building workflow is simple:

  • Assign a version number to an assembly
  • Build binaries
  • Copy binaries to package folder and build msi-package with the same version as  assembly

Build process will be controlled by MSBuild project file and can be started from a continues integrations server.

Sample solution is available for download here https://github.com/mchudinov/PackagingMSI. Solution is compatible with Visual Studio 2013.

Automated building and versioning processes were described in my previous posts:



I need to create a MSI Windows package from my .NET binaries. The result should be a package that does the following while installation:

  • Package version is the same as an assembly version of an application
  • Shows standard Windows graphical installation wizard
  • Installs binary by default into a predefined folder. It is typically %Program Files% folder.

I use Wix# that automates WiX toolset that automates creation of MSI packages. Wix# is a framework for building a complete MSI or WiX source code by using script files written with the C# syntax.

1. Prepare packaging environment

Since Wix# script is written in C# and will run in command line I need a C# scripting engine. I use CS-Script engine. CS-Script must be installed on the continues integration server and added to PATH in order to build packages there.

Installation process is simple:

  1. Download the CS-Script archive from official website
  2. Unarchive it to a folder without spaces. I use c:\lib\cs-script
  3. Add this folder to the PATH environment varible
  4. Follow instructions in readme.txt

Check that CS-Script engine works after installation and it was added to PATH. Run cscs command in command line:

cscs

 

2. Create packaging project

2.1 Add an empty C# project to the solution

Packaging script should be added to a separate project. I name it <MyProjectName>.WixSharp. It should be an empty C# project.

EmptyCSharp

2.2 Add Wix#

Add WixSharp NuGet package to this project.

2.3 Add packaging script

Add a public class Script in this new project.

Script.cs must have the method static public void Main(string[] args). This will be my packaging script. Add following using to the file:

Note //css_ref %WIXSHARP_DIR%\wixsharp.dll is commented out. This is how it must be. This is a directive for cs-script.

Keep method Main simple for a while:

The new project should look like this:

SolutionsWixSharp

2.4 Test that CS-Script engine can run Script.cs

Write the following command in Windows command line:

You should see a Hello World! response from CS-Scritp running Script.cs

HelloCSScript

3. Create building script for the solution

Add simple MSBuild script to the project. Read more about MSBuild in my Building and Testing blogpost. This script will build and package application.

Build steps are the following:

  • Clean solution
  • Restore NuGet packages
  • Set version to assembles
  • Compile project

Note that versioning needs MSBuild.Extension.Pack and MSBuildTasks NuGet packages be installed.

Test build in Visual Studio command line msbuild Build.proj:

MSBuildprocess

Target Pack will be started separately in command line msbuild /target:Pack Build.proj

packbuild

4. Write packaging script

Do the following finale modifications:

Use same GUID in Script.cs as generated in AssemblyInfo.cs. (Or create a new GUID, but not use this one)

Then add AssemblyCompany info to assembly in AssemblyInfo.cs:

This is a requirement of MSI package.

Test msbuild /target:Pack Build.proj

packbuild2

Script will create a new MSI package named <project_name>.<version>.<daymonth>.msi in the solution folder.

Install this newly created package

PackMSI_gui

Then program will be available in Programs menu and in Control Panel

PackageMenu

package_control_panel