BT

Using C# and Wix# to Build Windows Installer Packages

Posted by Jonathan Allen on Dec 10, 2014 |

 

We brought back Oleg Shilo, author of the CS-Script for Notepad++, to talk about Wix#.

InfoQ: For the benefit of our readers who haven't heard of it before, what is WiX#?

Oleg: Wix# (WixSharp) is a deployment authoring framework targeting Windows Installer (MSI). Wix# allows building complete MSI setups from a deployment specification expressed with C# syntax. The typical Wix# source file for building MSI contains plain-vanilla C# code, which uses a C# class structure to define (mimic) WiX entities.

Wix# answers many MSI authoring challenges. It solves the common MSI/WiX authoring limitations in a very elegant and yet unorthodox way. Wix# follows the steps of other transcompilers like Script#, CoffeeScript or GWT by using source code of a more manageable syntax (C# in this case) to produce the desired source code of a less manageable syntax (WiX). A "more manageable syntax" in this context means less verbose and more readable code. The code with a better compile-time error checking and availability of the advanced tools.

Wix# also removes any need to develop MSI sub-modules (Custom Actions) in a completely different language (e.g. C++) by allowing both components and behavior to be defined in the same language (C#). This also allows homogeneous, simplified and more consistent source code structure.

But what is even more important is that Wix# provides an abstraction layer that hides all the complexity and unintuitive nature of MSI/WiX and allows expressing the deployment algorithm in a more natural way. The following is a typical Wix# example that demonstrates a Wix# script for building an MSI file for installation of "My App" product:

using System;
using WixSharp;
class Script
{
    static public void Main()
    {
        var project = new Project("My App",
                          new Dir(@"%ProgramFiles%\My Company\My App",
                              new File(@"\\BuildServer\LatestRelease\Bin\MyApp.exe"),
                              new File(@"\\BuildServer\LatestRelease\Bin\MyApp.exe.config"),
                              new File(@"\\BuildServer\LatestRelease\Docs\readme.txt")));
        project.GUID = new Guid("6f330b47-2577-43ad-9095-1861ba25889b");
        Compiler.BuildMsi(project);
    }
}

Of course Wix# can handle more complex scenarios (Custom Actions or even custom UI written in WPF or WinForms) but the sample above is a perfect demonstration of what Wix# is really about.

Wix# belongs to the CS-Script toolset - an Open Source script engine suite for executing C# scripts. The InfoQ readers may be already familiar with it as I have already described it in my previous interview about CS-Script Notepad++ plugin.

InfoQ: Why did you choose to express Wix# in the form of an API instead of a domain specific language?

Oleg: The objective of Wix# is not to replace XML syntax with a different syntax. The alternative syntax would only address one of many practical limitations of the MSI+WiX combination. With Wix# I wanted to bring the deployment development back to the main stream programming. I wanted to move it closer to the developers. I wanted the very same developers who creates software to be able to create very quickly and comfortably deployment solutions as well. DSL would mean that developers need to learn yet another syntax and also to suffer from absence of the code assistance tools (e.g. Intellisense). Most likely DSL would also require developing Custom Actions in another syntax again. Instead, I wanted C# and your favorite IDE to become a one-stop-shop for the deployment development. Then your development team doesn't need a designated WiX-guy, the usual programming skill set is sufficient enough for implementing practically any deployment algorithm.

