the blog
Back to -Blog

WiX in Visual Studio 2012

by Dermot Hogan
Monday 29 October 2012.

One of the consequences of the release of Visual Studio 2012 has been the demise of the Visual Studio Setup and Deployment project (vdproj). To be honest vdproj was never that great and I can’t say that I mourn its passing. But something has to replace it and the free InstallShield Express wasn’t capable of building the installations that I require.

We’ve been working on the next versions of Amethyst and Sapphire (Ruby in Steel) for some time now, and one of the jobs was to make sure that both worked with the new version of Visual Studio 2012. Mostly this has been straightforward with none of the labour required when moving from Visual Studio 2008 to Visual Studio 2010, when Microsoft introduced a new core text editor (that was hard work!). The main problem has been the requirement to find a replacement for vdproj that works with both Visual studio 2010 and Visual Studo 2012.

So after a bit of searching, I came across WiX (Windows Installer XML) available here. WiX uses a declarative XML based syntax to describe the installation and you then use the WIX toolset to construct an MSI file, which you install in the normal way. WiX is widely used inside Microsoft itself, and with the 3.6 release is stable and well integrated into Visual Studio 2012. Oh, and it’s free.

The problem I was faced with was a pretty complicated vdproj installation file – services had to be installed, DLLs loaded into the GAC with a messy set of over 100 registry settings plus a good number of files that had to be scatted over the target disk. On top of that, there were non-trivial installation ‘actions’, such as configuring manifest files, searching for SDKs and the like.

There were two approaches to the problem. The first would have been to start from scratch and build the WiX file by hand. The second, which I chose, was to use a tool to convert the vdproj file to WiX format. For the second option, there were two routes. One would have been to use Dark (all WiX tools have terrible punning names like Candle, Burn and so on) which takes an MSI file and spits out WiX XML. Wonderful! Until you look at the resulting XML – it is completely incomprehensible. It’s very like looking at C code produced from disassembling a native EXE file. My guess is that Dark is unusable for the purpose I wanted except for the simplest of MSI files. Another route was to use my favourite all-purpose parser ANTLR to analyse the vdproj fle and spit out WiX XML. And that’s the route I chose.

The first thing to do was to look at the structure of the vdproj file. This isn’t documented by Microsoft, but it isn’t too difficult to figure out what’s going on and to construct an ANTLR parser for it. The parser produces an internal parse tree as normal, but the next step is something that I’d never tried before. Instead of just analysing the tree and generating IntelliSense for an internal data base, I needed to output real, solid XML.

The parse tree generated by using ANTLR to scan the Amethyst vdproj file.

So again, after a bit of searching and thinking, I turned to ANTLR ’string templates’, something I’ve never used before. A string template sort of does the opposite to the ANTLR parser: given a parse tree, you can traverse it and match ‘templates’ to the tree elements. When an element matches, the corresponding string template is invoked and spits out a piece of text, in this case WiX XML. Here’s a small sample that outputs a WiX file command:

file_unit(component_id, file_id, sourcepath, targetname, gac, folder_id, guid) ::= <<
<DirectoryRef Id="$folder_id$">
 <Component Id="$component_id$" Guid="$guid$">
   <File Id="_$file_id$" Source="$sourcepath$" KeyPath="yes"$if(gac)$ Assembly=".net"$endif$ Checksum="yes"/>
 </Component>
</DirectoryRef>
>>

The template ‘payload’ is between the ‘<<’ and the ‘>>’ delimiters and it’s declared as a ’function’ that can be called from an ANTLR tree grammar. The ’function’s parameters like ’folder_id’ are substituted into the template body between ’$’ tokens’ (by default the token is ’<’, but ’$’ was more suibtable for XML). The template is invoked from the ANTLR tree grammar like this:

file_unit               
@init {string folderName; VdpToken t; bool inGAC; string targetName; string componentID; Guid g; string folderID;}
 : ^(FILE_UNIT id=STRING sp=STRING tn=STRING f=STRING sc=scatter_unit?) {
 t = f.Token as VdpToken;
 folderID = t.ID;

 folderName = _folders[folderID];
 t = id.Token as VdpToken;
 componentID = t.ID;
 switch (folderName) {
 case "ProgramFilesFolder":
 case "PersonalFolder":
 case "SystemFolder":
   folderID = folderName;
   break;
 default:
   break;
 }
 inGAC = folderName == "GAC";
 if ((sc!=null?((CommonTree)sc.Start):default(CommonTree)) != null) {
   targetName = (sc!=null?sc.dllName:default(string));
 } else {
   targetName = ((VdpToken)(tn.Token)).ID;
 }
 _sequence += 1;
 g = Guid.NewGuid();
 _components.Add(componentID, _sequence);
} -> file_unit(component_id={componentID}, file_id={_sequence}, sourcepath={((VdpToken)($sp.Token)).ID}, targetname={targetName}, gac={inGAC}, folder_id={folderID}, guid={g})
};

You can see that I’m mixing C# code and ANTLR tree grammar to invoke the ‘file_unit’ template which actually does the work of generating the WiX XML. The C# code builds up the arguments for the ’function call’ to the template. This might look a bit messy, and it did take me a little while to get the hang of ANTLR string templates, especially the conditional syntax, but it’s still, I think, faster than building the output by constructing the output file piece by piece in a program. And once the grammar and template exist, they can be modified simply.

The result is a fully functional WiX file, integrated into Visual Studio 20102 (and Visual Studio 2010). Here’s a snippet generated by the above ‘file’ string template: Looking back, I suspect that it would have taken me just about as long to construct the WiX file by hand as to do it using ANTLR and string templates. But I now have a new tool – ANTLR string templates - in my programming armoury. I think that will prove very useful in the future.

Bookmark and Share   Keywords:  development  sapphire  Visual Studio
© SapphireSteel Software 2014