C# WinForm实现自动更新程序的方法详解
作者:Csharp小记 发布时间:2021-12-12 16:19:54
标签:C#,WinForm,更新,程序
这一篇就着重写一下客户端的代码,客户端主要实现的有:启动后检测本地的xml文件,然后发送到服务器获取需要更新的文件以及版本列表。循环下载。下载成功后,备份原始文件->复制到主目录(若失败进行回滚)->修改本地xml文件,更新完成后打开主程序。
开发环境
.NET Core 3.1
开发工具
Visual Studio 2019
实现代码
/// <summary>
/// 更新自己
/// </summary>
private void CopyRun() {
string runPath = Process.GetCurrentProcess().MainModule.FileName;
string runName = Path.GetFileName(runPath);
if(!runName.StartsWith("_")) {
string copyPath = runPath.Replace(runName, "_" + runName);
byte[] bytes;
using(FileStream fileStream = new FileStream(runPath, FileMode.Open, FileAccess.Read)) {
bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, bytes.Length);
}
using(FileStream fileStream = new FileStream(copyPath, FileMode.Create, FileAccess.Write)) {
fileStream.Write(bytes);
}
ProcessStartInfo info = new ProcessStartInfo(copyPath, _runApp);
Process.Start(info);
Environment.Exit(0);
}
}
/// <summary>
/// 更新UI
/// </summary>
/// <param name="actoin"></param>
private void UpdateUI(Action actoin) {
if(this.InvokeRequired) {
this.BeginInvoke(actoin);
}
else {
actoin();
}
}
/// <summary>
/// 获取本地更新地址
/// </summary>
/// <returns></returns>
private string GetUpdateUrl() {
XElement xele = XElement.Load(updateXml);
string url = xele.Element("url").Value;
return url;
}
/// <summary>
/// 获取本地更新文件
/// </summary>
/// <returns></returns>
private string GetUpdateFiles() {
XDocument xdoc = XDocument.Load(updateXml);
var files = from f in xdoc.Root.Element("files").Elements() select new { name = f.Attribute("name").Value, version = f.Attribute("version").Value };
return JsonConvert.SerializeObject(files);
}
/// <summary>
/// 更新完成后修改版本文件
/// </summary>
/// <param name="list"></param>
private void UpdateXml(List<UpdateModel> list) {
XDocument xdoc = XDocument.Load(updateXml);
foreach(var model in list) {
var ele_files = xdoc.Root.Element("files");
XElement xele = ele_files.Elements().FirstOrDefault(s => s.Attribute("name").Value == model.name);
if(xele != null) {
xele.SetAttributeValue("version", model.version);
}
else {
XElement addXele = new XElement("file");
addXele.SetAttributeValue("name", model.name);
addXele.SetAttributeValue("version", model.version);
ele_files.Add(addXele);
}
}
xdoc.Save(updateXml);
}
readonly string _runApp;
public Form_update(string runApp) {
InitializeComponent();
_runApp = runApp;
//CopyRun();
}
readonly string updateXml = Application.StartupPath + "UpdateList.xml";
private void btn_update_Click(object sender, EventArgs e) {
#region 设置ui
btn_update.Enabled = false;
label_status.Text = "正在更新";
#endregion
#region 初始化路径
string savePath = Application.StartupPath + "temp_update\\";
string backPath = Application.StartupPath + "temp_back\\";
if(Directory.Exists(savePath)) {
Directory.Delete(savePath, true);
}
Directory.CreateDirectory(savePath);
if(Directory.Exists(backPath)) {
Directory.Delete(backPath, true);
}
Directory.CreateDirectory(backPath);
#endregion
#region 图标旋转
int angle = 0;
Image img = pictureBox1.BackgroundImage;
System.Threading.Timer timer = new System.Threading.Timer(s => {
UpdateUI(() => {
angle = angle == 360 ? 0 : angle + 10;
pictureBox1.BackgroundImage = img.RotateImage(angle);
});
}, null, 0, 100);
#endregion
#region 下载更新
string url = GetUpdateUrl();
bool isSuccess = false;
Task.Run(() => {
try {
//获取下载列表
HttpResult httpResult = HttpUtil.HttpRequest(new HttpItem(url + "GetUpdateFiles", requestData: GetUpdateFiles()));
if(httpResult.Status) {
UpdateModel_Out output = JsonConvert.DeserializeObject<UpdateModel_Out>(httpResult.HttpStringData);
if(output.updateList.Count == 0) {
throw new Exception("当前已是最新版本");
}
UpdateUI(() => {
progressBar1.Maximum = output.updateList.Count + 1;
});
//循环下载文件
for(int i = 0; i < output.updateList.Count; i++) {
UpdateModel updateModel = output.updateList[i];
#region 进度条
UpdateUI(() => {
label_status.Text = $"正在更新第 {i + 1}/{output.updateList.Count} 个文件,文件名:{updateModel.name}";
progressBar1.Value = i + 1;
});
#endregion
#region 下载文件
httpResult = HttpUtil.HttpRequest(new HttpItem(url + "DownloadFile", requestData: JsonConvert.SerializeObject(updateModel)));
if(httpResult.Status) {
using(FileStream fileStream = new FileStream(savePath + updateModel.name, FileMode.Create)) {
fileStream.Write(httpResult.HttpByteData, 0, httpResult.HttpByteData.Length);
}
}
else {
throw new Exception(updateModel.name + "下载失败,请重试");
}
#endregion
Task.Delay(1000).Wait();
}
#region 备份
UpdateUI(() => {
label_status.Text = "正在备份";
});
try {
File.Copy(updateXml, backPath + "UpdateList.xml");
foreach(var file in output.updateList) {
if(File.Exists(Application.StartupPath + file.name)) {
File.Copy(Application.StartupPath + file.name, backPath + file.name, true);
}
}
}
catch { }
#endregion
#region 完成更新
UpdateUI(() => {
label_status.Text = "正在完成更新";
progressBar1.Value = progressBar1.Maximum;
});
string[] files = Directory.GetFiles(savePath);
try {
#region 更新成功
foreach(string file in files) {
File.Copy(file, Application.StartupPath + Path.GetFileName(file), true);
}
Directory.Delete(savePath, true);
#region 保存最新版本
UpdateXml(output.updateList);
isSuccess = true;
UpdateUI(() => {
label_status.Text = "更新完成";
});
#endregion
#endregion
}
catch {
#region 失败回滚
UpdateUI(() => {
label_status.Text = "更新失败,正在回滚";
});
string[] files_back = Directory.GetFiles(backPath);
foreach(string file in files_back) {
File.Copy(file, Application.StartupPath + Path.GetFileName(file), true);
}
File.Copy(backPath + "UpdateList.xml", updateXml, true);
UpdateUI(() => {
label_status.Text = "回滚完成";
});
return;
#endregion
}
#endregion
}
else {
throw new Exception("获取更新列表失败");
}
}
catch(Exception ex) {
UpdateUI(() => {
label_status.Text = "更新失败!" + ex.Message;
btn_update.Enabled = true;
});
}
finally {
UpdateUI(() => {
timer.Change(-1, -1);
pictureBox1.BackgroundImage = img;
if(isSuccess) {
if(File.Exists(_runApp)) {
if(MessageBox.Show("更新完成,是否打开程序", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
Process.Start(_runApp);
}
Environment.Exit(0);
}
}
});
}
});
#endregion
}
private void btn_close_Click(object sender, EventArgs e) {
Close();
}
private void panel1_Paint(object sender, PaintEventArgs e) {
Pen pen = new Pen(Color.LightGray);
e.Graphics.DrawLine(pen, new Point(36, 36), new Point(panel1.Width - 36, 36));
}
实现效果
代码解析
主要注释已经在代码中标注了,由于基本都是在线程中进行操作的,所以在更新UI的时候封装了UpdateUI方法,然后就是加了一个定时器实现图标的旋转。关于CopyRun(更新自己)方法,就是用来更新自动更新程序本身的,即运行之前复制一份本身,然后启动复制的程序,否则本身正在运行的时候是不能更新自己的。
来源:https://mp.weixin.qq.com/s/LlW9YtAGVoHGyusJt2t_qA
0
投稿
猜你喜欢
- 前言:IntelliJ IDEA 如果说IntelliJ IDEA是一款现代化智能开发工具的话,Eclipse则称得上是石器时代的东西了。其
- 1、servlet层package com.ycz.controller;import com.alibaba.fastjson.JSON;
- 1、配置maven环境变量,将maven安装的bin⽬录添加到path路径中(此电脑->属性->高级系统设置->环境变量-
- mybatis自动生成实体类、mapper文件、mapper.xml文件若采用mybatis框架,数据库新建表,手动编写的话,需要编写大量的
- 本文实例为大家分享了Java实现发送邮件并携带附件的具体代码,供大家参考,具体内容如下一、 邮件服务器与传输协议要在网络上实现邮件功能,必须
- jackson提供对LocalDate的支持SpringBoot默认使用jackson来进行json格式转换,我们在配置文件中加入如下配置可
- 调整数组顺序使奇数位于偶数之前1. 题目描述输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的
- 最近由于工作要求:前端采用vue开发,后端采用springboot开发,前后端分离开发,最后前端页面又整合到后端来。经历多次采坑,总结以下方
- 背景系统: SpringBoot开发的Web应用;ORM: JPA(Hibernate)接口功能简述: 根据实体类ID到数据库中查询实体信息
- 第一步:整合pom文件,在Spring Cloud中添加XXL-Job的依赖<!-- xxl-job-core --><d
- 问题遇到问题:在前后端分离跨域访问的项目中shiro进行权限拦截失效 (即使有正确权限的访问也会被拦截) 时造成302重定向错误等问题报错:
- 简介在java编写过程中,我们会使用到各种各样的表达式,在使用表达式的过程中,有哪些安全问题需要我们注意的呢?一起来看看吧。注意表达式的返回
- SpringMVC配置多个properties文件之通配符在springmvc中配置加载properties文件一般会在xml文件中配置如下
- 在装2个不同版本JDK时遇到了这个问题,在网上钩了一吧!查到一个讲解比较好的资料。一:要解决的问题我们在尝鲜 JDK1.5 的时候,相信不少
- 引言最新有一个winform项目使用的是DevExpress的控件,所以最近都在摸索使用这套控件,实在是佩服整套控件的强大,同时代码写起来也
- 我们经常需要对我们的开发的软件做各种测试, 软件对系统资源的使用情况更是不可少, 目前有多个监控工具, 相比JProfiler对系统资源尤其
- 本文实例讲述了Java Swing组件编程之JTable表格用法。分享给大家供大家参考,具体如下:表格是GUI编程中使用较多,但也是最麻烦的
- 概要本节要实现的是多表关联查询的简单demo。场景是根据id查询某商品分类信息,并展示该分类下的商品列表。一、Mysql测试数据新建表Cat
- 本文实例为大家分享了Java通过exchange协议发送邮件的具体代码,供大家参考,具体内容如下pom.xml 导入包<depende
- 话不多说,先上图 &n