4
votes

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

3
Please add the warning message. - Mirko Ebert
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 :( - Toss Net
It would not be <![CDATA[Installed OR (DOTNETCORE31 = 1)]]> for testing value ? - Xarkam
The syntax seems more correct but for this problem the result does not change :/ - Toss Net

3 Answers

1
votes

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.

1
votes

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;
    }
0
votes

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;
        }
    }
}