WIX Install .NET and VSTO if Admin, fail early if non-Admin

1

I'm trying to deploy an Excel plug-in to the mass market. The plug-in requires VSTO runtime (vstor_redist.exe) and .NET 4.5 (for 64 bit OS) or .NET 4.0 (for 32 bit OS). I'm willing to assume that potential users already have at least .NET 2.0. Currently my setup consists of:

  1. Separate 32 bit and 64 bit WIX .msi installers which install the plug-in in per-user mode.
  2. Separate 32 bit and 64 bit bootstrapper bundles which wrap each .msi and install VSTO runtime and .NET as shown in the code below:

    <util:RegistrySearch Id="VSTORuntimeTest" Root="HKLM" Key="SOFTWARE\Microsoft\VSTO Runtime Setup\v4R\" Value="VSTORFeature_CLR40" Variable="VSTORFeature"/>
    <util:RegistrySearch Id="VSTORuntimeVersionV4R" Root="HKLM" Key="SOFTWARE\Microsoft\VSTO Runtime Setup\v4R\" Value="Version" Variable="VSTORVersionV4R"/>
    <util:RegistrySearch Id="VSTORuntimeVersionV4" Root="HKLM" Key="SOFTWARE\Microsoft\VSTO Runtime Setup\v4\" Value="Version" Variable="VSTORVersionV4"/>
    
    <Chain>
      <!-- Install .Net 4.0 or 4.5, depending on build -->
      <?ifdef x64?>
      <PackageGroupRef Id="NetFx45Web" />
      <?endif ?>
    
      <?ifdef x86?>
      <PackageGroupRef Id="NetFx40Web" />
      <?endif ?>
      <RollbackBoundary />
    
      <!-- Install VSTO runtime -->
      <ExePackage Id="VSTORuntime" SourceFile="..\resources\vstor_redist.exe" Permanent="yes" Vital="yes" Cache="no" Compressed="no"
                  DownloadUrl="http://go.microsoft.com/fwlink/?LinkId=158917"
                  PerMachine="yes"
                  InstallCommand="/q /norestart"
                  DetectCondition="VSTORFeature"
                  InstallCondition="NOT VSTORFeature OR NOT (VSTORVersionV4R >=v10.0.40303) OR NOT (VSTORVersionV4 >=v10.0.21022)" />
      <RollbackBoundary />
    
      <?ifdef x64?>
      <MsiPackage
        Id="nx_msi_package_version"
        SourceFile="..\My 64 bit Setup.msi"
        Compressed="yes"
        Vital="yes" />
      <?endif ?>
    
      <?ifdef x86?>
      <MsiPackage
        Id="nx_msi_package_version"
        SourceFile="..\My 32 bit Setup.msi"
        Compressed="yes"
        Vital="yes" />
      <?endif ?>
    
    </Chain>
    
  3. A .NET 2.0 wrapper which includes both the bootstrappers as embedded resources and deploys & runs the correct bootstrapper for the client OS. (Essentially Yochai Timmer's answer in Single MSI to install correct 32 or 64 bit c# application)

The whole thing feels like a turducken, but it works nicely for both silent upgrades and for fresh installs when the user has admin credentials. But if the user is not an admin and does not yet have both VSTO and the appropriate .NET installed, it fails ungracefully with the error: "0x8007051b - This security ID may not be assigned as the owner of the object" after a long download process.

What I would like to do is check in advance whether the user needs to ask an administrator to intervene and install VTSO and/or .NET for them, and display a message, ideally with website link(s), when this is the case. This check could be in the bootstrapper, or in my .NET 2.0 wrapper. Any recommendation on how best to do this?

Thanks, Eric

wix
vsto
burn
asked on Stack Overflow Feb 4, 2014 by Eric Hirst • edited May 23, 2017 by Community

1 Answer

0

The best way for me was to do the work in my 32-bit C# wrapper. As the code in my question indicates, Burn doesn't appear to offer any built-in package with VSTO support -- instead I'm just reading the registry manually and basing my logic on that. Burn does offer pre-built packages for .NET 4.0 and .NET 4.5, but I didn't see an easy way to support Fail Early with these.

My C# 2.0 wrapper now includes the following logic:

    private static bool NeedsMorePermissionToInstallPrerequisites(out string error)
    {
        error = string.Empty;

        // Is user an admin?  If so, we're OK
        if (PrivilegeTester.CanBeAdmin())
        {
            return false;
        }

        // Is .NET already installed?
        bool isDotNetInstalled;
        string dotNetVersion;
        if (Is64BitOperatingSystem())
        {
            isDotNetInstalled = PrereqSoftwareChecker.IsDotNet45Installed();
            dotNetVersion = "Microsoft .NET 4.5";
        }
        else
        {
            isDotNetInstalled = PrereqSoftwareChecker.IsDotNet40Installed();
            dotNetVersion = "Microsoft .NET 4.0";
        }

        // Is VTSO already installed?
        bool isVtsoInstalled = PrereqSoftwareChecker.IsVstoRuntimeInstalled();

        if (isVtsoInstalled && isDotNetInstalled)
        {
            return false;
        }

        // If we got this far, there's trouble.  Build the error.
        [...]

Here the CanBeAdmin() functionality is based on Calling IPrincipal.IsInRole on Windows 7, and the IsVstoRuntimeInstalled() looks at the exact same set of registry keys that I use in my VSTORuntimeTest, etc. logic in my original question. The .NET checks also look at the registry, using keys that are well documented in MSDN. For example:

    internal static bool IsDotNet40Installed()
    {
        try
        {
            Version dotnet4Version  = new Version(GetHKLMValue("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4.0\\Client", "Version").ToString());
            return dotnet4Version >= new Version("4.0.0.0");
        }
        catch
        {
            return false;
        }
    }

    private static object GetHKLMValue(string key, string valueName)
    {
        return Registry.GetValue("HKEY_LOCAL_MACHINE\\" + key, valueName, null);
    }

It would be nice if Burn could handle something like this better, but it might be complicated, since simply providing a Fail Early .NET package and a separate Fail Early VSTO package would not be enough to build a full list of packages that would need to be installed independently by an administrator.

Using C# and .NET 2.0 in bootstrapper-wrapper code is obviously a risk here, but I'm guessing that there aren't too many old XP boxes out in the wild that have Office and don't have at least .NET 2.0.

answered on Stack Overflow Feb 7, 2014 by Eric Hirst • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0