Architecture independent installer

WiX Windows InstallerThis is a continuation of my series on WiX Windows Installers. In this part we talk about making the installer architecture independent.

To be clear: MSI-installers do not support both 32 and 64 bit platforms, but are built for one or the other.

To get around this you can create a “bootstrapper”.

boot·strap /ˈbutˌstræp/ noun, adjective,verb, -strapped, -strap·ping.
4. self-generating or self-sustaining: a bootstrap process.

The bootstrapper is simply an .exe file compiled for any architecture (supports both 32 and 64 bit) which have the 32 bit and 64 bit .msi files embedded as a resource.

In Visual Studio, add a new resource file and add your two .msi files. You may refer to this resource from your C# and extract to a file using the File.WriteAllBytes method. Use this code to extract the .msi file matching the current architecture and initiating the .msi file:

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Bootstrapper {
    class Program {
        static void Main(string[] args) {

            if (Is64Bit()) {
                File.WriteAllBytes("MyInstaller.msi", ResourceFile.MyInstaller_x64_v1_2);
            }
            else {
                File.WriteAllBytes("MyInstaller.msi", ResourceFile.MyInstaller_x86_v1_2);
            }

            Process p = new Process();
            p.StartInfo.FileName = "msiexec.exe";
            p.StartInfo.Arguments = "/i MyInstaller.msi;
            p.Start();

        }

        [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);

        static bool Is64Bit() {
            return (IntPtr.Size == 8 || (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor()));
        }

        static bool Is32BitProcessOn64BitProcessor() {
            bool retVal;
            IsWow64Process(Process.GetCurrentProcess().Handle, out retVal);
            return retVal;
        }
    }
}

To make it a bit more interesting we can sneak in a parameter through the filename of the bootstrapper. This way you can pass user-specific information into the installer later on with a custom action or a script. Read more about this in my previous post. Note the msiexec argument “CUSTOMID”. Parameters like these must be upper case for WiX to put them in the public scope.

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Bootstrapper {
    class Program {
        static void Main(string[] args) {

            string id = Process.GetCurrentProcess().ProcessName;

            if (Is64Bit()) {
                File.WriteAllBytes("MyInstaller.msi", ResourceFile.MyInstaller_x64_v1_2);
            }
            else {
                File.WriteAllBytes("MyInstaller.msi", ResourceFile.MyInstaller_x86_v1_2);
            }

            Process p = new Process();
            p.StartInfo.FileName = "msiexec.exe";
            p.StartInfo.Arguments = "/i MyInstaller.msi CUSTOMID=" + id;
            p.Start();

        }

        [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);

        static bool Is64Bit() {
            return (IntPtr.Size == 8 || (IntPtr.Size == 4 && Is32BitProcessOn64BitProcessor()));
        }

        static bool Is32BitProcessOn64BitProcessor() {
            bool retVal;
            IsWow64Process(Process.GetCurrentProcess().Handle, out retVal);
            return retVal;
        }
    }
}
  1. No comments yet.

  1. No trackbacks yet.