Skip to content

Instantly share code, notes, and snippets.

@VAllens
Last active June 13, 2024 01:12
Show Gist options
  • Save VAllens/8579ab5a0ef26aef70a5875f54745170 to your computer and use it in GitHub Desktop.
Save VAllens/8579ab5a0ef26aef70a5875f54745170 to your computer and use it in GitHub Desktop.
Three ways to get a list of installed .NET runtimes on your local computer/server
  1. Using terminal command
dotnet --list-runtimes
  1. Using C#/CSharp code
    using CliWrap;
    using CliWrap.EventStream;
    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Threading;
    
    // License: Do whatever you want with this. This is what my project uses,
    // I make no guarantees it works or will work in the future
    // THIS IS ONLY FOR .NET CORE DETECTION (no .NET framework!)
    // Requires CliWrap https://github.com/Tyrrrz/CliWrap
    
    namespace DotnetHelper
    {
        /// <summary>
        /// Class that can determine if a version of .NET Core is installed
        /// </summary>
        public class DotNetRuntimeVersionDetector
        {
            /// <summary>
            /// This is very windows specific
            /// </summary>
            /// <returns>List of versions matching the specified version</returns>
            public static async IAsyncEnumerable<ProductInfo> GetInstalledRuntimesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
            {
                // No validation. Make sure exit code is checked in the calling process.
                Command cmd = Cli.Wrap("dotnet.exe").WithArguments("--list-runtimes").WithValidation(CommandResultValidation.None);
                await foreach(CommandEvent cmdEvent in cmd.ListenAsync(cancellationToken))
                {
                    switch(cmdEvent)
                    {
                        case StartedCommandEvent started:
                            break;
                        case StandardOutputCommandEvent stdOut:
                            if(string.IsNullOrWhiteSpace(stdOut.Text))
                            {
                                continue;
                            }
    
                            if(stdOut.Text.StartsWith("Microsoft.NETCore.App")
                               || stdOut.Text.StartsWith("Microsoft.WindowsDesktop.App")
                               || stdOut.Text.StartsWith("Microsoft.AspNetCore.App")
                               || stdOut.Text.StartsWith("Microsoft.AspNetCore.All"))
                            {
                                yield return Parse(stdOut.Text);
                            }
                            break;
                        case StandardErrorCommandEvent stdErr:
                            break;
                        case ExitedCommandEvent exited:
                            break;
                    }
                }
            }
    
            private static ProductInfo Parse(ReadOnlySpan<char> stdOutText)
            {
                int firstIndex = stdOutText.IndexOf(' ');
                string productName = stdOutText.Slice(0, firstIndex).ToString();
                ReadOnlySpan<char> subText = stdOutText.Slice(firstIndex + 1, stdOutText.Length - firstIndex - 2);
                int secondIndex = subText.IndexOf(' ');
                ReadOnlySpan<char> version = subText.Slice(0, secondIndex);
                ReadOnlySpan<char> path = subText.Slice(secondIndex + 2);
                return new ProductInfo(productName, Version.Parse(version), string.Concat(path, "\\", version));
            }
    
            private static Version ParseVersion(string stdOutText)
            {
                // 0 = SDK name, 1 = version, 2+ = path parts
                string[] split = stdOutText.Split(' ');
                return Version.Parse(split[1]);
            }
        }
    
        /// <summary>
        /// .NET Product information
        /// </summary>
        /// <param name="ProductName">Product name</param>
        /// <param name="Version">Product version</param>
        /// <param name="InstalledPath">Installed path</param>
        public record struct ProductInfo(string ProductName, Version Version, string InstalledPath);
    }
  1. Using PowerShell code
class ProductInfo {
	[string]$ProductName
	[System.Version]$Version
	[string]$InstalledPath
	
	ProductInfo()
	{
		
	}
	
	ProductInfo([string]$productName, [System.Version]$version, [string]$installedPath)
	{
		$this.ProductName = $productName
		$this.Version = $version
		$this.Args = $installedPath
	}
}

function Get-InstalledDotNetRuntimes
{
	[OutputType([ProductInfo[]])]
	param ()
	
	# Execute `dotnet --list-runtimes` command.
	$runtimes = Invoke-Expression "dotnet --list-runtimes"
	
	# Create an empty array to store the Product informations.
	# $productInfos = New-Object System.Collections.Generic.List[ProductInfo]
	[ProductInfo[]]$productInfos = @()
	
	# Foreach line of output
	$runtimes -split "`n" | ForEach-Object {
		# Skip empty line
		if (![string]::IsNullOrWhiteSpace($_))
		{
			# Parse product name, version and installation path
			$parts = $_ -split " ", 3
			$productName = $parts[0]
			$version = [System.Version]::Parse($parts[1])
			$installedPath = $parts[2].TrimStart('[').TrimEnd(']') # 去掉前后的[]字符
			
			# Create a new ProductInfo object.
			$productInfo = New-Object ProductInfo -Property @{
				ProductName   = $productName
				Version	      = $version
				InstalledPath = $installedPath
			}
			
			# Add the ProductInfo object to the array
			$productInfos += $productInfo
		}
	}
	
	# Output the ProductInfo list
	return $productInfos
}

# Execute function
Get-InstalledDotNetRuntimes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment