mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
fix.继续修复 .NET运行时问题
This commit is contained in:
@@ -26,6 +26,8 @@ internal sealed record DotNetRuntimeProbeOptions
|
|||||||
|
|
||||||
public string? ProgramFilesX86Path { get; init; }
|
public string? ProgramFilesX86Path { get; init; }
|
||||||
|
|
||||||
|
public string? LocalAppDataPath { get; init; }
|
||||||
|
|
||||||
public IReadOnlyList<string>? DotNetHostCandidates { get; init; }
|
public IReadOnlyList<string>? DotNetHostCandidates { get; init; }
|
||||||
|
|
||||||
public bool IncludeRegistry { get; init; } = true;
|
public bool IncludeRegistry { get; init; } = true;
|
||||||
@@ -63,6 +65,13 @@ internal sealed record DotNetRuntimeProbeResult(
|
|||||||
internal static class DotNetRuntimeProbe
|
internal static class DotNetRuntimeProbe
|
||||||
{
|
{
|
||||||
public const string RequiredSharedFrameworkName = "Microsoft.NETCore.App";
|
public const string RequiredSharedFrameworkName = "Microsoft.NETCore.App";
|
||||||
|
public const string WindowsDesktopSharedFrameworkName = "Microsoft.WindowsDesktop.App";
|
||||||
|
|
||||||
|
private static readonly string[] RequiredSharedFrameworkNames =
|
||||||
|
[
|
||||||
|
RequiredSharedFrameworkName,
|
||||||
|
WindowsDesktopSharedFrameworkName
|
||||||
|
];
|
||||||
|
|
||||||
public static DotNetRuntimeProbeResult Probe(DotNetRuntimeProbeOptions? options = null)
|
public static DotNetRuntimeProbeResult Probe(DotNetRuntimeProbeOptions? options = null)
|
||||||
{
|
{
|
||||||
@@ -71,10 +80,25 @@ internal static class DotNetRuntimeProbe
|
|||||||
var searchedPaths = new List<string>();
|
var searchedPaths = new List<string>();
|
||||||
var detected = new List<DotNetRuntimeInfo>();
|
var detected = new List<DotNetRuntimeInfo>();
|
||||||
var requiredMajor = options.RequiredMajorVersion;
|
var requiredMajor = options.RequiredMajorVersion;
|
||||||
var sharedFrameworkDirectory = GetSharedFrameworkDirectory(options, RequiredSharedFrameworkName);
|
|
||||||
searchedPaths.Add(sharedFrameworkDirectory);
|
|
||||||
|
|
||||||
AddDirectoryRuntimes(sharedFrameworkDirectory, RequiredSharedFrameworkName, "shared-framework-directory", detected);
|
var localAppDataRoot = GetLocalAppDataPath(options);
|
||||||
|
var perUserDotnetRoot = !string.IsNullOrWhiteSpace(localAppDataRoot)
|
||||||
|
? Path.Combine(localAppDataRoot, "dotnet")
|
||||||
|
: null;
|
||||||
|
|
||||||
|
foreach (var frameworkName in RequiredSharedFrameworkNames)
|
||||||
|
{
|
||||||
|
foreach (var basePath in EnumerateDotNetInstallRoots(options))
|
||||||
|
{
|
||||||
|
var sharedFrameworkDirectory = Path.Combine(basePath, "shared", frameworkName);
|
||||||
|
searchedPaths.Add(sharedFrameworkDirectory);
|
||||||
|
var isPerUser = perUserDotnetRoot is not null &&
|
||||||
|
string.Equals(basePath, perUserDotnetRoot, StringComparison.OrdinalIgnoreCase);
|
||||||
|
AddDirectoryRuntimes(sharedFrameworkDirectory, frameworkName,
|
||||||
|
isPerUser ? "shared-framework-directory-per-user" : "shared-framework-directory",
|
||||||
|
detected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string? dotNetHostPath = null;
|
string? dotNetHostPath = null;
|
||||||
foreach (var candidate in EnumerateDotNetHostCandidates(options))
|
foreach (var candidate in EnumerateDotNetHostCandidates(options))
|
||||||
@@ -88,12 +112,15 @@ internal static class DotNetRuntimeProbe
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows() && options.IncludeRegistry)
|
if (OperatingSystem.IsWindows() && options.IncludeRegistry)
|
||||||
{
|
{
|
||||||
AddRegistryRuntimes(options.Architecture, RequiredSharedFrameworkName, detected);
|
foreach (var frameworkName in RequiredSharedFrameworkNames)
|
||||||
|
{
|
||||||
|
AddRegistryRuntimes(options.Architecture, frameworkName, detected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.IncludeDotNetCli)
|
if (options.IncludeDotNetCli)
|
||||||
{
|
{
|
||||||
AddDotNetCliRuntimes(dotNetHostPath, RequiredSharedFrameworkName, detected);
|
AddDotNetCliRuntimes(dotNetHostPath, detected);
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAvailable = detected.Any(runtime =>
|
var isAvailable = detected.Any(runtime =>
|
||||||
@@ -162,13 +189,23 @@ internal static class DotNetRuntimeProbe
|
|||||||
!File.Exists(Path.Combine(directory, "System.Private.CoreLib.dll"));
|
!File.Exists(Path.Combine(directory, "System.Private.CoreLib.dll"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetSharedFrameworkDirectory(DotNetRuntimeProbeOptions options, string sharedFrameworkName)
|
private static IEnumerable<string> EnumerateDotNetInstallRoots(DotNetRuntimeProbeOptions options)
|
||||||
{
|
{
|
||||||
var root = options.Architecture == DotNetRuntimeArchitecture.X86
|
var programFilesRoot = options.Architecture == DotNetRuntimeArchitecture.X86
|
||||||
? GetProgramFilesX86Path(options)
|
? GetProgramFilesX86Path(options)
|
||||||
: GetProgramFilesPath(options);
|
: GetProgramFilesPath(options);
|
||||||
|
|
||||||
return Path.Combine(root, "dotnet", "shared", sharedFrameworkName);
|
yield return Path.Combine(programFilesRoot, "dotnet");
|
||||||
|
|
||||||
|
var localAppData = GetLocalAppDataPath(options);
|
||||||
|
if (!string.IsNullOrWhiteSpace(localAppData))
|
||||||
|
{
|
||||||
|
var perUserDotnet = Path.Combine(localAppData, "dotnet");
|
||||||
|
if (!string.Equals(perUserDotnet, Path.Combine(programFilesRoot, "dotnet"), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
yield return perUserDotnet;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> EnumerateDotNetHostCandidates(DotNetRuntimeProbeOptions options)
|
private static IEnumerable<string> EnumerateDotNetHostCandidates(DotNetRuntimeProbeOptions options)
|
||||||
@@ -186,11 +223,21 @@ internal static class DotNetRuntimeProbe
|
|||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var root = options.Architecture == DotNetRuntimeArchitecture.X86
|
var programFilesRoot = options.Architecture == DotNetRuntimeArchitecture.X86
|
||||||
? GetProgramFilesX86Path(options)
|
? GetProgramFilesX86Path(options)
|
||||||
: GetProgramFilesPath(options);
|
: GetProgramFilesPath(options);
|
||||||
|
|
||||||
yield return Path.Combine(root, "dotnet", OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet");
|
yield return Path.Combine(programFilesRoot, "dotnet", OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet");
|
||||||
|
|
||||||
|
var localAppData = GetLocalAppDataPath(options);
|
||||||
|
if (!string.IsNullOrWhiteSpace(localAppData))
|
||||||
|
{
|
||||||
|
var perUserHost = Path.Combine(localAppData, "dotnet", OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet");
|
||||||
|
if (!string.Equals(perUserHost, Path.Combine(programFilesRoot, "dotnet", OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
yield return perUserHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetProgramFilesPath(DotNetRuntimeProbeOptions options)
|
private static string GetProgramFilesPath(DotNetRuntimeProbeOptions options)
|
||||||
@@ -215,6 +262,16 @@ internal static class DotNetRuntimeProbe
|
|||||||
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetLocalAppDataPath(DotNetRuntimeProbeOptions options)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.LocalAppDataPath))
|
||||||
|
{
|
||||||
|
return Path.GetFullPath(options.LocalAppDataPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddDirectoryRuntimes(
|
private static void AddDirectoryRuntimes(
|
||||||
string sharedFrameworkDirectory,
|
string sharedFrameworkDirectory,
|
||||||
string sharedFrameworkName,
|
string sharedFrameworkName,
|
||||||
@@ -271,7 +328,6 @@ internal static class DotNetRuntimeProbe
|
|||||||
|
|
||||||
private static void AddDotNetCliRuntimes(
|
private static void AddDotNetCliRuntimes(
|
||||||
string? dotNetHostPath,
|
string? dotNetHostPath,
|
||||||
string sharedFrameworkName,
|
|
||||||
List<DotNetRuntimeInfo> detected)
|
List<DotNetRuntimeInfo> detected)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(dotNetHostPath) || !File.Exists(dotNetHostPath))
|
if (string.IsNullOrWhiteSpace(dotNetHostPath) || !File.Exists(dotNetHostPath))
|
||||||
@@ -300,7 +356,7 @@ internal static class DotNetRuntimeProbe
|
|||||||
{
|
{
|
||||||
var parsed = ParseListRuntimeLine(line);
|
var parsed = ParseListRuntimeLine(line);
|
||||||
if (parsed is not null &&
|
if (parsed is not null &&
|
||||||
string.Equals(parsed.Value.Name, sharedFrameworkName, StringComparison.OrdinalIgnoreCase))
|
RequiredSharedFrameworkNames.Contains(parsed.Value.Name, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
detected.Add(new DotNetRuntimeInfo(
|
detected.Add(new DotNetRuntimeInfo(
|
||||||
parsed.Value.Name,
|
parsed.Value.Name,
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ public sealed class DotNetRuntimeProbeTests : IDisposable
|
|||||||
private readonly string _root;
|
private readonly string _root;
|
||||||
private readonly string _programFiles;
|
private readonly string _programFiles;
|
||||||
private readonly string _programFilesX86;
|
private readonly string _programFilesX86;
|
||||||
|
private readonly string _localAppData;
|
||||||
|
|
||||||
public DotNetRuntimeProbeTests()
|
public DotNetRuntimeProbeTests()
|
||||||
{
|
{
|
||||||
_root = Path.Combine(Path.GetTempPath(), "LanMountainDesktop.DotNetRuntimeProbeTests", Guid.NewGuid().ToString("N"));
|
_root = Path.Combine(Path.GetTempPath(), "LanMountainDesktop.DotNetRuntimeProbeTests", Guid.NewGuid().ToString("N"));
|
||||||
_programFiles = Path.Combine(_root, "ProgramFiles");
|
_programFiles = Path.Combine(_root, "ProgramFiles");
|
||||||
_programFilesX86 = Path.Combine(_root, "ProgramFilesX86");
|
_programFilesX86 = Path.Combine(_root, "ProgramFilesX86");
|
||||||
|
_localAppData = Path.Combine(_root, "LocalAppData");
|
||||||
Directory.CreateDirectory(_programFiles);
|
Directory.CreateDirectory(_programFiles);
|
||||||
Directory.CreateDirectory(_programFilesX86);
|
Directory.CreateDirectory(_programFilesX86);
|
||||||
|
Directory.CreateDirectory(_localAppData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -61,6 +64,104 @@ public sealed class DotNetRuntimeProbeTests : IDisposable
|
|||||||
Assert.False(result.IsAvailable);
|
Assert.False(result.IsAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_DetectsPerUserRuntime()
|
||||||
|
{
|
||||||
|
CreateRuntime(_localAppData, "10.0.5", DotNetRuntimeProbe.RequiredSharedFrameworkName);
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(CreateOptions(DotNetRuntimeArchitecture.X64));
|
||||||
|
|
||||||
|
Assert.True(result.IsAvailable);
|
||||||
|
Assert.Contains(result.DetectedRuntimes, runtime =>
|
||||||
|
runtime.Version == "10.0.5" &&
|
||||||
|
runtime.Source == "shared-framework-directory-per-user");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_DetectsWindowsDesktopRuntime()
|
||||||
|
{
|
||||||
|
CreateRuntime(_programFiles, "10.0.5", DotNetRuntimeProbe.WindowsDesktopSharedFrameworkName);
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(CreateOptions(DotNetRuntimeArchitecture.X64));
|
||||||
|
|
||||||
|
Assert.False(result.IsAvailable);
|
||||||
|
Assert.Contains(result.DetectedRuntimes, runtime =>
|
||||||
|
runtime.Name == DotNetRuntimeProbe.WindowsDesktopSharedFrameworkName &&
|
||||||
|
runtime.Version == "10.0.5");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_DetectsPerUserWindowsDesktopRuntime()
|
||||||
|
{
|
||||||
|
CreateRuntime(_localAppData, "10.0.5", DotNetRuntimeProbe.WindowsDesktopSharedFrameworkName);
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(CreateOptions(DotNetRuntimeArchitecture.X64));
|
||||||
|
|
||||||
|
Assert.Contains(result.DetectedRuntimes, runtime =>
|
||||||
|
runtime.Name == DotNetRuntimeProbe.WindowsDesktopSharedFrameworkName &&
|
||||||
|
runtime.Version == "10.0.5" &&
|
||||||
|
runtime.Source == "shared-framework-directory-per-user");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_FindsDotNetHost_InPerUserPath()
|
||||||
|
{
|
||||||
|
var dotnetDir = Path.Combine(_localAppData, "dotnet");
|
||||||
|
Directory.CreateDirectory(dotnetDir);
|
||||||
|
File.WriteAllText(Path.Combine(dotnetDir, "dotnet.exe"), string.Empty);
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(new DotNetRuntimeProbeOptions
|
||||||
|
{
|
||||||
|
Architecture = DotNetRuntimeArchitecture.X64,
|
||||||
|
ProgramFilesPath = _programFiles,
|
||||||
|
ProgramFilesX86Path = _programFilesX86,
|
||||||
|
LocalAppDataPath = _localAppData,
|
||||||
|
IncludeRegistry = false,
|
||||||
|
IncludeDotNetCli = false
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.NotNull(result.DotNetHostPath);
|
||||||
|
Assert.Contains("LocalAppData", result.DotNetHostPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_PrefersProgramFilesHost_OverPerUserHost()
|
||||||
|
{
|
||||||
|
var systemDotnetDir = Path.Combine(_programFiles, "dotnet");
|
||||||
|
Directory.CreateDirectory(systemDotnetDir);
|
||||||
|
File.WriteAllText(Path.Combine(systemDotnetDir, "dotnet.exe"), string.Empty);
|
||||||
|
|
||||||
|
var perUserDotnetDir = Path.Combine(_localAppData, "dotnet");
|
||||||
|
Directory.CreateDirectory(perUserDotnetDir);
|
||||||
|
File.WriteAllText(Path.Combine(perUserDotnetDir, "dotnet.exe"), string.Empty);
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(new DotNetRuntimeProbeOptions
|
||||||
|
{
|
||||||
|
Architecture = DotNetRuntimeArchitecture.X64,
|
||||||
|
ProgramFilesPath = _programFiles,
|
||||||
|
ProgramFilesX86Path = _programFilesX86,
|
||||||
|
LocalAppDataPath = _localAppData,
|
||||||
|
IncludeRegistry = false,
|
||||||
|
IncludeDotNetCli = false
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.NotNull(result.DotNetHostPath);
|
||||||
|
Assert.Contains("ProgramFiles", result.DotNetHostPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Probe_CombinesSystemAndPerUserRuntimes()
|
||||||
|
{
|
||||||
|
CreateRuntime(_programFiles, "10.0.5");
|
||||||
|
CreateRuntime(_localAppData, "10.0.3");
|
||||||
|
|
||||||
|
var result = DotNetRuntimeProbe.Probe(CreateOptions(DotNetRuntimeArchitecture.X64));
|
||||||
|
|
||||||
|
Assert.True(result.IsAvailable);
|
||||||
|
Assert.Contains(result.DetectedRuntimes, runtime => runtime.Version == "10.0.5");
|
||||||
|
Assert.Contains(result.DetectedRuntimes, runtime => runtime.Version == "10.0.3");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ValidateDotNetRuntimePrerequisite_ReturnsStructuredFailure_WhenRuntimeIsMissing()
|
public void ValidateDotNetRuntimePrerequisite_ReturnsStructuredFailure_WhenRuntimeIsMissing()
|
||||||
{
|
{
|
||||||
@@ -109,19 +210,21 @@ public sealed class DotNetRuntimeProbeTests : IDisposable
|
|||||||
Architecture = architecture,
|
Architecture = architecture,
|
||||||
ProgramFilesPath = _programFiles,
|
ProgramFilesPath = _programFiles,
|
||||||
ProgramFilesX86Path = _programFilesX86,
|
ProgramFilesX86Path = _programFilesX86,
|
||||||
|
LocalAppDataPath = _localAppData,
|
||||||
DotNetHostCandidates = [],
|
DotNetHostCandidates = [],
|
||||||
IncludeRegistry = false,
|
IncludeRegistry = false,
|
||||||
IncludeDotNetCli = false
|
IncludeDotNetCli = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateRuntime(string programFilesRoot, string version)
|
private static void CreateRuntime(string root, string version, string? frameworkName = null)
|
||||||
{
|
{
|
||||||
|
frameworkName ??= DotNetRuntimeProbe.RequiredSharedFrameworkName;
|
||||||
Directory.CreateDirectory(Path.Combine(
|
Directory.CreateDirectory(Path.Combine(
|
||||||
programFilesRoot,
|
root,
|
||||||
"dotnet",
|
"dotnet",
|
||||||
"shared",
|
"shared",
|
||||||
DotNetRuntimeProbe.RequiredSharedFrameworkName,
|
frameworkName,
|
||||||
version));
|
version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -564,6 +564,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function GetPerUserDotNetDesktopRuntimePath(): String;
|
||||||
|
begin
|
||||||
|
Result := ExpandConstant('{localappdata}\dotnet\shared\Microsoft.WindowsDesktop.App');
|
||||||
|
end;
|
||||||
|
|
||||||
function GetDotNetRuntimeDownloadUrl(): String;
|
function GetDotNetRuntimeDownloadUrl(): String;
|
||||||
begin
|
begin
|
||||||
if '{#MyAppArch}' = 'x64' then
|
if '{#MyAppArch}' = 'x64' then
|
||||||
@@ -590,7 +595,8 @@ end;
|
|||||||
|
|
||||||
function IsDotNetDesktopRuntimeInstalled(): Boolean;
|
function IsDotNetDesktopRuntimeInstalled(): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := IsDotNet10RuntimePresent(GetTargetDotNetDesktopRuntimePath());
|
Result := IsDotNet10RuntimePresent(GetTargetDotNetDesktopRuntimePath()) or
|
||||||
|
IsDotNet10RuntimePresent(GetPerUserDotNetDesktopRuntimePath());
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DotNetDownloadProgress(
|
function DotNetDownloadProgress(
|
||||||
|
|||||||
Reference in New Issue
Block a user