.NET C# 程序自动更新组件

news/2025/3/18 15:18:48/文章来源:https://www.cnblogs.com/Bob-luo/p/18231510

引言

本来博主想偷懒使用AutoUpdater.NET组件,但由于博主项目有些特殊性和它的功能过于多,于是博主自己实现一个轻量级独立自动更新组件,可稍作修改集成到大家自己项目中,比如:WPF/Winform/Windows服务。大致思路:发现更新后,从网络上下载更新包并进行解压,同时在 WinForms 应用程序中显示下载和解压进度条,并重启程序。以提供更好的用户体验。

1. 系统架构概览

自动化软件更新系统主要包括以下几个核心部分:

  • 版本检查:定期或在启动时检查服务器上的最新版本。
  • 下载更新:如果发现新版本,则从服务器下载更新包。
  • 解压缩与安装:解压下载的更新包,替换旧文件。
  • 重启应用:更新完毕后,重启应用以加载新版本。

组件实现细节

独立更新程序逻辑:

1. 创建 WinForms 应用程序

首先,创建一个新的 WinForms 应用程序,用来承载独立的自动更新程序,界面就简单两个组件:添加一个 ProgressBar 和一个 TextBox 控件,用于显示进度和信息提示。

2. 主窗体加载事件

我们在主窗体的 Load 事件中完成以下步骤:

  • 解析命令行参数。
  • 关闭当前运行的程序。
  • 下载更新包并显示下载进度。
  • 解压更新包并显示解压进度。
  • 启动解压后的新版本程序。

下面是主窗体 Form1_Load 事件处理程序的代码:

private async void Form1_Load(object sender, EventArgs e)
{// 读取和解析命令行参数var args = Environment.GetCommandLineArgs();if (!ParseArguments(args, out string downloadUrl, out string programToLaunch, out string currentProgram)){_ = MessageBox.Show("请提供有效的下载地址和启动程序名称的参数。");Application.Exit();return;}// 关闭当前运行的程序Process[] processes = Process.GetProcessesByName(currentProgram);foreach (Process process in processes){process.Kill();process.WaitForExit();}// 开始下载和解压过程string downloadPath = Path.Combine(Path.GetTempPath(), Path.GetFileName(downloadUrl));progressBar.Value = 0;textBoxInformation.Text = "下载中...";await DownloadFileAsync(downloadUrl, downloadPath);progressBar.Value = 0;textBoxInformation.Text = "解压中...";await Task.Run(() => ExtractZipFile(downloadPath, AppDomain.CurrentDomain.BaseDirectory));textBoxInformation.Text = "完成";// 启动解压后的程序string programPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, programToLaunch);if (File.Exists(programPath)){_ = Process.Start(programPath);Application.Exit();}else{_ = MessageBox.Show($"无法找到程序:{programPath}");}
}

3. 解析命令行参数

我们需要从命令行接收下载地址、启动程序名称和当前运行程序的名称。以下是解析命令行参数的代码:

查看代码
        private bool ParseArguments(string[] args, out string downloadUrl, out string programToLaunch, out string currentProgram){downloadUrl = null;programToLaunch = null;currentProgram = null;for (int i = 1; i < args.Length; i++){if (args[i].StartsWith("--url=")){downloadUrl = args[i].Substring("--url=".Length);}else if (args[i] == "--url" && i + 1 < args.Length){downloadUrl = args[++i];}else if (args[i].StartsWith("--launch=")){programToLaunch = args[i].Substring("--launch=".Length);}else if (args[i] == "--launch" && i + 1 < args.Length){programToLaunch = args[++i];}else if (args[i].StartsWith("--current=")){currentProgram = args[i].Substring("--current=".Length);}else if (args[i] == "--current" && i + 1 < args.Length){currentProgram = args[++i];}}return !string.IsNullOrEmpty(downloadUrl) && !string.IsNullOrEmpty(programToLaunch) && !string.IsNullOrEmpty(currentProgram);}

4. 下载更新包并显示进度

使用 HttpClient 下载文件,并在下载过程中更新进度条:

private async Task DownloadFileAsync(string url, string destinationPath)
{using (HttpClient client = new HttpClient()){using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)){_ = response.EnsureSuccessStatusCode();long? totalBytes = response.Content.Headers.ContentLength;using (var stream = await response.Content.ReadAsStreamAsync())using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true)){var buffer = new byte[8192];long totalRead = 0;int bytesRead;while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0){await fileStream.WriteAsync(buffer, 0, bytesRead);totalRead += bytesRead;if (totalBytes.HasValue){int progress = (int)((double)totalRead / totalBytes.Value * 100);_ = Invoke(new Action(() => progressBar.Value = progress));}}}}}
}

5. 解压更新包并显示进度

在解压过程中跳过 Updater.exe 文件(因为当前更新程序正在运行,大家可根据需求修改逻辑),并捕获异常以确保进度条和界面更新:

 
private void ExtractZipFile(string zipFilePath, string extractPath)
{using (ZipArchive archive = ZipFile.OpenRead(zipFilePath)){int totalEntries = archive.Entries.Count;int extractedEntries = 0;foreach (ZipArchiveEntry entry in archive.Entries){try{// 跳过 Updater.exe 文件if (entry.FullName.Equals(CustConst.AppNmae, StringComparison.OrdinalIgnoreCase)){continue;}string destinationPath = Path.Combine(extractPath, entry.FullName);_ = Invoke(new Action(() => textBoxInformation.Text = $"解压中... {entry.FullName}"));if (string.IsNullOrEmpty(entry.Name)){// Create directory_ = Directory.CreateDirectory(destinationPath);}else{// Ensure directory exists_ = Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));// Extract fileentry.ExtractToFile(destinationPath, overwrite: true);}extractedEntries++;int progress = (int)((double)extractedEntries / totalEntries * 100);_ = Invoke(new Action(() => progressBar.Value = progress));}catch (Exception ex){_ = Invoke(new Action(() => textBoxInformation.Text = $"解压失败:{entry.FullName}, 错误: {ex.Message}"));continue;}}}
}

6. 启动解压后的新程序

在解压完成后,启动新版本的程序,并且关闭更新程序:

查看代码
 private void Form1_Load(object sender, EventArgs e)
{// 省略部分代码...string programPath = Path.Combine(extractPath, programToLaunch);if (File.Exists(programPath)){Process.Start(programPath);Application.Exit();}else{MessageBox.Show($"无法找到程序:{programPath}");}
}

检查更新逻辑

1. 创建 UpdateChecker

创建一个 UpdateChecker 类,对外提供引用,用于检查更新并启动更新程序

public static class UpdateChecker
{public static string UpdateUrl { get; set; }public static string CurrentVersion { get; set; }public static string MainProgramRelativePath { get; set; }public static void CheckForUpdates(){try{using (HttpClient client = new HttpClient()){string xmlContent = client.GetStringAsync(UpdateUrl).Result;XDocument xmlDoc = XDocument.Parse(xmlContent);var latestVersion = xmlDoc.Root.Element("version")?.Value;var downloadUrl = xmlDoc.Root.Element("url")?.Value;if (!string.IsNullOrEmpty(latestVersion) && !string.IsNullOrEmpty(downloadUrl) && latestVersion != CurrentVersion){// 获取当前程序名称string currentProcessName = Process.GetCurrentProcess().ProcessName;// 启动更新程序并传递当前程序名称string arguments = $"--url \"{downloadUrl}\" --launch \"{MainProgramRelativePath}\" --current \"{currentProcessName}\"";_ = Process.Start(CustConst.AppNmae, arguments);// 关闭当前主程序Application.Exit();}}}catch (Exception ex){_ = MessageBox.Show($"检查更新失败:{ex.Message}");}}
}

2. 服务器配置XML

服务器上存放一个XML文件配置当前最新版本、安装包下载地址等,假设服务器上的 XML 文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<update><version>1.0.2</version><url>https://example.com/yourfile.zip</url>
</update>

主程序调用更新检查

主程序可以通过定时器或者手动调用检查更新的逻辑,博主使用定时检查更新:

查看代码
 internal static class AutoUpdaterHelp{private static readonly System.Timers.Timer timer;static AutoUpdaterHelp(){UpdateChecker.CurrentVersion = "1.0.1";UpdateChecker.UpdateUrl = ConfigurationManager.AppSettings["AutoUpdaterUrl"].ToString();UpdateChecker.MainProgramRelativePath = "Restart.bat";timer = new System.Timers.Timer{Interval = 10 * 1000//2 * 60 * 1000};timer.Elapsed += delegate{UpdateChecker.CheckForUpdates();};}public static void Start(){timer.Start();}public static void Stop(){timer.Stop();}}

思考:性能与安全考量

在实现自动化更新时,还应考虑性能和安全因素。例如,为了提高效率,可以添加断点续传功能;为了保证安全,应验证下载文件的完整性,例如使用SHA256校验和,这些博主就不做实现与讲解了,目前的功能已经完成了基本的自动更新逻辑

结论

自动化软件更新是现代软件开发不可或缺的一部分,它不仅能显著提升用户体验,还能减轻开发者的维护负担。通过上述C#代码示例,你可以快速搭建一个基本的自动化更新框架,进一步完善和定制以适应特定的应用场景。


本文提供了构建自动化软件更新系统的C#代码实现,希望对开发者们有所帮助。如果你有任何疑问或建议,欢迎留言讨论!


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/720161.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IC设计企业致力于解决的HPC数据防泄漏,到底该怎么做?

对于半导体IC设计企业来说,芯片设计、验证、仿真使用HPC环境现在已逐渐成为趋势,主要原因在于原来的工作流程存在较多的缺陷: 性能瓶颈:仿真、设计、验证、生产过程中,前端仿真需要小文件高并发低时延的读写和巨量元数据处理能力,后端仿真存储需要提供很大的读写带宽满足…

重写学习 localStorage 与 sessionStorage

localStorage 与 sessionStorage localStorage 与 sessionStorage 很多小伙伴对它们俩都很熟悉了;最熟悉的莫过下面这2条 1,localStorage 存储的数据没有时间限制,理论上永久有效;除非手动清除。sessionStorage 存储的数据在关闭当前页面后失效; 2,有存储大小限制,两者存储大…

ASP.NET Web应用程序升级最新的MSBuild格式后,Visual Studio 2022中如何调试?

摘要 把ASP.NET的Web应用程序,Project文件从 <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">改为 <Project Sdk="Microsoft.NET.Sdk.Web">之后,升…

AI带你玩转音乐,使你成为真正音乐人(AI歌曲制作)

利用AI工具快速完成一首歌: 歌由词和曲组成。 词可以利用文心一言 输入:写一首赞扬国家繁华发展的歌词 这样一首歌的词部分已经出来。 曲部分: 利用https://suno.com/create生成: 拷贝文心一言生成的词 选择定制模式Custom Mode有更多的风格可以制作点击生成就可以完成曲制…

零基础写框架(2):故障排查和日志基础

关于从零设计 .NET 开发框架 作者:痴者工良 教程说明: 仓库地址:https://github.com/whuanle/maomi 文档地址:https://maomi.whuanle.cn 作者博客: https://www.whuanle.cn https://www.cnblogs.com/whuanle 故障排查和日志 .NET 程序进行故障排查的方式有很多,笔者个人总…

使用Visual Studio分析.NET Dump

前言 内存泄漏和高CPU使用率是在日常开发中经常遇到的问题,它们可能会导致应用程序性能下降甚至崩溃。今天我们来讲讲如何使用Visual Studio 2022分析.NET Dump,快速找到程序内存泄漏问题。 什么是Dump文件? Dump文件又叫内存转储文件或者叫内存快照文件。用于存储程序运行时…

springboot模块化开发项目搭建

1.New一个Project,命名,Next,Finish 2.根据需要修改Maven配置 3.初始化后,删除无用文件4.选中项目创建Module,命名,Next,一般模块包括common、dao、service、web、entrance(入口文件),也可以增加订单、会员等各种业务模块,各模块之间依赖引用即可5.删除无用文件,删…

组合数学中的食用工具

背景: 教授在打概率和期望中的《灯蹬登》,需要推式子。众所周知,一个正确的式子不光要可以解释已有的数据,还要能预测未知数据的结果。在这样的情况下,组合数学的工具是必不可少的。我们通过这个工具实现了三种计算器无法直接实现的功能:输入\(A,m,n\),表示求\(A^m_n\)的…

读AI未来进行式笔记03自然语言处理技术

自然语言处理1. AI伙伴 1.1. 作为AI能力的集大成者,AI伙伴融合了各种复杂的AI技术 1.2. 人类唯一可能超越AI的领域,只可能在机器无法触及之处,那是属于人类感性与直觉的领域 1.3. 要读懂人类,需要漫长而平缓的学习过程 1.4. AI塑造了我们,我们反过来也塑造了AI 1.5. AI的“…

PyQT5之单行文本输入到多行显示窗

from PyQt5 import QtWidgets from PyQt5 import QtCore, QtGui import sys import cv2class TextDemoPanel(QtWidgets.QWidget):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)select_btn = QtWidgets.QPushButton("输入")self.line_text …

ipsec-vpen核心demo

撸起袖子加油干!!!