using System; using System.Diagnostics; using System.IO; namespace LibationFileManager; /// /// Starts a fresh copy of Libation after startup has repaired the install folder underneath the running one. /// /// The running process cannot simply carry on: it has already loaded the assemblies that the rollback just /// replaced on disk. Restarting is the only way to pick up the restored files, so the user is asked whether /// to do it now. /// public static class InstallRelauncher { /// /// Set on the child process so it knows not to offer a restart of its own. /// /// A rollback deletes the pending upgrade marker before it returns, so it normally cannot happen twice. /// But that delete is best-effort and swallows its own failure, and a marker that outlives a rollback /// would otherwise mean every launch rolls back and offers to restart again. One relaunch, then. /// public const string RelaunchedEnvironmentVariable = "LIBATION_RELAUNCHED_AFTER_ROLLBACK"; /// True when this process was started by . public static bool WasRelaunched => Environment.GetEnvironmentVariable(RelaunchedEnvironmentVariable) == "1"; /// /// Overridable so tests can assert on the decision to relaunch without starting a process. /// public static Func StartProcess { get; set; } = StartDetached; /// /// Starts a new Libation process. Best effort: if it does not work the user can start Libation /// themselves, which is what the message tells them to do anyway. /// /// True when a new process was started. public static bool TryRelaunch() { try { var executable = Environment.ProcessPath; if (string.IsNullOrWhiteSpace(executable) || !File.Exists(executable)) { StartupLog.Error($"Cannot restart Libation: no executable at '{executable}'"); return false; } var started = StartProcess(executable); StartupLog.Information(started ? $"Restarting Libation from {executable}" : $"Could not restart Libation from {executable}"); return started; } catch (Exception ex) { StartupLog.Error(ex, "Could not restart Libation"); return false; } } private static bool StartDetached(string executable) { // UseShellExecute must stay false: setting an environment variable for the child is not allowed // with the shell, and the marker is what stops a restart loop. var startInfo = new ProcessStartInfo(executable) { // The install folder was just rewritten, so start from it rather than inheriting a stale one. WorkingDirectory = Configuration.ProcessDirectory, UseShellExecute = false, }; startInfo.Environment[RelaunchedEnvironmentVariable] = "1"; return Process.Start(startInfo) is not null; } }