WiX Custom .NET Bootstrapper with UI Part 1

Posted on: May 15, 2020 1:35:36 AM
I recently had the task of creating a custom UI for a WiX installer that uses a bootstrapper. This particular UI had to allow the user to customize their installed features along with installing the primary product. As it turns out, there is not many examples out there of how to best do this. I did find one (https://www.wrightfully.com/part-1-of-writing-your-own-net-based-installer-with-wix-overview), but it wasn't a complete project, so there was still a lot to figure out on my own. I did base a lot of my bootstrapper code from the example I found, it provided a great starting place.
There are a few requirements to get the WiX bootstrapper to use a custom UI.
  1. Your UI project must reference BootstrapperCore - installed with WiX
  2. Your project must contain a BootstrapperCore.config - this provides WiX with the .NET Framework requirements to run your UI
  3. Your project must contain a class that inherits from BootstrapperApplication - this is the entry point for WiX
  4. You must inform the bootstrapper about your project
Since setting a project reference should be pretty self explanatory, I'm going to start with BootstrapperCore.config. My first attempt at adding the config, I tried adding it to the App.config file. As it turns out, this doesn't work, the file must be named BootstrapperCore.config for it to be picked up. This is one of those things that isn't documented very well. Here is the contents this file in the example project I made.
BootstrapperCore.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="wix.bootstrapper" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore">
      <section name="host" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore" />
    </sectionGroup>
  </configSections>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" />
  </startup>
  <wix.bootstrapper>
    <host assemblyName="Bootstrapper.UI">
      <supportedFramework version="v4\Full" />
      <supportedFramework version="v4\Client" />
    </host>
  </wix.bootstrapper>
</configuration>
An important thing to point out in this file that is easy to miss is setting the assemblyName. This value must be the name of the assembly that contains your UI, in my case Bootstrapper.UI. I will admit that I played around with the supportedFramework properties, because my project targets .NET Framework 4.6.2. I wasn't able to find a combination that worked, so I left it at 4.0, it seems to work fine but it may cause issues if the .NET Framework 4.0 is not installed.
Next I'd like to talk about informing the bootstrapper about your project. I'm doing this one next because it's one of the smaller pieces needed. When you're telling the bootstrapper about your project, you need to tell it about your library along with all of it's dependencies. You also need to inform it where to find the BootstrapperCore.config file. Below you will find the contents of my example project bootstrapper.
Bundle.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Bundle Name="Bootstrapper" Version="!(bind.packageVersion.Msi_Installer)" Manufacturer="Justin" UpgradeCode="1b9106f0-5ba8-4857-ac0a-c1becba1fe51">
    <BootstrapperApplicationRef Id="ManagedBootstrapperApplicationHost">
        <Payload SourceFile="$(var.Bootstrapper.UI.TargetPath)" />
        <Payload SourceFile="$(var.Bootstrapper.UI.ProjectDir)BootstrapperCore.config"/>
        <Payload SourceFile="$(var.Bootstrapper.UI.TargetDir)BootstrapperCore.dll"/>
        <Payload SourceFile="$(var.Bootstrapper.UI.TargetDir)Microsoft.Deployment.WindowsInstaller.dll"/>
    </BootstrapperApplicationRef>

        <Chain>
        <PackageGroupRef Id="NetFx462Web"/>
        
        <MsiPackage Id="Msi_Installer" SourceFile="$(var.Installer.TargetPath)" EnableFeatureSelection="yes" Compressed="yes" />
        </Chain>
    </Bundle>
</Wix>
You'll notice that I'm using variables to inform the project about the file paths. I can do this because I added my project as a reference to the bootstrapper project. You can find a list of all available reference variables at https://wixtoolset.org/documentation/manual/v3/votive/votive_project_references.html.
For this next part, I'm going to only go over the key pieces of it because it's quite a bit larger than the others. This next part is going to be going over the entry point for the UI application, the BootstrapperApplication. The entry point into this class is the Run() method. This is where you will want to determine if UI should be shown or execute the command line options. It's important to remember that you can't just ignore the command line arguments because things like upgrade installations will attempt to uninstall the previous installation using command line arguments. If you don't handle these, the uninstall will never happen and the installers will hang indefinitely without informing your user about anything. Here is a code snippet from my example project.
BootstrapperEntry.cs - snippet
    protected override void Run()
    {
        WaitForDebugger();

        InitializePackages();

        _BootstrapDispatcher = Dispatcher.CurrentDispatcher;

        // should UI be displayed
        if (Command.Display == Display.Full || Command.Display == Display.Unknown)
        {
            Engine.Log(LogLevel.Verbose, "Launching custom UX");

            _InstallerWindowViewModel = new InstallerWindowViewModel(this);

            InstallerWindow installerWindow = new InstallerWindow
            {
                DataContext = _InstallerWindowViewModel
            };
            installerWindow.Closed += (s, e) => _BootstrapDispatcher.InvokeShutdown();
            installerWindow.Show();

            Dispatcher.Run();

            Engine.Quit(_ErrorCode);
        }
        else
        {
            DetectComplete += (sender, args) => Plan(Command.Action);
            PlanComplete += (sender, args) => Execute();
            ExecuteComplete += (sender, args) =>
            {
                Engine.Quit(args.Status);
                _BootstrapDispatcher.InvokeShutdown();
            };

            Detect();

            Dispatcher.Run();
        }
    }
First, with an MSI there are certain steps that always have to happen. You first need to detect the current state, then plan the next state, lastly you execute the plan. So when we aren't showing the UI, these steps still need to occur. The Engine property provided by the base class allows you to register to events during the entire installation process. In this case, we want to register to the completion step of each of these steps so we can automatically start the next step. We then tell then want to push the main execution frame on the event queue by calling Dispatcher.Run().
Debugging can be very difficult, that's why the very first method I call is WaitForDebugger(). This method checks for a command line parameter being passed in of DEBUG. If this value exists, the process will enter a loop checking if a debugger has been attached every half second. This gives you time to attach your favorite debugger to help you troubleshoot from the very beginning. Here is my implementation of that method.
BootstrapperEntry.cs - snippet
    
    private void WaitForDebugger()
    {
        if (Command.GetCommandLineArgs().Contains("DEBUG"))
        {
            Engine.Log(LogLevel.Verbose, "Waiting for debugger to be attached...");

            while (!Debugger.IsAttached)
            {
                Thread.Sleep(500);
            }

            Debugger.Break();
        }
    }
We also need to be able to collect and store the packages and features that are going to be installed. This step is important because it is going to help us tell the installer what to do and tell our UI the what state to show based on what the installer needs to do. At this point, we only know what the installer has in it, we won't know the current state until the Detect() method is called. In order to get what is in the installer, we need to parse an XML file that every bootstrapper creates upon launch. This file is created in the working directory of the installer and can be very easily parsed. I've created POCO classes to hold the information we want to gather from this file. I store this data in a property called Packages
BootstrapperEntry.cs - snippet
    private readonly XNamespace ManifestName = "http://schemas.microsoft.com/wix/2010/BootstrapperApplicationData";
…
    private void InitializePackages()
    {
        const string DataFilePathName = "BootstrapperApplicationData.xml";
        const string ApplicationDataNamespace = "BootstrapperApplicationData";
        const string MbaPrereqNamespace = "WixMbaPrereqInformation";
        const string PackageNamespace = "WixPackageProperties";
        const string FeatureNamespace = "WixPackageFeatureInfo";

        var workingDir = Path.GetDirectoryName(GetType().Assembly.Location);
        var dataFilePath = Path.Combine(workingDir, DataFilePathName);
        XElement applicationData = null;

        try
        {
            using (var reader = new StreamReader(dataFilePath))
            {
                var xml = reader.ReadToEnd();
                var xDoc = XDocument.Parse(xml);
                applicationData = xDoc.Element(ManifestName + ApplicationDataNamespace);
            }
        }
        catch (Exception ex)
        {
            Engine.Log(LogLevel.Error, $"Unable to parse {DataFilePathName}.\nReason: {ex.Message}");
        }

        var mbaPrereqs = applicationData.Descendants(ManifestName + MbaPrereqNamespace)
                                        .Select(x => new MbaPrereqPackage(x));
        // exclude prereq packages
        Packages = applicationData.Descendants(ManifestName + PackageNamespace)
                                      .Select(x => new BundlePackage(x))
                                      .Where(pkg => !mbaPrereqs.Any(preReq => preReq.PackageId == pkg.Id))
                                      .ToArray();

        // get features and associate with their package
        var featureNodes = applicationData.Descendants(ManifestName + FeatureNamespace);
        foreach (var featureNode in featureNodes)
        {
            var feature = new PackageFeature(featureNode);
            var parentPkg = Packages.First(pkg => pkg.Id == feature.PackageId);
            parentPkg.Features.Add(feature);
            feature.Package = parentPkg;
        }
    }
This will conclude part 1 of this series. There is a lot to cover and I feel that splitting it up is the best way to handle it. I will post a link to the complete project in the final part of the series.

Comments


gay cam chat [url="https://chatcongays.com"]321 gay chat[/url] free gay chat lines in fitchburg,ma

GenniesrPa September 16, 2022 1:26:46 AM

chat gay joven [url="https://chatcongays.com"]gay men snap chat[/url] dubuque gay chat

GenniesrPa September 16, 2022 6:40:46 AM

write my essay generator [url="https://au-bestessays.org"]help with essays assignments[/url] best essay writing websites

MarrissrPa September 20, 2022 12:17:18 AM

service essay [url="https://bestcampusessays.com"]best custom essay writing service[/url] write my essay services

DorolisasrPa September 20, 2022 7:31:05 PM

essay revision help online [url="https://bestcampusessays.com"]admission essay editing service[/url] the help essay prompts

DorolisasrPa September 20, 2022 8:07:02 PM

the best essay writers [url="https://besteasyessays.org"]essay paper help[/url] essay writing service canada

MartysrPa September 21, 2022 5:49:56 PM

college admission essay writing service [url="https://besteasyessays.org"]online essay writing services[/url] common app essay help

MartysrPa September 21, 2022 6:28:14 PM

custom essays for cheap [url="https://bestessayreviews.net"]hire essay writer[/url] argumentative essay helper

MerolasrPa September 22, 2022 2:27:58 PM

essay about helping others [url="https://bestessaysden.com"]pay you to write my essay[/url] buy essay

AshlensrPa September 23, 2022 9:32:31 AM

essay help service [url="https://bestessaysden.com"]essays online to buy[/url] essay writing website reviews

AshlensrPa September 23, 2022 10:13:51 AM

customized essay writing [url="https://bestsessays.org"]custom essay UK[/url] essay writing service reviews

CharitasrPa September 24, 2022 11:12:04 PM

help with college essay [url="https://bestsessays.org"]best essay writing websites[/url] write my essay custom writing

CharitasrPa September 24, 2022 11:48:48 PM

help with college essays [url="https://buyacademicessay.com"]best writing paper[/url] can you write my essay

NanicesrPa September 25, 2022 6:10:11 PM

help writing college application essay [url="https://buyacademicessay.com"]help on writing an essay[/url] cheap essay writing service online

NanicesrPa September 25, 2022 6:46:50 PM

buy essay paper [url="https://buy-eessay-online.com"]medical school essay service[/url] which essay writing service is the best

ChelsaesrPa September 26, 2022 1:50:09 PM

help with college essays [url="https://buy-eessay-online.com"]customer essay[/url] buy essay

ChelsaesrPa September 26, 2022 2:28:44 PM

custom essay company [url="https://buytopessays.com"]help writing an essay for college[/url] my custom essay

PennysrPa September 27, 2022 8:56:45 AM

i need help writing my essay [url="https://buytopessays.com"]help starting an essay[/url] best writing services reviews

PennysrPa September 27, 2022 9:37:05 AM

best writing service [url="https://cheapessaywritingservice1.com"]essay writers online[/url] custom essay writing service org

TammiesrPa September 28, 2022 4:50:34 AM

write my essay website [url="https://customcollegeessays.net"]custom essays usa[/url] essay writing services usa

AntoniesrPa September 29, 2022 12:48:27 AM

buy essay writing online [url="https://customcollegeessays.net"]looking for someone to write my essay[/url] help in essay writing

AntoniesrPa September 29, 2022 1:21:46 AM

college essay service [url="https://customessays-writing.org"]essays writing service[/url] essay writers toronto

RhiamonsrPa September 29, 2022 8:25:46 PM

buy pre written essays [url="https://customessays-writing.org"]custom essay service toronto[/url] buy cheap essays online

RhiamonsrPa September 29, 2022 9:01:06 PM

essay writer website [url="https://customessaywwriting.com"]service learning reflection essay[/url] essay writing service discount

CharosrPa September 30, 2022 3:19:13 PM

custom writing essay service [url="https://customessaywwriting.com"]essay writing services online[/url] essay writing service legit

CharosrPa September 30, 2022 3:53:21 PM

i need help writing a essay [url="https://customs-essays-writing.org"]college essay helper[/url] reviews of essay writing services

DronasrPa October 1, 2022 9:51:50 AM

professional essay writing help [url="https://customs-essays-writing.org"]help me write a compare and contrast essay[/url] essay writing website

DronasrPa October 1, 2022 10:30:33 AM

write my history essay for me [url="https://firstessayservice.net"]instant essay writer[/url] usa essay writing services

TwylasrPa October 2, 2022 5:23:45 AM

top rated essay writing websites [url="https://firstessayservice.net"]cheap essay writer[/url] college application essay service

TwylasrPa October 2, 2022 5:59:37 AM

law school essay review service [url="https://geniusessaywriters.net"]essay on helping others[/url] sat essay help

LeilahsrPa October 3, 2022 2:08:23 AM

the best essay writer [url="https://howtobuyanessay.com"]essay about military service[/url] top rated essay writing services

CthrinesrPa October 4, 2022 12:03:06 AM

custom made essays [url="https://howtobuyanessay.com"]top 10 essay writers[/url] customized essay

CthrinesrPa October 4, 2022 12:38:31 AM

custom essay writing services reviews [url="https://lawessayhelpinlondon.com"]writing essays services[/url] academic essay writing help

GinniesrPa October 9, 2022 11:56:42 AM

urgent custom essays [url="https://lawessayhelpinlondon.com"]customized essay[/url] best essay website

GinniesrPa October 9, 2022 12:32:57 PM

buy my essay [url="https://ukessayservice.net"]essay on community service[/url] essay introduction help

VivienesrPa October 11, 2022 6:17:31 PM

custom essay writing services reviews [url="https://ukessayservice.net"]premium essay writing service[/url] cheap essay writing service

VivienesrPa October 11, 2022 6:52:00 PM

write my essay reviews [url="https://writemyessaycheap24h.com"]write my admission essay[/url] sat essay help

EastersrPa October 13, 2022 9:16:02 AM

writing essays help [url="https://writemyessaycheap24h.com"]sat essay help[/url] the help by kathryn stockett essay

EastersrPa October 13, 2022 9:54:07 AM

help writing a argumentative essay [url="https://bestcampusessays.com"]buy essays for college[/url] help writing essays for scholarships

DorolisasrPa November 19, 2022 5:56:23 AM

best essay service [url="https://bestcampusessays.com"]high school essay help[/url] essay about military service

DorolisasrPa November 19, 2022 6:56:52 AM

essays on service [url="https://besteasyessays.org"]help on essay writing[/url] top 10 essay writers

MartysrPa November 20, 2022 12:54:46 PM

college essay writing service reviews [url="https://besteasyessays.org"]what are good essay writing services[/url] custom essays for sale

MartysrPa November 20, 2022 1:47:31 PM

best rated essay writing service [url="https://bestessayreviews.net"]write my essay[/url] college essay helper

MerolasrPa November 21, 2022 8:44:27 PM

college essay review services [url="https://bestsessays.org"]essay writers review[/url] essay service

CharitasrPa November 24, 2022 2:12:04 PM

essay writers service [url="https://buy-eessay-online.com"]order custom essay[/url] custom english essays

ChelsaesrPa November 27, 2022 7:49:32 AM

essays online to buy [url="https://buy-eessay-online.com"]essay writer program[/url] essay writing service canada

ChelsaesrPa November 27, 2022 8:55:55 AM

online essay writing services [url="https://buytopessays.com"]buy college essay[/url] buy essay

PennysrPa November 28, 2022 5:04:49 PM

write my essay custom writing [url="https://buytopessays.com"]college essay online help[/url] english essay writing help

PennysrPa November 28, 2022 6:05:26 PM

Leave a Comment