- Using terminal command
- 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);
}
- 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