Check if .NETCore is installed using CustomAction with Wix
Asked Answered
L

3

5

I want to cancel the installation if the NetCore 3.1 (preview) is not installed

I create this CustomAction :

using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Win32;

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

            RegistryKey lKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost");

            var version = (string)lKey.GetValue("Version");

            session["DOTNETCORE31"] = version == "3.1.0-preview3.19553.2" ? "1" : "0";

            return ActionResult.Success;
        }
    }
}

Then in the WXS file :

<<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">

   <Product ...>

  (...)

    <Property Id="DOTNETCORE31">0</Property>

    <Condition Message="You must first install the .NET Core 3.1 Runtime">
      Installed OR DOTNETCORE31="1"
    </Condition>

    <InstallExecuteSequence>
      <Custom Action="Check.NetCore" Before="LaunchConditions">NOT Installed</Custom>
    </InstallExecuteSequence>

  </Product>

  <Fragment>
    <Binary Id="WixCA.dll" SourceFile="$(var.WixCustomAction.TargetDir)$(var.WixCustomAction.TargetName).CA.dll" />
    <CustomAction Id="Check.NetCore" BinaryKey="WixCA.dll" DllEntry="CheckDotNetCore31Installed" Execute="immediate"  />
  </Fragment>

And this is where I have a problem because I always get the warning message. An idea ? thanks

Lavinialavinie answered 19/11, 2019 at 8:57 Comment(4)
Please add the warning message.Coligny
for me it's the line "<Condition Message="You must first install the .NET Core 3.1 Runtime">". If you ask the question, it shows that I didn't understand anything :(Lavinialavinie
It would not be <![CDATA[Installed OR (DOTNETCORE31 = 1)]]> for testing value ?Wilburnwilburt
The syntax seems more correct but for this problem the result does not change :/Lavinialavinie
C
2

maybe it will be better to check .net core version using cmd with dotnet --version command, to avoid windows architecture dependencies

custom action sample for this case:

    [CustomAction]
    public static ActionResult CheckVersion(Session session)
    {
       var minVersion = new Version(3, 1, 1);
        var command = "/c dotnet --version";// /c is important here
        var output = string.Empty;
        using (var p = new Process())
        {
            p.StartInfo = new ProcessStartInfo()
            {
                FileName = "cmd.exe",
                Arguments = command,
                UseShellExecute = false,                    
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                CreateNoWindow = true,
            };
            p.Start();
            while (!p.StandardOutput.EndOfStream)
            {
                output += $"{p.StandardOutput.ReadLine()}{Environment.NewLine}";
            }
            p.WaitForExit();
            if (p.ExitCode != 0)
            {
                session["DOTNETCORE1"] = "0";
                return ActionResult.Success;
                //throw new Exception($"{p.ExitCode}:{ p.StandardError.ReadToEnd()}");
            }

            //you can implement here your own comparing algorithm
            //mine will not work with -perview string, but in this case you can just 
            //replace all alphabetical symbols with . using regex, for example
            var currentVersion = Version.Parse(output);
            session["DOTNETCORE1"] = (currentVersion < minVersion) ? "0" : "1";
            
            return ActionResult.Success;
         }
      

UPDATE: As Adam mentioned, I was wrong with that snippet - it works only for SDKs. Here's another one to get runtimes:

    static readonly List<string> runtimes = new List<string>()
    {
        "Microsoft.NETCore.App",//.NET Runtime
        "Microsoft.AspNetCore.App",//ASP.NET Core Runtime
        "Microsoft.WindowsDesktop.App",//.NET Desktop Runtime
    };

    [CustomAction]
    public static ActionResult CheckVersion(Session session)
    {
        var minVersion = new Version(3, 1, 1);
        var command = "/c dotnet --list-runtimes";// /c is important here
        var output = string.Empty;
        using (var p = new Process())
        {
            p.StartInfo = new ProcessStartInfo()
            {
                FileName = "cmd.exe",
                Arguments = command,
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                CreateNoWindow = true,
            };
            p.Start();
            while (!p.StandardOutput.EndOfStream)
            {
                output += $"{p.StandardOutput.ReadLine()}{Environment.NewLine}";
            }
            p.WaitForExit();
            if (p.ExitCode != 0)
            {
                session["DOTNETCORE1"] = "0";
                return ActionResult.Success;
                //throw new Exception($"{p.ExitCode}:{ p.StandardError.ReadToEnd()}");
            }
            session["DOTNETCORE1"] = (GetLatestVersionOfRuntime(runtimes[0], output) < minVersion) ? "0" : "1";
            return ActionResult.Success;
        }
    }

    private static Version GetLatestVersionOfRuntime(string runtime, string runtimesList)
    {
        var latestLine = runtimesList.Split(Environment.NewLine).ToList().Where(x => x.Contains(runtime)).OrderBy(x => x).LastOrDefault();
        if (latestLine != null)
        {
            Regex pattern = new Regex(@"\d+(\.\d+)+");
            Match m = pattern.Match(latestLine);
            string versionValue = m.Value;
            if (Version.TryParse(versionValue, out var version))
            {
                return version;
            }
        }
        return null;
    }
Carpophore answered 1/7, 2020 at 14:51 Comment(2)
In general I like this idea but dotnet --version isn't available for .NET Core runtimes, only SDKs. The runtime does support dotnet --list-runtimes but parsing is a bit tedious.Curvature
yes, you're absolutely right. that time I tested it only on developer's machine. updated post with parsing of dotnet --list-runtimesCarpophore
M
1

Debugging: Are you attaching the debugger to your custom action so you can see what is going on there? I bet it is not setting your property correctly. The custom action might not be running at all? Show a message box to smoke test that? More involved (attaching Visual Studio debugger):

LaunchCondition: In an MSI database launch conditions are represented by records in the LaunchCondition table. This table has two columns. The Condition column contains an expression which must evaluate to True for installation to continue:

MSI

Conclusion: So your condition does not evaluate to true properly. What is the actual value of DOTNETCORE31? I bet it is 0. Double check please. Easiest way is obviously to set it to 1 instead of 0 directly - and then compile again and test. Hard coding temporarily like this:

<Property Id="DOTNETCORE31">1</Property>

Links: Here are some previous answer on launch conditions and other topics:


WiX Custom Action: You have the basic markup for calling the custom action? Check the compiled MSI with Orca to see if there are entries in the Binary, CustomAction and InstallExecuteSequence and InstallUISequence tables. Some mock-up WiX markup (pillage gihub.com for samples?):

<Binary Id="CustomActions" SourceFile="C:\Test.CA.dll" />

<...>

<CustomAction Id="CustomAction1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>

<...>

<InstallUISequence>
  <Custom Action="CustomAction1" After="CostFinalize" />
</InstallUISequence>

<...>

<InstallExecuteSequence>
  <Custom Action="CustomAction1" After="CostFinalize" />
</InstallExecuteSequence>

GUI & Silent Install: Obviously you could also run the custom action from a dialog event - like a button click - but that would make it NOT run in silent mode. The GUI is skipped in silent mode so you need to run the custom action in the InstallExecuteSequence as well as the GUI.

Manipulate answered 19/11, 2019 at 12:28 Comment(5)
Thank you very much for your very informative message. It helped me a lot. I was able to debug my CustomAction and understood where the problem came from : it didn't read the 64bit registry !Lavinialavinie
Ah, the classic bitness problem. Always high on the list and I should have mentioned it. I am not sure this registry read is the best approach to detect .NET Core, but that is another story. What when the next version comes along? Might have a look later. There are built-in measures to detect the main .NET framework (old school .NET).Hotbox
I waiting for this github.com/wixtoolset/issues/issues/6099 but we'll have to wait for the.NETCore to come out of the previewLavinialavinie
Ah, shows how outdated I am. Thanks for the link! Maybe you can do a "higher than" check rather than just check for a specific version number? I haven't studied this much.Hotbox
Very good idea! As soon as a new preview is available I will check how the version number evolvesLavinialavinie
L
0

With the help of Stein Asmul, I was able to debug my CustomAction. Here is the code that works :

using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Win32;

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

            RegistryKey localMachine64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
            RegistryKey lKey = localMachine64.OpenSubKey(@"SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost\", false);

            var version = (string)lKey.GetValue("Version");

            session["DOTNETCORE1"] = version == "3.1.0-preview3.19553.2" ? "1" : "0";

            return ActionResult.Success;
        }
    }
}
Lavinialavinie answered 20/11, 2019 at 7:34 Comment(1)
When using SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost there is a problem; If the admin uninstalls .net core, the registry value remains and does not reflect what is actually installed.Monaural

© 2022 - 2024 — McMap. All rights reserved.