Add launcher debug settings, recovery & version fixes

Introduce a persistent LauncherDebugSettingsStore and wire it into ErrorWindow and SplashWindow so dev-mode and custom host path can be saved/loaded. Harden DeploymentLocator/FlexibleHostLocator to safely normalize and validate saved debug paths and log warnings for malformed values. Add a WaitingForShell startup state and recoverable-activation logic across App and LauncherFlowCoordinator (with registry updates) so Launcher can attach to an in-progress desktop shell rather than failing. Clean up ErrorDebugWindow UI/flow (WasAccepted flag, localization fixes, event wiring) and improve splash version population. Improve AppVersionProvider to trim surrounding quotes, robustly parse version.json via JsonDocument and read string properties; add unit tests for AppVersionProvider, DeploymentLocator and LauncherDebugSettingsStore. Also quote Exec commands in the csproj and harden scripts/Generate-VersionFile.ps1 (argument normalization, LiteralPath, error handling).
This commit is contained in:
lincube
2026-04-23 19:04:39 +08:00
parent 2d9391f930
commit d4901e436f
17 changed files with 742 additions and 198 deletions

View File

@@ -108,7 +108,9 @@ public static class AppVersionProvider
return fallback;
}
var normalized = rawValue.Split('+', 2, StringSplitOptions.TrimEntries)[0].Trim();
var normalized = TrimSurroundingQuotes(rawValue)
.Split('+', 2, StringSplitOptions.TrimEntries)[0]
.Trim();
return string.IsNullOrWhiteSpace(normalized)
? fallback
: normalized;
@@ -116,9 +118,10 @@ public static class AppVersionProvider
public static string NormalizeCodename(string? rawValue, string fallback = DefaultCodename)
{
return string.IsNullOrWhiteSpace(rawValue)
var normalized = TrimSurroundingQuotes(rawValue);
return string.IsNullOrWhiteSpace(normalized)
? fallback
: rawValue.Trim();
: normalized;
}
private static AppVersionInfo OverrideMissingParts(
@@ -158,17 +161,24 @@ public static class AppVersionProvider
try
{
var json = File.ReadAllText(versionFilePath);
var parsedInfo = JsonSerializer.Deserialize<AppVersionInfo>(json);
if (parsedInfo is null || string.IsNullOrWhiteSpace(parsedInfo.Version))
using var document = JsonDocument.Parse(File.ReadAllText(versionFilePath));
var root = document.RootElement;
if (root.ValueKind != JsonValueKind.Object)
{
return false;
}
var version = ReadStringProperty(root, nameof(AppVersionInfo.Version));
if (string.IsNullOrWhiteSpace(version))
{
return false;
}
var codename = ReadStringProperty(root, nameof(AppVersionInfo.Codename));
info = new AppVersionInfo
{
Version = NormalizeVersionText(parsedInfo.Version),
Codename = NormalizeCodename(parsedInfo.Codename)
Version = NormalizeVersionText(version),
Codename = NormalizeCodename(codename)
};
return true;
}
@@ -359,4 +369,43 @@ public static class AppVersionProvider
return null;
}
}
private static string? ReadStringProperty(JsonElement root, string propertyName)
{
foreach (var property in root.EnumerateObject())
{
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) &&
property.Value.ValueKind == JsonValueKind.String)
{
return property.Value.GetString();
}
}
return null;
}
private static string TrimSurroundingQuotes(string? rawValue)
{
if (string.IsNullOrWhiteSpace(rawValue))
{
return string.Empty;
}
var normalized = rawValue.Trim();
while (normalized.Length >= 2)
{
var first = normalized[0];
var last = normalized[^1];
if ((first == '\'' && last == '\'') ||
(first == '"' && last == '"'))
{
normalized = normalized[1..^1].Trim();
continue;
}
break;
}
return normalized;
}
}