前言
在实际开发中,WinForm 应用程序的版本管理是一个常见需求。传统的发布方式如 ClickOnce 虽然方便,但在某些场景下会出现 DLL 文件缺失、更新失败等问题。
本文介绍一种基于代码实现的在线自动更新机制,通过读取服务器上的版本信息与本地对比,判断是否需要下载并更新程序。
该方案使用 JSON 配置文件进行版本控制,结合 ZIP 压缩包实现更新逻辑,并利用 FastZip
进行解压操作,适用于中小型项目快速集成自动更新功能。
实现思路
1、在 IIS 或 Web 服务器上部署更新包(ZIP 格式)和版本信息文件(JSON 格式)。
2、客户端启动时读取本地版本号,并向服务器请求最新的版本信息。
3、比对版本号,若服务器版本高于本地,则触发更新流程。
4、下载 ZIP 更新包后进行解压替换原有文件。
5、重启主程序完成更新。
这种方式避免了传统 ClickOnce 的兼容性问题,具有更高的灵活性和可控性。
系统结构说明
1、版本配置文件(updates.json)
{
"latestversion": "3.0.3",
"downloadurl": "http://127.0.0.1:8091/FD3_WcsClient.zip",
"changelog": "更改日志",
"mandatory": true
}
2、对应实体类 UpdateEntity
/// <summary>
/// 更新信息
/// </summary>
publicclassUpdateEntity
{
/// <summary>
/// 提供更新的版本号
/// </summary>
publicstring latestversion { get; set; }
/// <summary>
/// 更新包的下载路径, 这里将需要更新的文件压缩成zip文件
/// </summary>
publicstring downloadurl { get; set; }
/// <summary>
/// 更新日志
/// </summary>
publicstring changelog { get; set; }
/// <summary>
/// 是否是强制更新
/// </summary>
publicbool mandatory { get; set; }
}
3、窗体界面截图(进度条展示更新状态)
4、完整核心代码以下为完整的 WinForm 自动更新窗体逻辑代码:
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Windows.Forms;
namespaceOnlineUpdateDemo
{
publicpartialclassForm1 : Form
{
privatestring NowVersion;
privatestring InstallPath;
privatestring StartPath;
private UpdateEntity updateEntity = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// 获取当前版本号
NowVersion = ConfigurationManager.AppSettings["Version"];
// 获取解压目录
InstallPath = ConfigurationManager.AppSettings["InstallPath"];
// 获取启动路径
StartPath = ConfigurationManager.AppSettings["StartPath"];
// 获取更新配置
string UpdateEntityUrl = ConfigurationManager.AppSettings["UpdateEntityUrl"];
string updateJson = GetHtml(UpdateEntityUrl);
updateEntity = JsonConvert.DeserializeObject<UpdateEntity>(updateJson);
// 显示版本信息
this.label_NowVersion.Text = NowVersion;
this.label_NextVersion.Text = updateEntity.latestversion;
if (string.Compare(updateEntity.latestversion, NowVersion) > 0)
{
label_message.Text = "有新版本";
if (updateEntity.mandatory)
{
Btn_Next_Click(null, null);
}
}
else
{
label_message.Text = "没有更新版本了";
if (updateEntity.mandatory)
{
ShowLogin(); // 启动主程序
}
}
}
private void Btn_Next_Click(object sender, EventArgs e)
{
try
{
WebClient wc = new WebClient();
wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
Uri uri = new Uri(updateEntity.downloadurl);
if (!Directory.Exists(InstallPath))
{
Directory.CreateDirectory(InstallPath);
}
wc.DownloadFileAsync(uri, InstallPath + "FD3_WcsClient.zip");
}
catch (Exception er)
{
MessageBox.Show("下载失败:" + er.Message);
}
}
private void Btn_Cancel_Click(object sender, EventArgs e)
{
this.Close();
this.Dispose();
}
private void Btn_Login_Click(object sender, EventArgs e)
{
ShowLogin();
}
private void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Action act = () =>
{
this.progressBar1.Value = e.ProgressPercentage;
label_message.Text = "正在下载.....";
};
this.Invoke(act);
if (e.ProgressPercentage == 100)
{
label_message.Text = "正在解压.....";
try
{
string zipFileName = InstallPath + "FD3_WcsClient.zip";
string targetDirectory = InstallPath;
var result = Compress(targetDirectory, zipFileName, "");
if (result == "Success!")
{
progressBar1.Value = 100;
this.label_message.Text = "更新完成";
// 更新配置文件中的版本号
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["Version"].Value = updateEntity.latestversion;
config.Save(ConfigurationSaveMode.Full);
ConfigurationManager.RefreshSection("appSettings");
ShowLogin(); // 启动主程序
}
else
{
MessageBox.Show("更新失败:" + result);
this.Close();
}
}
catch (Exception ex)
{
MessageBox.Show("解压失败:" + ex.Message);
}
}
}
public string GetHtml(string url)
{
try
{
using (WebClient client = new WebClient())
{
byte[] data = client.DownloadData(url);
using (var ms = new MemoryStream(data))
using (var sr = new StreamReader(ms, Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
catch (Exception)
{
MessageBox.Show("网络连接失败");
return"";
}
}
public string Compress(string DirPath, string ZipPath, string ZipPWD)
{
string state = "Fail!";
if (!Directory.Exists(DirPath) || !File.Exists(ZipPath)) return state;
try
{
FastZip fastZip = new FastZip();
fastZip.ExtractZip(ZipPath, DirPath, ZipPWD);
state = "Success!";
}
catch (Exception ex)
{
state += ", " + ex.Message;
}
return state;
}
public void ShowLogin()
{
System.Diagnostics.Process.Start(StartPath);
GC.Collect();
Application.Exit();
}
}
}
5、配置文件 App.config 内容
<configuration>
<appSettings>
<!-- 当前版本号 -->
<add key="Version" value="3.0.2" />
<!-- 版本配置文件地址 -->
<add key="UpdateEntityUrl" value="http://192.168.31.2:8091/updates.json" />
<!-- 解压目录 -->
<add key="InstallPath" value="D:/WCS_Project/" />
<!-- 主程序启动路径 -->
<add key="StartPath" value="D:/WCS_Project/福鼎物流控制系统.exe" />
</appSettings>
</configuration>
总结
本文详细介绍了如何使用 C# 编写一个 WinForm 自动更新程序。通过比对远程 JSON 中的版本号,决定是否下载并解压 ZIP 包进行更新。整个过程无需依赖 ClickOnce,更加灵活可靠,适合需要自定义更新策略的项目。
方案可扩展性强,未来可加入差分更新、断点续传、数字签名验证等高级功能,进一步提升系统的稳定性和安全性。
关键词
#WinForm、#自动更新、C#、#在线升级、#版本控制、#ZIP解压、#JSON配置、#IIS部署、ICSharpCode.SharpZipLib、#FastZip
阅读原文:原文链接
该文章在 2025/7/29 22:45:27 编辑过