mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-21 08:04:26 +08:00
Add automatic release version stamping and multiple launcher reliability improvements. The Release workflow now runs scripts/Set-ReleaseVersion.ps1 in build jobs to inject tag-derived Version/AssemblyVersion into project metadata; several .csproj/Directory.Build.props and app.manifest files were changed to use a dev placeholder. Introduced AppVersionProvider (and related runtime metadata) to centralize version resolution and updated DeploymentLocator to use it and to prefer package-root/version.json. Launcher startup flow was hardened: added startup success tracking, public-activation recovery path, improved success/fallback semantics, and related IPC handling. UI/UX fixes include OOBE entrance/exit animation improvements (scaling-aware, concurrent fade+translate) and minor window lifecycle reorder in DesktopShellHost. CommandContext now recognizes restart and key=value args. New DesktopTrayService and .trae spec files (spec, checklist, tasks) document shell/tray hardening work. Miscellaneous logging, comments and housekeeping edits across launcher and shared contracts to support the above.
164 lines
5.9 KiB
C#
164 lines
5.9 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using Avalonia;
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
using Avalonia.Threading;
|
|
using LanMountainDesktop.PluginSdk;
|
|
using LanMountainDesktop.Shared.Contracts.Launcher;
|
|
|
|
namespace LanMountainDesktop.Services;
|
|
|
|
public sealed class HostApplicationLifecycleService : IHostApplicationLifecycle
|
|
{
|
|
private const string UpgradeHelperExecutableName = "LanMountainDesktop.PluginUpgradeHelper.exe";
|
|
|
|
public bool TryExit(HostApplicationLifecycleRequest? request = null)
|
|
{
|
|
App? app = null;
|
|
try
|
|
{
|
|
AppLogger.Info(
|
|
"HostLifecycle",
|
|
$"Exit requested. Source='{request?.Source ?? "Unknown"}'; Reason='{request?.Reason ?? string.Empty}'.");
|
|
|
|
app = Application.Current as App;
|
|
if (app?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
|
{
|
|
AppLogger.Warn("HostLifecycle", "Exit request ignored because desktop lifetime is unavailable.");
|
|
return false;
|
|
}
|
|
|
|
app.PrepareForShutdown(isRestart: false, request?.Source ?? "Unknown");
|
|
if (Dispatcher.UIThread.CheckAccess())
|
|
{
|
|
desktop.Shutdown();
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() => desktop.Shutdown(), DispatcherPriority.Send);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
app?.ResetShutdownIntent(request?.Source ?? "Unknown");
|
|
AppLogger.Warn("HostLifecycle", "Failed to exit the application.", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRestart(HostApplicationLifecycleRequest? request = null)
|
|
{
|
|
App? app = null;
|
|
try
|
|
{
|
|
app = Application.Current as App;
|
|
|
|
if (HasPendingPluginUpgrades())
|
|
{
|
|
return TryRestartWithUpgradeHelper(request);
|
|
}
|
|
|
|
return TryRestartDirectly(request);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
app?.ResetShutdownIntent(request?.Source ?? "Unknown");
|
|
AppLogger.Warn("HostLifecycle", "Failed to restart the application.", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool HasPendingPluginUpgrades()
|
|
{
|
|
try
|
|
{
|
|
var pluginsDirectory = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"LanMountainDesktop",
|
|
"Extensions",
|
|
"Plugins");
|
|
var pendingUpgradesPath = Path.Combine(pluginsDirectory, ".pending-plugin-upgrades.json");
|
|
return File.Exists(pendingUpgradesPath);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool TryRestartWithUpgradeHelper(HostApplicationLifecycleRequest? request)
|
|
{
|
|
AppLogger.Info("HostLifecycle", "Detected pending plugin upgrades. Using upgrade helper for restart.");
|
|
|
|
var helperPath = ResolveUpgradeHelperPath();
|
|
if (!File.Exists(helperPath))
|
|
{
|
|
AppLogger.Warn("HostLifecycle", $"Upgrade helper not found at '{helperPath}'. Falling back to direct restart.");
|
|
return TryRestartDirectly(request);
|
|
}
|
|
|
|
var pluginsDirectory = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"LanMountainDesktop",
|
|
"Extensions",
|
|
"Plugins");
|
|
|
|
var app = Application.Current as App;
|
|
var restartPresentationMode = app?.GetCurrentRestartPresentationMode() ?? RestartPresentationMode.Foreground;
|
|
var startInfo = AppRestartService.CreateRestartStartInfo(restartPresentationMode: restartPresentationMode);
|
|
var launchCommand = startInfo?.FileName ?? Process.GetCurrentProcess().MainModule?.FileName ?? AppContext.BaseDirectory;
|
|
var launchArgs = startInfo?.Arguments ?? "";
|
|
|
|
var helperStartInfo = new ProcessStartInfo
|
|
{
|
|
FileName = helperPath,
|
|
Arguments = $"--plugins-dir \"{pluginsDirectory}\" --parent-pid {Environment.ProcessId} --launch \"{launchCommand}\" --launch-args \"{launchArgs}\" --working-dir \"{AppContext.BaseDirectory}\"",
|
|
UseShellExecute = true,
|
|
WorkingDirectory = AppContext.BaseDirectory
|
|
};
|
|
|
|
AppLogger.Info("HostLifecycle", $"Starting upgrade helper: {helperStartInfo.FileName} {helperStartInfo.Arguments}");
|
|
|
|
Process.Start(helperStartInfo);
|
|
|
|
app?.PrepareForShutdown(isRestart: true, request?.Source ?? "Unknown");
|
|
|
|
return TryExit(request);
|
|
}
|
|
|
|
private bool TryRestartDirectly(HostApplicationLifecycleRequest? request)
|
|
{
|
|
var app = Application.Current as App;
|
|
var restartPresentationMode = app?.GetCurrentRestartPresentationMode() ?? RestartPresentationMode.Foreground;
|
|
var startInfo = AppRestartService.CreateRestartStartInfo(restartPresentationMode: restartPresentationMode);
|
|
if (startInfo is null)
|
|
{
|
|
AppLogger.Warn(
|
|
"HostLifecycle",
|
|
$"Restart request rejected because restart start info could not be resolved. Source='{request?.Source ?? "Unknown"}'.");
|
|
return false;
|
|
}
|
|
|
|
Process.Start(startInfo);
|
|
app?.PrepareForShutdown(isRestart: true, request?.Source ?? "Unknown");
|
|
var exitRequest = request is null
|
|
? new HostApplicationLifecycleRequest(Reason: "Restart accepted.")
|
|
: request with
|
|
{
|
|
Reason = string.IsNullOrWhiteSpace(request.Reason)
|
|
? "Restart accepted."
|
|
: request.Reason
|
|
};
|
|
|
|
return TryExit(exitRequest);
|
|
}
|
|
|
|
private static string ResolveUpgradeHelperPath()
|
|
{
|
|
return Path.Combine(AppContext.BaseDirectory, "PluginUpgradeHelper", UpgradeHelperExecutableName);
|
|
}
|
|
}
|