引言

本来博主想偷懒使用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 file
entry.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#代码实现,希望对开发者们有所帮助。如果你有任何疑问或建议,欢迎留言讨论!


.NET C# 程序自动更新组件的更多相关文章

  1. c/s应用程序自动更新组件GeneralUpdate3.2.1发布

    一.组件简介 GeneralUpdate是基于.net standard 开发的一款(c/s应用)自动升级程序.该组件将更新的核心部分抽离出来方便应用于多种项目当中目前适用于wpf,控制台应用,win ...

  2. Winform(C#.NET)自动更新组件的使用及部分功能实现

    声明:核心功能的实现是由园子里圣殿骑士大哥写的,本人是基于他核心代码,按照自己需求进行修改的.   而AutoUpdaterService.xml文件生成工具是基于评论#215楼 ptangbao的代 ...

  3. Winform(C#.NET)自动更新组件的使用及部分功能实现(一点改进功能)

    接前两篇继续: Winform(C#.NET)自动更新组件的使用及部分功能实现 Winform(C#.NET)自动更新组件的使用及部分功能实现(续) 借鉴文章:http://www.cnblogs.c ...

  4. Winform(C#.NET)自动更新组件的使用及部分功能实现(续)

    接昨天的文章Winform(C#.NET)自动更新组件的使用及部分功能实现 强制更新的实现部分: 将DownloadConfirm窗体修改成单纯的类 public class DownloadConf ...

  5. Winform自动更新组件分享

    作者:圣殿骑士 出处:http://www.cnblogs.com/KnightsWarrior/ 关于作者:专注于微软平台项目架构.管理和企业解决方案.自认在面向对象及面向服务领域有一定的造诣,熟悉 ...

  6. .Net桌面程序自动更新NAppUpdate

    自动更新介绍 我们做了程序,不免会有版本升级,这就需要程序有自动版本升级的功能.应用程序自动更新是由客户端应用程序自身负责从一个已知服务器下载并安装更新,用户唯一需要进行干预的是决定是否愿意现在或以后 ...

  7. 【Android】友盟的自动更新组件

    前言 又好又专业的服务能帮开发者省很多时间.一开始做项目也准备自己来统计数据.自己做自动更新,随着使用友盟服务的时间增加,渐渐放弃了这种想法,转而研究如何更充分的使用,这里分享一下使用自动更新组件的心 ...

  8. nw.js桌面程序自动更新(node.js表白记)

    Hello Google Node.js 一个基于Google V8 的JavaScript引擎. 一个伟大的端至端语言,或许我对你的热爱源自于web这门极富情感的技术吧! 注: 光阴似水,人生若梦, ...

  9. EF-使用迁移技术让程序自动更新数据库表结构

    承接上一篇文章:关于类库中EntityFramework之CodeFirst(代码优先)的操作浅析 本篇讲述的是怎么使用迁移技术让程序自动通过ORM框架将模型实体类结构映射到现有数据库,并新增或修改与 ...

  10. Windows 程序自动更新方案: Squirrel.Windows

    Windows 程序自动更新方案: Squirrel.Windows 1. Squirrel Squirrel 是一组工具和适用于.Net的库,用于管理 Desktop Windows 应用程序的安装 ...

随机推荐

  1. 实训篇-Html-计算器

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. 使用ollama分别在我的window、mac、小米手机上部署体验llama3-8b

    1.ollama到底是个什么玩意 一句话来说, Ollama 是一个基于 Go 语言开发的简单易用的本地大模型运行框架.可以将其类比为 docker(有类似docker中的一些常规命令list,pul ...

  3. 力扣506(java)-相对名次(简单)

    题目: 给你一个长度为 n 的整数数组 score ,其中 score[i] 是第 i 位运动员在比赛中的得分.所有得分都互不相同 . 运动员将根据得分 决定名次 ,其中名次第 1 的运动员得分最高, ...

  4. 云服务器ECS共享标准型S6全新发布,行业内最具性价比

    近日,阿里云弹性计算发布全新一代云服务ECS共享标准型S6,性能相对上一代实例提升15%以上,价格相对上一代最高降低42%,是目前国内云计算厂商更能够提供的最具性价比的云服务器产品.一些中小型网站.轻 ...

  5. Apsara Stack 同行者专刊 | 政企混合云技术架构的演进和发展

    简介: 现在,政企客户已进入到用云计算全面替换传统IT基础架构的攻坚阶段,混合云与传统架构的技术产品能力也正在经历全面的比较与评估.阿里云混合云平台首席架构师张晓丹分享IT架构技术深刻洞察,并对政企混 ...

  6. 宜搭5月更新:跨应用数据读写能力升级,AI组件内测开放

    ​简介:表单.权限管理.AI组件等功能上新啦- 本次,我们带来了表单.权限管理.数据管理.平台管理权限.组件等功能的升级. 表单 支持跨应用数据查询 在使用组件数据联动.关联其他表单数据.关联表单组件 ...

  7. Quick Audience 营销活动功能一期上线

    ​简介: 营销活动为Quick Audience(QA)用户洞察下的一个功能模块,通过这个模块,可以将QA侧生成的受众以及营销渠道全部关联起来,从营销活动的视角,一站式完成活动目标制定.活动计划制定到 ...

  8. [GPT] Unable to negotiate with xx.xx.xx.xx port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

      这个错误通常发生在 SSH 客户端无法找到与 SSH服务器 匹配的主机密钥类型时. 这可能是因为SSH服务器配置为使用SSH客户端不支持的主机密钥类型. 要解决此问题,您需要将缺少的主机密钥类型添 ...

  9. [FAQ] FinalCutPro 视频背景加模糊效果

    1. 时间轴右上方,找到 倒数第二个 "显示或隐藏效果浏览器",里面有一个 "模糊" 效果: 2. "模糊"效果中的 "高斯曲线& ...

  10. CF522D Closest Equals 离线扫描 + 线段树

    CF522D Closest Equals 题意:m 个询问,求 [l,r] 内相同元素的最小距离. 离线询问,按右端点排序. 对于每一个 a[i],如果 last[a[i]] 存在,将线段树 las ...