Though there is a less obvious reason for not choosing DLS approach. The typical DSL story is either a translation of one syntax to another one or a conversion of one programming model to another one. The translation story has very little practical value in case of WiX. While it is simple to implement it would only map the original WiX syntax one-to-one to a new simpler one and leave the all MSI limitations unaffected. From the other hand, conversion to a new clean and more intuitive programming model is exactly what WiX is needed. Such a conversion is usually achieved with high-level generic programming languages (C# in case of Wix#). And after that the new programming model is typically bound to a new DSL syntax. However in Wix# case this last step (new syntax binding) wouldn't bring any extra value. Wast majority of Windows developers is already comfortable or at least familiar with .NET languages. Thus the new DSL syntax would only bring a new overhead. So I decided to stick with C#.

Saying that, a simple translation based DSL on top of Wix# programming model may make some sense and it is actually easy to implement. But it is a completely different story...

InfoQ: Aside from the complexity of the WiX format, what do you feel is the biggest hindrance for WiX adoption?

Oleg: This is a very good question. Yes WiX is indeed very complex but the complexity isn't the real obstacle for the WiX adoption. Close ties to MSI are. WiX dramatically simplifies access to the MSI programming model. However this model is the core of the problem not the difficulties with accessing it. Thus WiX users still have to put up with the nuisances of MSI but now via XML syntax.

Here I have to stop and say a few words in defense of WiX. The faulty MSI programming model isn't WiX's fault. In fact considering what WiX did for MSI it would be almost impossible to expect WiX to address the MSI limitations. In order to really appreciate WiX we need to look back at the evolution of Microsoft deployment technologies.

When MSI was introduced it was trying to be the 'answer for everything'. And as with many Microsoft technologies it was over-engineered from the start. It hasn't changed sense then. Microsoft implemented many complicated MSI features that have very little practical value today (e.g. components, MSM, advertised installation...).

And there is no surprise that Microsoft recognized the problem and got rid of all these features in their latest attempt - ClickOnce. ClickOnce is an extremely lean deployment framework. In fact Microsoft completely ditched the MSI concept and implemented ClickOnce from scratch as an MSI alternative. It is light, clever and minimalistic (in a good way). Though it is completely closed and neither customizable nor extendable.

On top of the internal flaws MSI is extremely development unfriendly. MSI setup file is a database file. The deployment logic is expressed via the data in the tables. Microsoft expected the MSI development to be conducted by entering the data manually, field by field, with their MSI-table editor Orca. This gave a fantastic revenue opportunity for InstallShiled (by offering overpriced MSI support) but left developers with practically nothing. Then WiX happened. And it stopped the "MSI chaos". WiX absolutely deserves all credit for bringing the order and making authoring MSI into a software development (not data entry) activity - "building the binary from the source file". Thus WiX allowed to populate those MSI tables from the XML file instead of doing it manually. However it did not changed the essence of the MSI development. With WiX the developers still had (and have) to think "in MSI Tables".

Just a couple weeks ago one of Wix# users shared his work around for a simple registry-only MSI scenario. The problem was that MSI/WiX required the install directory to be defined...even if you are not going to install any files but registry key only. The work around was to define dummy install directory and then ... define "remove directory" action to avoid the physical creation of the dummy directory during the installation. All this is not because of the deployment requirements but because of the MSI mind bending limitation: Directory table must have at least a single entry.

Wix# from another hand allows automatic creation of the dummy directory entry and hiding the MSI/WIX complexity completely:

var project = new Project("MyProduct",
                  new RegValue(RegistryHive.LocalMachine, "Software\\My Company\\My Product", "Message", "Hello"),
                  new RegValue(RegistryHive.LocalMachine, "Software\\My Company\\My Product", "Count", 777)); 

project.GUID = new Guid("6f330b47-2577-43ad-9095-1861ba25889b"); 

Compiler.BuildMsi(project);

The WiX equivalent of the code above will take about 40 lines of very dense XML content and batch file for executing WiX compiler and linker.

"Dummy directory" is not the only case. There are plenty of other MSI/WiX syntactic "obstructions" that are avoided in Wix#.

Many WiX gurus think that being so close to MSI architecture is a strength of WiX because it allows the ultimate access to the MSI functionality. I disagree. I believe it prevents the whole technology adoption because it ignores developer needs. Thus WiX is completely MSI-oriented instead of being developer oriented. But we shouldn't blame WiX for this. I am not even convinced WiX should be concerned about this at all. In my opinion there should be another layer that would take any deployment requirement/specification (e.g. Wix# file) and convert it into MSI-DB specification (WiX file).

WiX is playing the same role in the MSI environment as IL in .NET. IL allows the ultimate access to the CLR functionality but yet no one is using it for the development. We have all .NET high level languages for this. Thus if I rephrase your question as: "What is the biggest hindrance for IL adoption?" it will be easy to answer. There is no need for such an adoption. We have much better development options than IL (while IL is a vital part of the whole.NET technology). And Wix# is an attempt to bring at least one of such "better options" but into MSI domain. Thus Wix# or any similar solution is just a next logical evolutionary step from WiX.

InfoQ: What do you see as the most exciting feature that WX# offers beyond making basic installer packages easier?

Oleg: There are a few very strong features worth to mention.

a. First of all is that unbeatable simplicity of the managed Custom Actions, which comes from the power of the high level programming language (C#):

using System;
using WixSharp;
class Script
{
    static void Main()
    {
        var project = new Project("CustomActionTest",
                           new ManagedAction("MyAction", Return.check, 
                                             When.After, Step.InstallInitialize, Condition.NOT_Installed));
        Compiler.BuildMsi(project);
    }
}
public class CustomActions
{
    [CustomAction]
    public static ActionResult MyAction(Session session)
    {
        MessageBox.Show("Hello World!", "Embedded Managed CA");
        session.Log("Begin MyAction Hello World");
        return ActionResult.Success;
    }
}

b. Custom UI is the another one. Wix# not only allows injecting a plain vanilla WinForm dialog into the UI sequence. It also makes it possible to create a complete custom GUI and hook it to the MSI engine at runtime. This fantastic possibility offered by MSI API is completely overlooked by practically all MSI authoring frameworks, while Microsoft uses it in so many products (e.g. Office, Visual Studio setups).

I do consider the UI customization as very controversial deployment technique and discourage (for design reasons) developers from going this way. But I just couldn't ignore the user demands and implemented it anyway. The following is the screenshot of the WPF sample (from the Wix# distro) for the custom UI:

c. The last feature I want to mention is Wix# extensibility. The deployment definition is just a C# classes declaration. It can be easily improved by using custom utility classes for simplification or modification of the typical setup definition. For example instead of listing all the files you can create the class (e.g. AllFiles) that iterates through all directory structure and does this for you dynamically. So no need to modify the setup script if your product needs to distribute a new dll:

var project = new Project("My App",
                  new Dir(@"%ProgramFiles%\My Company\My App",
                      new AllFiles(@"\\BuildServer\LatestRelease\*.*")));

In cases when Wix# functionality is not sufficient you can directly manipulate generated XML (WiX) content my means of the Wix# compiler event handlers (just before the XML is passed to the MSI compiler). You can inject or remove any element or attribute from the XML tree. The distributable samples demonstrate this technique by showing how the ASP.NET web site can be installed by using Wix# native functionality as well as by injecting required XML just before the compilation.

By the way the samples library is so significant that it takes ~95% of the whole Wix# codebase.

At this stage of Wix# life cycle I consider it's core functionality to be practically complete. Thus the new features will rater represent horizontal then vertical evolution. They will be mostly about integration with the development tools and making the developers more comfortable with the framework. My plans include publishing Wix# in NuGet, providing a complete equivalent of the full MSI UI but as an external set of WinForm dialogs, developing dedicated Notepad++ plugin and possibly MSBuild task with the project template (currently Visual Studio handles Wix# as a regular C# project). And of course processing the users feedback.

Wix# is available on CodePlex under the MIT license.

About the Interviewee

Oleg Shilo was born in Ukraine, where he got his original qualification - Master's Degree in Research Chemistry. After moving to Australia 18 years ago he obtained his second qualification as a Software Engineer and worked in Australian leading engineering companies in the Robotics and Automationdomain. For the last 5 years he worked as a Product Leader at Aqsacom Australia (Lawful Interception).

Rate this Article

Relevance
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Great article by Marvin Fenner

Oleg has artfully summed up many of the frustrations MSI install developers have experienced over the years. Thank you for the articulate reinforcement, and also thanks for creating great tools. I look forward to evaluating Wix# immediately.

Wix# user, love it! Great interview! Learned a couple helpful things here! by Jim Daniels

I ran across Wix# back in July on the CodeProject site. See Oleg's article and code here: www.codeproject.com/Articles/31407/Wix-WixSharp...

I used Wix# to do a tricky setup of a .NET dll registered for COM Interop on a Windows 2008 Server. Another team member had done a little WiX XML installer, but looking at the XML and trying to figure out how to make XML "do installer stuff" just made my head hurt. Then I saw the CodeProject Wix# article, and decided to give it a try. What a relief and a pleasure to code an installer in C#!! Yes, there were some tricky parts and quirks, and I had to learn some WiX and Windows Installer in the process, to know what Wix# could and couldn't do. For example, I had to learn how to make installer components 64-bit so they hit the 64-bit registry, using syntax like "{ AttributesDefinition = "Component:Win64=yes" }". But, in the end, it all worked!!

I was able to give our server admin guys a walkthrough of the C# code for the installer before asking them to install it on "their" servers, so they could see what it was doing and trust the process. Having reviewed the source, they were happy to then accept my MSI installer, and install it on the servers with confidence and trust.

Pretty sure I was the Wix# user Oleg mentioned in the article who pointed out the dummy directory left on the target system after a registry-only install. It felt "dirty" leaving a stray directory on the servers after an install. I found some WiX XML syntax and used the XML injection technique to bundle it neatly into my Wix# installer. Then Oleg did a nice enhancement to Wix# that removes the directory in situations like mine. Now I've been bugging him to automatically import .reg files into Wix# to further simplify registry entries deployment, and he has it in process.

There are now a few more Wix# questions and answers on Stack Overflow than there were a couple weeks ago, under the "WixSharp" tag, courtesy of me. WiX XML people have a great helpful presence on Stack Overflow, which also helps Wix# users. The great thing is that you can ask WiX questions, and get WiX XML answers, and then take those answers and use them in your Wix# installer.

I will have to try Oleg's tip in the article:
"The deployment definition is just a C# class declaration. It can be easily improved by using custom utility classes for simplification or modification of the typical setup definition. For example instead of listing all the files you can create the class (e.g. AllFiles) that iterates through all directory structure and does this for you dynamically. So no need to modify the setup script if your product needs to distribute a new dll"
This will be a huge help in large installs with tons of files to deploy, where the list of files and folders is subject to frequent additions or changes. Website deployments come quickly to mind... new pages, new folders, on an ongoing basis.

For most .NET developers, I believe Wix# + WiX XML (via XML Injection) is so much better of an option for Windows installers than anything else I've seen out there.

Thanks Oleg, for all your fantastic work making this available to .NET developers!

Create msi package at run time. by tenkani bharati

How to wrap multiple exe files into msi package with InstallShield at run time..Please help me creating msi package with code at run time instead of manually creating msi package with install shield.

Create msi package at run time. by tenkani bharati

How to wrap multiple exe files into msi package with InstallShield at run time..Please help me creating msi package with code at run time instead of manually creating msi package with install shield.

Create msi package at run time. by tenkani bharati

How to wrap multiple exe files into msi package with InstallShield at run time..Please help me creating msi package with code at run time instead of manually creating msi package with install shield.

Create msi package at run time. by tenkani bharati

How to wrap multiple exe files into msi package with InstallShield at run time..Please help me creating msi package with code at run time instead of manually creating msi package with install shield.

Create msi package at run time. by tenkani bharati

How to wrap multiple exe files into msi package with InstallShield at run time..Please help me creating msi package with code at run time instead of manually creating msi package with install shield.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

7 Discuss
General Feedback
Bugs
Advertising
Editorial
Marketing
InfoQ.com and all content copyright © 2006-2016 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT