Files
fast_install/R0Installer/UpdateForm.cs
T

493 lines
18 KiB
C#
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.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace R0Installer
{
public class UpdateForm : Form
{
private Panel progressBar;
private Panel progressFill;
private Label titleLabel;
private Label statusLabel;
private Label percentLabel;
private Label versionLabel;
private string currentVersion;
private UpdateManager updateManager;
private readonly Color primaryGreen = Color.FromArgb(76, 175, 80);
private readonly Color darkBg = Color.FromArgb(18, 18, 18);
private readonly Color panelBg = Color.FromArgb(28, 28, 28);
private readonly Color textGray = Color.FromArgb(180, 180, 180);
private readonly Color textDark = Color.FromArgb(100, 100, 100);
private bool isDragging = false;
private Point dragStart;
private string installPath;
private bool updateConfirmed;
private string projectId;
private string updateTarget;
private string projectConfig;
private string updateConfig;
private string windowTitle;
private string[] processesToClose;
private string launchAfterUpdate;
private string holdFile;
public UpdateForm(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();
InitializeComponent();
SetupUI();
this.Load += UpdateForm_Load;
}
private void LoadConfig()
{
projectConfig = ConfigManager.GetProjectConfigSync(projectId);
if (projectConfig != null)
{
updateConfig = ConfigManager.GetUpdateConfig(projectConfig, updateTarget);
}
if (updateConfig != null)
{
windowTitle = ConfigManager.ExtractJsonValue(updateConfig, "window_title");
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))
{
List<string> procs = ConfigManager.ParseJsonStringArray(procArray);
processesToClose = procs.ToArray();
}
this.updateManager = new UpdateManager(updateConfig, projectConfig);
}
else
{
windowTitle = "更新程序";
processesToClose = new string[0];
launchAfterUpdate = "";
holdFile = "";
string fallbackUpdateJson = "{\"check_api\":\"/v3/update/check?project=" + projectId + "&v={0}\",\"temp_filename_template\":\"update_v{version}.exe\"}";
string fallbackProjectJson = "{}";
this.updateManager = new UpdateManager(fallbackUpdateJson, fallbackProjectJson);
}
if (string.IsNullOrEmpty(windowTitle)) windowTitle = "更新程序";
if (processesToClose == null) processesToClose = new string[0];
this.updateManager.OnProgress = OnProgressUpdate;
this.updateManager.OnStatus = OnStatusUpdate;
}
private async void UpdateForm_Load(object sender, EventArgs e)
{
await StartUpdateProcess();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.Text = windowTitle;
this.Size = new Size(500, 200);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.None;
this.BackColor = darkBg;
this.DoubleBuffered = true;
try
{
this.Icon = Icon.ExtractAssociatedIcon(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
catch { }
this.Region = CreateRoundedRegion(this.Width, this.Height, 12);
this.MouseDown += Form_MouseDown;
this.MouseMove += Form_MouseMove;
this.MouseUp += Form_MouseUp;
this.ResumeLayout(false);
}
private Region CreateRoundedRegion(int width, int height, int radius)
{
GraphicsPath path = new GraphicsPath();
path.AddArc(0, 0, radius * 2, radius * 2, 180, 90);
path.AddArc(width - radius * 2, 0, radius * 2, radius * 2, 270, 90);
path.AddArc(width - radius * 2, height - radius * 2, radius * 2, radius * 2, 0, 90);
path.AddArc(0, height - radius * 2, radius * 2, radius * 2, 90, 90);
path.CloseAllFigures();
return new Region(path);
}
private void SetupUI()
{
Label closeBtn = new Label();
closeBtn.Text = "×";
closeBtn.Font = new Font("Segoe UI", 14, FontStyle.Regular);
closeBtn.ForeColor = textGray;
closeBtn.Size = new Size(36, 36);
closeBtn.Location = new Point(this.Width - 40, 4);
closeBtn.TextAlign = ContentAlignment.MiddleCenter;
closeBtn.Cursor = Cursors.Hand;
closeBtn.Click += CloseBtn_Click;
closeBtn.MouseEnter += (s, e) => ((Label)s).ForeColor = Color.White;
closeBtn.MouseLeave += (s, e) => ((Label)s).ForeColor = textGray;
this.Controls.Add(closeBtn);
titleLabel = new Label();
titleLabel.Text = windowTitle.Replace("程序", "");
titleLabel.Font = new Font("Microsoft YaHei", 16, FontStyle.Bold);
titleLabel.ForeColor = primaryGreen;
titleLabel.AutoSize = true;
titleLabel.BackColor = Color.Transparent;
titleLabel.Location = new Point((this.Width - 180) / 2, 30);
this.Controls.Add(titleLabel);
versionLabel = new Label();
versionLabel.Text = "当前版本: " + currentVersion;
versionLabel.Font = new Font("Microsoft YaHei", 9, FontStyle.Regular);
versionLabel.ForeColor = textGray;
versionLabel.AutoSize = true;
versionLabel.Location = new Point(40, 75);
this.Controls.Add(versionLabel);
statusLabel = new Label();
statusLabel.Text = "正在检查更新...";
statusLabel.Font = new Font("Microsoft YaHei", 9, FontStyle.Regular);
statusLabel.ForeColor = textGray;
statusLabel.AutoSize = false;
statusLabel.Size = new Size(300, 25);
statusLabel.Location = new Point(40, 110);
this.Controls.Add(statusLabel);
percentLabel = new Label();
percentLabel.Text = "";
percentLabel.Font = new Font("Microsoft YaHei", 9, FontStyle.Bold);
percentLabel.ForeColor = primaryGreen;
percentLabel.AutoSize = false;
percentLabel.Size = new Size(160, 25);
percentLabel.TextAlign = ContentAlignment.MiddleRight;
percentLabel.Location = new Point(this.Width - 200, 110);
this.Controls.Add(percentLabel);
progressBar = new Panel();
progressBar.Size = new Size(this.Width - 80, 8);
progressBar.Location = new Point(40, 140);
progressBar.BackColor = panelBg;
this.Controls.Add(progressBar);
progressFill = new Panel();
progressFill.Size = new Size(0, 8);
progressFill.Location = new Point(0, 0);
progressFill.BackColor = primaryGreen;
progressBar.Controls.Add(progressFill);
this.Paint += UpdateForm_Paint;
}
private void UpdateForm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen pen = new Pen(Color.FromArgb(50, 255, 255, 255), 1))
{
Rectangle rect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
using (GraphicsPath path = CreateRoundedPath(rect, 12))
{
g.DrawPath(pen, path);
}
}
}
private GraphicsPath CreateRoundedPath(Rectangle rect, int radius)
{
GraphicsPath path = new GraphicsPath();
path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90);
path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90);
path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
path.CloseAllFigures();
return path;
}
#region Window events
private void CloseBtn_Click(object sender, EventArgs e) { Application.Exit(); }
private void Form_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) { isDragging = true; dragStart = e.Location; }
}
private void Form_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
Point diff = new Point(e.X - dragStart.X, e.Y - dragStart.Y);
this.Location = new Point(this.Location.X + diff.X, this.Location.Y + diff.Y);
}
}
private void Form_MouseUp(object sender, MouseEventArgs e) { isDragging = false; }
#endregion
#region Progress
private void OnProgressUpdate(int percent, string message, string speedText)
{
if (this.InvokeRequired) { this.Invoke(new Action<int, string, string>(OnProgressUpdate), percent, message, speedText); return; }
int targetWidth = (int)(progressBar.Width * percent / 100.0);
progressFill.Width = targetWidth;
if (message != null)
statusLabel.Text = message;
if (!string.IsNullOrEmpty(speedText))
percentLabel.Text = String.Format("{0}% {1}", percent, speedText);
else
percentLabel.Text = String.Format("{0}%", percent);
}
private void OnStatusUpdate(string message)
{
if (this.InvokeRequired) { this.Invoke(new Action<string>(OnStatusUpdate), message); return; }
statusLabel.Text = message;
}
private void UpdateVersionLabel(string newVersion)
{
if (this.InvokeRequired) { this.Invoke(new Action<string>(UpdateVersionLabel), newVersion); return; }
versionLabel.Text = String.Format("更新: {0} → {1}", currentVersion, newVersion);
}
#endregion
#region Update process
private async Task StartUpdateProcess()
{
try
{
OnStatusUpdate("正在获取更新信息...");
UpdateCheckResult result = await updateManager.CheckForUpdate(currentVersion);
if (result == null)
{
OnStatusUpdate("获取更新信息失败,请检查网络连接");
Console.WriteLine("ERROR");
await Task.Delay(3000);
Application.Exit();
return;
}
if (!updateConfirmed && !result.has_update)
{
OnStatusUpdate("当前已是最新版本");
Console.WriteLine("OK");
Application.Exit();
return;
}
bool hasHoldFile = !string.IsNullOrEmpty(holdFile) && holdFile != "null";
if (hasHoldFile)
{
CreateHoldFile();
}
OnStatusUpdate("正在关闭相关程序...");
CloseProcesses();
await Task.Delay(500);
UpdateVersionLabel(result.latest_version);
bool success = false;
bool isIncrementalUpdate = false;
switch (result.update_type)
{
case "full":
OnStatusUpdate("正在准备全量更新...");
success = await updateManager.PerformFullUpdate(result);
if (success)
{
OnProgressUpdate(100, "更新完成,正在启动安装程序...", "");
await Task.Delay(1000);
}
break;
case "incremental":
isIncrementalUpdate = true;
OnStatusUpdate("正在准备增量更新...");
success = await updateManager.PerformIncrementalUpdate(result.download_url, installPath);
if (success)
{
OnProgressUpdate(100, "更新完成!", "");
await Task.Delay(1000);
if (hasHoldFile) DeleteHoldFile();
LaunchMainProgram();
}
break;
case "multi_incremental":
isIncrementalUpdate = true;
OnStatusUpdate("正在准备增量更新...");
success = await updateManager.PerformMultiIncrementalUpdate(result.incremental_updates, installPath);
if (success)
{
OnProgressUpdate(100, "更新完成!", "");
await Task.Delay(1000);
if (hasHoldFile) DeleteHoldFile();
LaunchMainProgram();
}
break;
default:
OnStatusUpdate("未知的更新类型: " + result.update_type);
await Task.Delay(3000);
break;
}
if (!success)
{
OnStatusUpdate("更新失败,请稍后重试");
if (hasHoldFile && isIncrementalUpdate) DeleteHoldFile();
await Task.Delay(3000);
}
Application.Exit();
}
catch (Exception ex)
{
OnStatusUpdate("更新出错: " + ex.Message);
Debug.WriteLine("更新错误: " + ex.ToString());
Console.WriteLine("ERROR: " + ex.Message);
System.Threading.Thread.Sleep(5000);
Application.Exit();
}
}
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
{
string holdPath = GetHoldFilePath();
File.WriteAllText(holdPath, 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);
}
}
#endregion
}
}