I will be using Visual Studio 2012 soon. I need it for Windows Phone 8 development and it is getting to be a bit annoying having multiple versions of Visual Studio installed. I already have VS 2008 for some of my day job projects and VS 2010 installed because it was newer and better when it came out.

The biggest problem with VS 2012 is that it lacks support for basic Windows installer functionality. In other words, the setup deployment project type is no longer supported and ClickOnce is not available for C++ projects; at least not in a simple way.

Wix to the Rescue

Wix is a tool for creating setup files from XML data. It is not well documented and it took me at least eight hours to get a basic working installer for the Linkage program. When I say that I have a basic working installer, what I mean is that I have a linkage.msi file that will install the linkage software and documentation and will create a desktop shortcut and a Programs menu shortcut. That’s it.

The Wix XML

I will present the XML data that I am using then describe some of the problems I had. I did have two requirements to fulfill before I considered this a working solution. The first to have some shortcuts and the second was to be able to install the software with no oddball errors. I’ll get back to that.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

    <?define PRODUCTVERSION="2.9.12"?>
    <?define UPGRADECODE="363E6270-4C3C-4022-80C5-32499430E4BE"?>

    <Product Id="*" Name="Linkage" Language="1033" Codepage="1252" Version="$(var.PRODUCTVERSION)" 
             Manufacturer="David Rector" UpgradeCode="$(var.UPGRADECODE)">

        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Id="*" SummaryCodepage="1252"/>

        <Upgrade Id="$(var.UPGRADECODE)">
            <UpgradeVersion OnlyDetect="yes"
                            Minimum="$(var.PRODUCTVERSION)"
                            Property="NEWERVERSIONDETECTED"
                            IncludeMinimum="no" />

            <!-- 
                The IncludeMaximum="yes" attribute will cause an ICE612 warning normally.
                My project supresses that warning. The end result is that my failure to
                increase the version number of this install doesn't stop the installer from
                removing the old version and installing this new one. Change the attribute
                to "no" and the user will see an error during install that the same version
                is already installed but is from a different build. That's a strange error
                for a user to see so I just let the install happen.

                A symptom of not detecting the alternate build with the same version is
                having multiple entries in the "Programs and Features" list in the Control
                Panel. If you see that then you have multiple different builds with the same
                version number!         
            -->

            <UpgradeVersion OnlyDetect="no"
                            Maximum="$(var.PRODUCTVERSION)"
                            Property="OLDERVERSIONBEINGUPGRADED"
                            IncludeMaximum="yes" />

        </Upgrade>

        <CustomAction Id="CA_BlockOlderVersionInstall" Error="A newer version of Linkage is already installed. You will need to remove it first if you want to install this older version." />
        <CustomAction Id="CA_BlockAnotherBuildInstall" Error="Another version of Linkage is already installed but it has the same version number as this version. Uninstall the other version or contact customer support to help resove this issue." />

        <InstallExecuteSequence>

            <Custom Action="CA_BlockOlderVersionInstall" After="FindRelatedProducts">
                <![CDATA[NEWERVERSIONDETECTED]]>
            </Custom>

            <Custom Action="CA_BlockAnotherBuildInstall" After="FindRelatedProducts">
                <![CDATA[ANOTHERBUILDINSTALLED]]>
            </Custom>
            
            <LaunchConditions After="AppSearch" />

            <RemoveExistingProducts After="InstallInitialize" />
        </InstallExecuteSequence>


        <InstallUISequence>
            <Custom Action="CA_BlockOlderVersionInstall" After="FindRelatedProducts">
                <![CDATA[NEWERVERSIONDETECTED]]>
            </Custom>

            <Custom Action="CA_BlockAnotherBuildInstall" After="FindRelatedProducts">
                <![CDATA[ANOTHERBUILDINSTALLED]]>
            </Custom>

            <LaunchConditions After="AppSearch" />
        </InstallUISequence>
        
        
        <MediaTemplate/>

        <Feature Id="ProductFeature" Title="Linkage" Level="1">
            <ComponentRef Id="LinkageExecutable"/>
            <ComponentRef Id="LinkageDocumentation"/>
            <ComponentRef Id="LinkageMisc"/>
        </Feature>

        <Icon Id="icon.exe" SourceFile="..\res\icon.exe" />
        <Property Id="ARPPRODUCTICON" Value="icon.exe" />
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="DesktopFolder" Name="Desktop"/>
            <Directory Id="ProgramMenuFolder">
                <Directory Id="ApplicationProgramsFolder" Name="Linkage"/>
            </Directory>
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="Linkage"/>
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <Component Id="LinkageExecutable" Directory="INSTALLFOLDER" Guid="7AF9110C-E88C-4BBE-A510-1933A0798D8A">
            <File Id="linkage.exe" Name="linkage.exe" DiskId="1" Vital="yes" Source="..\Release\linkage.exe" KeyPath="yes">
                <Shortcut Id="ApplicationStartMenuShortcut"
                          Advertise="yes"
                          Directory="ApplicationProgramsFolder"
                          Name="Linkage"
                          Icon="icon.exe"
                          Description="Linkage Mechanism Designer and Simulator"
                          WorkingDirectory="INSTALLFOLDER"/>
                <Shortcut Id="DesktopMenuShortcut"
                          Advertise="yes"
                          Directory="DesktopFolder"
                          Name="Linkage"
                          Icon="icon.exe"
                          Description="Linkage Mechanism Designer and Simulator"
                          WorkingDirectory="INSTALLFOLDER"/>
            </File>
            <RemoveFolder Id="ApplicationProgramsFolder" Directory="ApplicationProgramsFolder" On="uninstall" />
        </Component>

        <Component Id="LinkageDocumentation" Directory="INSTALLFOLDER" Guid="27D1D154-2197-4B00-9B1D-D97C57D2E96D">
            <File Id="linkage.pdf" Name="linkage.pdf" DiskId="1" Vital="yes" Source="..\linkage.pdf" />
        </Component>

        <Component Id="LinkageMisc" Directory="INSTALLFOLDER" Guid="ACE32A96-1D38-453D-9E22-671366577E46">
            <File Id="history.txt" Name="history.txt" DiskId="1" Vital="yes" Source="..\history.txt" />
        </Component>

    </Fragment>
</Wix>

The Wix XML was tough to create. Most examples that I found were weak; they either had XML that caused errors when I used it or they had example pieces of the XML file and other data was clearly needed to finish the example. Dang, if you’re going to tell someone to create XML tags in a file, please tell them where in the file that new XML will go; what is the enclosing tag?

Some examples were great but it took a while to find them. There was a lot of conflicting information when it came to the structure of the data in the file.

I present a complete XML file for an installer that works. Here were some issues:

I had trouble finding out how to remove old versions of the software. I ended up finding what I needed and I even figured out how to deal with repeated development installs that didn’t require a version number increment every time I made a change to the installer or program source.
Installing shortcuts for all users is a pain. I had to use advertised shortcuts that are part of the <File> element for the executable. Other methods never worked right.
Adding a <RemoveFolder> element for the executable file didn’t work and produced and ICE error until I finally figured out that it needed an Id attribute and a Directory attribute!
I could not find a copy of a working XML file like the one I am presenting here. I found lots of parts but no whole file.

So here it is. It might work for someone else and it might not.

My next task is to add dialog boxes so that I can ask the user if this is a per-user or all-user install and to show when the install is finished. I expect the per-user vs. all-user installs to be troublesome since there are many problems related to this that are reported but not resolved in many online forums.

Wix Home Page