using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; namespace R0Installer { /// /// 静默更新执行器:不创建任何窗口,直接在后台完成检查/下载/应用更新。 /// 复用与 UpdateForm 完全相同的配置加载与 UpdateManager 逻辑, /// 仅去除界面展示与纯用于观感的等待,因此更新行为与有界面模式一致。 /// public class SilentUpdater { private readonly string currentVersion; private readonly bool updateConfirmed; private readonly string projectId; private readonly string updateTarget; private readonly string installPath; private UpdateManager updateManager; private string projectConfig; private string updateConfig; private string[] processesToClose = new string[0]; private string launchAfterUpdate = ""; private string holdFile = ""; public SilentUpdater(string version, bool updateConfirmed, string projectId, string updateTarget) { this.currentVersion = version; this.updateConfirmed = updateConfirmed; this.projectId = projectId; this.updateTarget = updateTarget; this.installPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); LoadConfig(); } /// 同步入口:阻塞当前线程直到静默更新完成,返回是否成功。 public bool Run() { return RunAsync().GetAwaiter().GetResult(); } private void LoadConfig() { projectConfig = ConfigManager.GetProjectConfigSync(projectId); if (projectConfig != null) updateConfig = ConfigManager.GetUpdateConfig(projectConfig, updateTarget); if (!string.IsNullOrEmpty(updateConfig)) { launchAfterUpdate = ConfigManager.ExtractJsonValue(updateConfig, "launch_after_update"); holdFile = ConfigManager.ExtractJsonValue(updateConfig, "hold_file"); string procArray = ConfigManager.ExtractJsonArray(updateConfig, "processes_to_close"); if (!string.IsNullOrEmpty(procArray)) processesToClose = ConfigManager.ParseJsonStringArray(procArray).ToArray(); updateManager = new UpdateManager(updateConfig, projectConfig); } else { string fallbackUpdateJson = "{\"check_api\":\"/v3/update/check?project=" + projectId + "&v={0}\",\"temp_filename_template\":\"update_v{version}.exe\"}"; updateManager = new UpdateManager(fallbackUpdateJson, "{}"); } // 静默模式下没有界面,进度/状态仅写入调试输出,便于排查问题。 updateManager.OnProgress = (percent, message, speed) => Debug.WriteLine(String.Format("[静默更新] {0}% {1} {2}", percent, message, speed)); updateManager.OnStatus = (message) => Debug.WriteLine("[静默更新] " + message); } public async Task RunAsync() { bool hasHoldFile = !string.IsNullOrEmpty(holdFile) && holdFile != "null"; try { UpdateCheckResult result = await updateManager.CheckForUpdate(currentVersion); if (result == null) { Console.WriteLine("ERROR"); return false; } if (!updateConfirmed && !result.has_update) { Console.WriteLine("OK"); return true; } if (hasHoldFile) CreateHoldFile(); CloseProcesses(); // 给系统一点时间释放文件句柄,确保增量补丁能成功覆盖文件。 await Task.Delay(500); bool success = false; switch (result.update_type) { case "full": // 全量包(Inno Setup)静默安装。 success = await updateManager.PerformFullUpdate(result, true); break; case "incremental": success = await updateManager.PerformIncrementalUpdate(result.download_url, installPath); break; case "multi_incremental": success = await updateManager.PerformMultiIncrementalUpdate(result.incremental_updates, installPath); break; default: Debug.WriteLine("未知的更新类型: " + result.update_type); if (hasHoldFile) DeleteHoldFile(); Console.WriteLine("ERROR"); return false; } // 无论全量/增量,静默更新由本程序负责全流程,完成后统一清理 hold 文件并拉起主程序。 if (hasHoldFile) DeleteHoldFile(); if (success) LaunchMainProgram(); Console.WriteLine(success ? "OK" : "ERROR"); return success; } catch (Exception ex) { Debug.WriteLine("静默更新出错: " + ex); if (hasHoldFile) DeleteHoldFile(); Console.WriteLine("ERROR: " + ex.Message); return false; } } private void CloseProcesses() { foreach (string name in processesToClose) { try { Process[] processes = Process.GetProcessesByName(name); foreach (Process proc in processes) { try { proc.Kill(); proc.WaitForExit(5000); } catch (Exception ex) { Debug.WriteLine("关闭进程失败: " + ex.Message); } finally { proc.Dispose(); } } } catch (Exception ex) { Debug.WriteLine("获取进程失败: " + ex.Message); } } } private string GetHoldFilePath() { return Path.Combine(Path.GetTempPath(), holdFile); } private void CreateHoldFile() { try { File.WriteAllText(GetHoldFilePath(), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } catch (Exception ex) { Debug.WriteLine("创建 hold 文件失败: " + ex.Message); } } private void DeleteHoldFile() { try { string holdPath = GetHoldFilePath(); if (File.Exists(holdPath)) File.Delete(holdPath); } catch (Exception ex) { Debug.WriteLine("删除 hold 文件失败: " + ex.Message); } } private void LaunchMainProgram() { if (string.IsNullOrEmpty(launchAfterUpdate)) return; try { string mainExe = Path.Combine(installPath, launchAfterUpdate); if (File.Exists(mainExe)) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = mainExe; psi.UseShellExecute = true; psi.WorkingDirectory = installPath; Process.Start(psi); } else { Debug.WriteLine("程序未找到: " + mainExe); } } catch (Exception ex) { Debug.WriteLine("启动程序失败: " + ex.Message); } } } }