Files
LanMountainDesktop/docs/auto_commit_md/20250525_12f0caa.md

408 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Git 提交分析报告
**提交哈希**: 12f0caafc735aae8dc9c8d19f2c0829288106280
**提交时间**: 2026-05-25 01:24:18 +0800
**作者**: lincube \<lincube3@hotmail.com\>
**提交信息**: fix.继续修复 .NET运行时问题
---
## 变更统计
- **修改文件数**: 3
- **新增行数**: 181
- **删除行数**: 16
- **净变更行数**: +165
### 变更文件
| 文件 | 变更类型 | 变更行数 |
|------|---------|---------|
| LanMountainDesktop.Launcher/Services/DotNetRuntimeProbe.cs | 核心修复 | +80 / -15 |
| LanMountainDesktop.Tests/DotNetRuntimeProbeTests.cs | 新增测试 | +109 |
| LanMountainDesktop/installer/LanMountainDesktop.iss | 增强检测 | +8 |
---
## 详细变更分析
### 1. LanMountainDesktop.Launcher/Services/DotNetRuntimeProbe.cs
**核心问题修复**: 支持按用户安装的 .NET 运行时检测
#### 变更 1: 扩展选项配置
```csharp
public record DotNetRuntimeProbeOptions
{
// ... existing properties ...
+ public string? LocalAppDataPath { get; init; }
}
```
- **新增**: `LocalAppDataPath` 配置项
- **用途**: 支持检测 `%LOCALAPPDATA%\dotnet` 目录下的运行时
#### 变更 2: 定义必需的共享框架
```csharp
public const string RequiredSharedFrameworkName = "Microsoft.NETCore.App";
+ public const string WindowsDesktopSharedFrameworkName = "Microsoft.WindowsDesktop.App";
+
+ private static readonly string[] RequiredSharedFrameworkNames =
+ [
+ RequiredSharedFrameworkName,
+ WindowsDesktopSharedFrameworkName
+ ];
```
- **新增**: Windows Desktop 运行时框架名称常量
- **变更**: 将单一框架改为框架列表,支持多框架检测
#### 变更 3: 核心检测逻辑重构
```csharp
public static DotNetRuntimeProbeResult Probe(DotNetRuntimeProbeOptions? options = null)
{
// ... 初始化代码 ...
+ 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);
+ }
+ }
}
```
- **核心改进**:
- 支持扫描多个 .NET 安装位置
- 区分系统安装和按用户安装
- 检测多个必需的共享框架
#### 变更 4: 新增安装根目录枚举
```csharp
+ private static IEnumerable<string> EnumerateDotNetInstallRoots(DotNetRuntimeProbeOptions options)
+ {
+ var programFilesRoot = options.Architecture == DotNetRuntimeArchitecture.X86
+ ? GetProgramFilesX86Path(options)
+ : GetProgramFilesPath(options);
+
+ 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;
+ }
+ }
+ }
```
- **功能**: 枚举所有可能的 .NET 安装根目录
- **包括**:
- Program Files 下的系统安装
- LocalAppData 下的按用户安装
#### 变更 5: 增强 dotnet host 路径搜索
```csharp
+ 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;
+ }
+ }
```
- **改进**: 在按用户路径中搜索 dotnet host 可执行文件
#### 变更 6: 添加 LocalAppData 路径获取
```csharp
+ private static string GetLocalAppDataPath(DotNetRuntimeProbeOptions options)
+ {
+ if (!string.IsNullOrWhiteSpace(options.LocalAppDataPath))
+ {
+ return Path.GetFullPath(options.LocalAppDataPath);
+ }
+
+ return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ }
```
- **功能**: 获取 LocalAppData 路径,支持自定义配置
#### 变更 7: 增强 dotnet CLI 检测
```csharp
- private static void AddDotNetCliRuntimes(
- string? dotNetHostPath,
- string sharedFrameworkName,
- List<DotNetRuntimeInfo> detected)
+ private static void AddDotNetCliRuntimes(
+ string? dotNetHostPath,
+ List<DotNetRuntimeInfo> detected)
```
- **变更**: 移除 `sharedFrameworkName` 参数
- **改进**: 使用 `RequiredSharedFrameworkNames` 列表,支持多框架检测
---
### 2. LanMountainDesktop.Tests/DotNetRuntimeProbeTests.cs
**新增单元测试**: 6 个全面的测试用例
#### 测试 1: 按用户运行时检测
```csharp
[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");
}
```
#### 测试 2: Windows Desktop 运行时检测
```csharp
[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");
}
```
#### 测试 3: 按用户 Windows Desktop 运行时检测
```csharp
[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");
}
```
#### 测试 4: 在按用户路径中查找 dotnet host
```csharp
[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
{
// ... 配置 ...
LocalAppDataPath = _localAppData,
IncludeRegistry = false,
IncludeDotNetCli = false
});
Assert.NotNull(result.DotNetHostPath);
Assert.Contains("LocalAppData", result.DotNetHostPath);
}
```
#### 测试 5: 优先使用 Program Files host
```csharp
[Fact]
public void Probe_PrefersProgramFilesHost_OverPerUserHost()
{
// 创建两个 dotnet.exe一个在 Program Files一个在 LocalAppData
var result = DotNetRuntimeProbe.Probe(/* ... */);
Assert.NotNull(result.DotNetHostPath);
Assert.Contains("ProgramFiles", result.DotNetHostPath);
}
```
#### 测试 6: 合并系统和按用户运行时
```csharp
[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");
}
```
**测试覆盖评估**:
- ✅ 按时用户安装检测
- ✅ Windows Desktop 运行时检测
- ✅ 混合安装场景
- ✅ host 路径优先级
- ⚠️ 建议:添加跨架构检测测试
---
### 3. LanMountainDesktop/installer/LanMountainDesktop.iss
**增强安装程序检测逻辑**:
#### 新增函数
```pascal
function GetPerUserDotNetDesktopRuntimePath(): String;
begin
Result := ExpandConstant('{localappdata}\dotnet\shared\Microsoft.WindowsDesktop.App');
end;
```
#### 增强检测函数
```pascal
function IsDotNetDesktopRuntimeInstalled(): Boolean;
begin
- Result := IsDotNet10RuntimePresent(GetTargetDotNetDesktopRuntimePath());
+ Result := IsDotNet10RuntimePresent(GetTargetDotNetDesktopRuntimePath()) or
+ IsDotNet10RuntimePresent(GetPerUserDotNetDesktopRuntimePath());
end;
```
**变更说明**:
- 支持检测按用户安装的 .NET Desktop 运行时
- 允许在缺少系统安装但有按用户安装时继续安装
---
## 问题分析与解决方案
### 原始问题
**问题**: 只能检测系统级别的 .NET 运行时安装
**问题**: 无法识别用户通过 Visual Studio 或 winget 安装的 .NET 运行时
**问题**: 可能导致安装程序要求用户重新安装 .NET 运行时,即使已存在按用户安装
### 解决方案
**扩展搜索路径**:
- Program Files (系统级别)
- %LOCALAPPDATA% (用户级别)
**支持多框架检测**:
- Microsoft.NETCore.App
- Microsoft.WindowsDesktop.App
**智能合并**:
- 合并系统和用户安装的运行时
- 优先使用系统级别的 dotnet host
---
## 代码审查要点
### 潜在问题
1. **性能考虑**:
- ✅ 良好:使用 `yield return` 延迟枚举,避免不必要的文件系统访问
- ⚠️ 低风险:路径比较使用 `OrdinalIgnoreCase`,性能影响可接受
2. **路径安全性**:
- ✅ 良好:使用 `Path.GetFullPath()` 规范化路径
- ✅ 良好:避免路径注入攻击
3. **错误处理**:
- ✅ 良好:`string.IsNullOrWhiteSpace()` 检查空值
- ✅ 良好:可选的配置参数,提供默认值
4. **测试覆盖**:
- ✅ 优秀6 个新的测试用例覆盖主要场景
- ⚠️ 建议:添加边界情况测试(如路径包含特殊字符)
### 代码质量评估
| 方面 | 评分 | 说明 |
|------|------|------|
| 架构设计 | ⭐⭐⭐⭐⭐ | 清晰的分层和职责分离 |
| 代码可读性 | ⭐⭐⭐⭐⭐ | 良好的命名和注释 |
| 测试覆盖 | ⭐⭐⭐⭐⭐ | 全面的测试用例 |
| 错误处理 | ⭐⭐⭐⭐ | 考虑周全,可进一步增强 |
| 性能 | ⭐⭐⭐⭐⭐ | 延迟枚举优化性能 |
---
## 影响范围
### 功能影响
-**运行时检测**: 支持按用户安装的 .NET 运行时
-**安装程序**: 更智能的 .NET 运行时检测
-**用户体验**: 减少不必要的 .NET 运行时重新安装
### 技术影响
-**跨用户支持**: 支持同一机器上的多个用户配置
-**混合安装**: 支持系统和按用户安装混合场景
-**向后兼容**: 保持对现有系统安装的检测能力
---
## 安全考虑
1. **路径验证**: ✅ 使用 `Path.GetFullPath()` 防止路径注入
2. **权限检查**: ✅ 区分系统和用户目录的访问权限
3. **文件存在性**: ✅ 在访问前检查文件和目录存在性
---
## 总结
这是一次高质量的功能修复提交,主要解决了 .NET 运行时检测的关键问题:
### 核心改进
1.**扩展检测范围**: 支持按用户安装的 .NET 运行时
2.**多框架支持**: 同时检测 Core 和 Desktop 运行时
3.**智能合并**: 正确处理系统和用户安装的混合场景
4.**全面测试**: 6 个新的单元测试确保可靠性
5.**安装程序增强**: Inno Setup 脚本同步更新
### 代码质量
- 🏆 **优秀**: 架构清晰,代码规范
- 🏆 **优秀**: 测试覆盖全面
- 🏆 **优秀**: 错误处理周全
**建议**: ✅ 可以合并,建议后续添加更多边界情况测试。