Installing vcredist_x86.exe or vcredist_x64.exe

Aug 11, 2015 at 6:26 PM
I have a WPF external UI. What is the best way to install the C++ runtime libs? My preference is to silently run the executable version of the installer. An example would be much appreciated.

Thanks in advance.
Aug 12, 2015 at 1:15 AM
First you will need to embed your vcredist_x*.exe with Binary. After that you need to setup managed custom action and call Process.Start(...) from it.

Have a look at <Wix#>\Samples\Bootstrapper\Simplified Bootstrapper. It actually implements exactly your scenario.
Aug 13, 2015 at 8:27 PM
Edited Aug 13, 2015 at 8:29 PM
Your suggested approach never fired the custom action because I'm using the WPF external UI approach. This turned out to be more involved.

Below are the relevant code snippets. On Windows 7, I'm getting this error from the process that is launched to install the CRT: ERROR_INSTALL_ALREADY_RUNNING

I'm not convinced my use of Sequence.InstallExecuteSequence and Step.CostFinalize is correct. I want the CRT to be installed before everything else but execute as part of the installation process. Because it's an external UI implementation, how do you prevent two instances of the installer running simultaneously as is reported under Windows 7? I guess this is due to the fact that the MSI is running when the secondary install process is launched?

Curiously, everything works as expected on Windows 8/10 (very strange). Any ideas?

// ...
    Project project = new Project(PRODUCT_NAME,
        rootDir,
        new Binary(new Id("vcredist2012"), extrasDirectory.FullName + @"\x86\2012vcredist_x86.exe"),
        new Binary(new Id("vcredist2013"), extrasDirectory.FullName + @"\x86\2013vcredist_x86.exe"),
        new ManagedAction(@"Install2012CRTAction", Return.check, When.Before, Step.CostFinalize, Condition.NOT_Installed, Sequence.InstallExecuteSequence),
        new ManagedAction(@"Install2013CRTAction", Return.check, When.Before, Step.CostFinalize, Condition.NOT_Installed, Sequence.InstallExecuteSequence),
        //new ManagedAction(@"InstallCRTAction", Return.check, When.Before, Step.InstallExecute, Condition.NOT_Installed, Sequence.InstallExecuteSequence),
        new LaunchCondition("CUSTOM_UI=\"true\" OR REMOVE=\"ALL\"", "Please run " + PRODUCT_NAME + ".Installer.exe instead."))
// ...
[CustomAction]
public static ActionResult Install2012CRTAction(Session session)
{
    session.Log(string.Format("----- Custom Action CRT 2012 -----\r\n"));
    return InstallCRT(session, "vcredist2012");
}

[CustomAction]
public static ActionResult Install2013CRTAction(Session session)
{
    session.Log(string.Format("----- Custom Action CRT 2013 -----\r\n"));
    return InstallCRT(session, "vcredist2013");
}

static ActionResult InstallCRT(Session session, string crtName)
{
    string CRTExeId;
    bool hasCRT = false;

    CRTExeId = crtName.Expand();

    if (crtName.EndsWith("2012"))
        hasCRT = IsVS2012CRTInstalled();
    else if (crtName.EndsWith("2013"))
        hasCRT = IsVS2013CRTInstalled();

    if (!hasCRT)
    {
        string CRTExeFile = io.Path.ChangeExtension(io.Path.GetTempFileName(), ".exe");
        session.SaveBinary(CRTExeId, CRTExeFile);

        ProcessStartInfo info = new ProcessStartInfo();
        info.FileName = CRTExeFile;
        info.Arguments = string.Format("/install /quiet /norestart /log %TEMP%\\{0}.log", crtName);

        session.Log(string.Format("Custom Action Launch: {0} {1}\r\n", info.FileName, info.Arguments));

        // Launch CRT installer.
        Process process = Process.Start(info);
        process.WaitForExit();

        if (process.ExitCode == -2147023278) // Check if ERROR_INSTALL_ALREADY_RUNNING == -2147023278
            session.Log("Custom Action Error Code: ERROR_INSTALL_ALREADY_RUNNING\r\n");

        session.Log(string.Format("Custom Action Exit Code: {0}\r\n", process.ExitCode));

        if (crtName.EndsWith("2012"))
            hasCRT = IsVS2012CRTInstalled();
        else if (crtName.EndsWith("2013"))
            hasCRT = IsVS2013CRTInstalled();

        if (!hasCRT) // CRT installation might fail.
            return ActionResult.Failure;
    }

    return ActionResult.Success;
}

static bool IsVS2012CRTInstalled()
{
    using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DevDiv\vc\Servicing\11.0\RuntimeMinimum"))
    {
        if (key != null)
        {
            var dw = key.GetValue("Install");
            return dw.ToString() == "1";
        } else
            return false;
    }
}

static bool IsVS2013CRTInstalled()
{
    using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DevDiv\vc\Servicing\12.0\RuntimeMinimum"))
    {
        if (key != null)
        {
            var dw = key.GetValue("Install");
            return dw.ToString() == "1";
        } else
            return false;
    }
}
// ...
Aug 14, 2015 at 1:51 AM
MSI installations are atomic. You cannot have two installations active at the time. It is the model enforced by MSI runtime.

You cannot have two installations running at the same time in the active installation phase. However you can have two installations running at the same time in the collecting user input (UI) phase. The active installation phase starts when you press that "install" button on UI.

This is why I directed you towards the sample. It triggers the secondary installation before its own while it's interacting with the user. It does it at that 'pre-install' stage. As you correctly noted, because you are using an external UI this stage is 'taken over' by your UI and it is the reason why the custom action is not fired. Thus you need to duplicate the custom action routine in your UI so it can be executed in both cases "setup with UI" and "silent setup".

However it seems to me that the whole approach becoming is less suitable after your extra (stronger) requirement: "I want the CRT to be installed before everything else but execute as part of the installation process.". You may benefit from the bootstrapper. Have a look at the included samples. I think you will be mostly interested in the WixBootsrtapper_NoUI sample, which installs prerequisite while relying on the primary MSI setup UI. There is also a sample of the WPF boostrapper UI (WixBootsrtapper_UI)
Sep 16, 2015 at 4:47 PM
This is an old question, but the answer may be useful to others.
Leaving apart the bootstrapper problem if you need to redistribute C++ dependencies (MFC, CRT, ATL...) look into "Program Files\Common Files\Merge Modules".
You will find a lot of msm files which you can add to the WixSharp script using the Merge element. This is for old version of Visual Studio; from version 2010 you can redistribute all the dll dependencies simply by copying the needed files in the application directory at installation time: you will find all the files needed in "Visual Studio Directory\VC\Redist"