2
votes

I have a .NET project which has 2 components which communicate over MSMQ. I'm building my installer using Wix because Microsoft has inexplicably ceased support for installers in Visual Studio 2012. I'm quite happy with the process of creating an instance of MSMQ in a Wix Installer, and I'm quite happy with the process of detecting whether MSMQ is installed on the computer (by trying to load Mqrt.dll).

Does anyone know how to use Wix to install the MSMQ Windows System component itself? Is there a way to get Wix to instruct Windows to install a system component?

2

2 Answers

4
votes

It took a long time, but finally I found the elegant way to do this.

1) In Visual Studio create a new WiX C# Custom Action Project

2) Paste the following over your CustomAction.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

namespace InstallMsmq
{
    public class CustomActions
    {
        [CustomAction]
        public static ActionResult CustomAction1(Session session)
        {
            session.Log("Begin CustomAction1");

            return ActionResult.Success;
        }

        [DllImport("kernel32")]
        static extern IntPtr LoadLibrary(string lpFileName);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeLibrary(IntPtr hModule);

        [CustomAction]
        public static ActionResult InstallMsmq(Session session)
        {

            ActionResult result = ActionResult.Failure;
            session.Log("Detecting MSMQ");
            bool loaded;
            CreateConfigFile();
            CreateWindows8InstallerScript();

            try
            {
                IntPtr handle = LoadLibrary("Mqrt.dll");
                if (handle == IntPtr.Zero || handle.ToInt32() == 0)
                {
                    loaded = false;
                }
                else
                {
                    loaded = true;
                    session.Log("MSMQ is already installed");
                    result = ActionResult.Success;
                    FreeLibrary(handle);
                }
            }
            catch
            {
                loaded = false;
            }

            if (!loaded)
            {
                session.Log("Installing MSMQ");
                try
                {

                    Version win8version = new Version(6, 2, 9200, 0);

                    if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= win8version)//Windows 8 or higher
                    {
                        // its win8 or higher.

                        session.Log("Windows 8 or Server 2012 detected");

                        using (Process p = new Process())
                        {
                            session.Log("Installing MSMQ Server");
                            ProcessStartInfo containerStart = new ProcessStartInfo("MSMQWindows8.bat");
                            containerStart.Verb = "runas";
                            p.StartInfo = containerStart;                            
                            bool success = p.Start();
                            p.WaitForExit();
                        }
                    }
                    else if (Environment.OSVersion.Version.Major < 6) // Windows XP or earlier
                    {
                        session.Log("Windows XP or earlier detected");
                        string fileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "MSMQAnswer.ans");
                        using (System.IO.StreamWriter writer = new System.IO.StreamWriter(fileName))
                        {
                            writer.WriteLine("[Version]");
                            writer.WriteLine("Signature = \"$Windows NT$\"");
                            writer.WriteLine();
                            writer.WriteLine("[Global]");
                            writer.WriteLine("FreshMode = Custom");
                            writer.WriteLine("MaintenanceMode = RemoveAll");
                            writer.WriteLine("UpgradeMode = UpgradeOnly");
                            writer.WriteLine();
                            writer.WriteLine("[Components]");
                            writer.WriteLine("msmq_Core = ON");
                            writer.WriteLine("msmq_LocalStorage = ON");
                        }

                        using (Process p = new Process())
                        {
                            session.Log("Installing MSMQ Container");
                            ProcessStartInfo start = new ProcessStartInfo("sysocmgr.exe", "/i:sysoc.inf /u:\"" + fileName + "\"");
                            p.StartInfo = start;
                            p.Start();
                            p.WaitForExit();
                        }
                    }
                    else if (Environment.OSVersion.Version.Major < 8) // Vista or later
                    {
                        session.Log("Windows Vista or Windows 7 detected");
                        using (Process p = new Process())
                        {
                            session.Log("Installing MSMQ Container");
                            ProcessStartInfo containerStart = new ProcessStartInfo("ocsetup.exe", "MSMQ-Container /unattendFile:MSMQ.xml");
                            containerStart.Verb = "runas";
                            p.StartInfo = containerStart;
                            p.Start();
                            p.WaitForExit();
                        }
                        using (Process p = new Process())
                        {
                            session.Log("Installing MSMQ Server");
                            ProcessStartInfo serverStart = new ProcessStartInfo("ocsetup.exe", "MSMQ-Server /unattendFile:MSMQ.xml");
                            serverStart.Verb = "runas";
                            p.StartInfo = serverStart;
                            p.Start();
                            p.WaitForExit();
                        }
                    }
                    session.Log("Installation of MSMQ Completed succesfully");
                    result = ActionResult.Success;
                }
                catch (Exception ex)
                {
                    session.Log("Installation of MSMQ failed due to " + ex.Message);
                }
            }
            return result;
        }

        private static void CreateWindows8InstallerScript()
        {
            StreamWriter configFile = new StreamWriter("MSMQWindows8.bat");
            configFile.WriteLine("%WINDIR%\\SysNative\\dism.exe /online /enable-feature /all /featurename:MSMQ-Server");
            configFile.Close();
        }
        private static void CreateConfigFile()
        {
            StreamWriter configFile = new StreamWriter("MSMQ.xml");
            configFile.WriteLine("<?xml version=\"1.0\"?>");
            configFile.WriteLine("");
            configFile.WriteLine("<unattend>");
            configFile.WriteLine("");
            configFile.WriteLine("  <servicing>");
            configFile.WriteLine("");
            configFile.WriteLine("    <package action=\"configure\">");
            configFile.WriteLine("");
            configFile.WriteLine("<assemblyIdentity name=\"Microsoft-Windows-Foundation-Package\" version=\"6.0.6000.16386\" language=\"neutral\" processorArchitecture=\"AMD64\" publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("      <selection name=\"MSMQ-Container\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-Server\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-Triggers\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-DCOMProxy\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-Multicast\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-ADIntegration\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("<selection name=\"MSMQ-HTTP\" state=\"true\"/>");
            configFile.WriteLine("");
            configFile.WriteLine("    </package>");
            configFile.WriteLine("");
            configFile.WriteLine("  </servicing>");
            configFile.WriteLine("");
            configFile.WriteLine("</unattend>");
            configFile.Close();
        }
    }
}

3) Compile the custom action

4) Add the following to your WiX setup.wxs file:

<Binary SourceFile="[path to custom action project]\bin\[Debug or Release]\[custom action project name].CA.dll" Id="[custom action project name]step" />
<CustomAction Id="[custom action project name]CustomAction" BinaryKey="[custom action project name]step" DllEntry="[custom action project name]" Execute="deferred" Impersonate="no"/>
<InstallExecuteSequence>
  <Custom Action="[custom action project name]CustomAction" Before="InstallFiles"/>
</InstallExecuteSequence> 

5) The custom action can be configured to run at any one of a number of points during the installation process (detailed here: http://wixtoolset.org/documentation/manual/v3/xsd/wix/installexecutesequence.html). The above example runs the custom action before installing the files detailed in the .wxs file.

6) Test - If all goes well you should have a setup file which installs msmq as part of the installation sequence!

-1
votes

When your product depends on additional third-party prerequisites such as MSMQ, .NET Framework, SQL Server etc., you need to use a bootstrapper program to run a chain of installs: first the needed prereqs one-by-one, then the main product install.

In WiX, these chains are called bundles, and are created/run using the burn bootstrapper.