C# WinForm实现自动更新程序的方法详解

作者:Csharp小记 时间:2021-12-12 16:19:54 

这一篇就着重写一下客户端的代码,客户端主要实现的有:启动后检测本地的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));
       }

实现效果

C# WinForm实现自动更新程序的方法详解

代码解析

主要注释已经在代码中标注了,由于基本都是在线程中进行操作的,所以在更新UI的时候封装了UpdateUI方法,然后就是加了一个定时器实现图标的旋转。关于CopyRun(更新自己)方法,就是用来更新自动更新程序本身的,即运行之前复制一份本身,然后启动复制的程序,否则本身正在运行的时候是不能更新自己的。

来源:https://mp.weixin.qq.com/s/LlW9YtAGVoHGyusJt2t_qA

标签:C#,WinForm,更新,程序
0
投稿

猜你喜欢

  • Java ThreadPoolExecutor线程池有关介绍

    2022-11-21 02:03:20
  • C#操作windows系统进程的方法

    2023-09-01 03:13:26
  • 修改Android FloatingActionButton的title的文字颜色及背景颜色实例详解

    2021-08-21 21:18:44
  • Java 中的 xx ≠ null 是什么新语法

    2022-09-10 20:51:13
  • Java实现商城订单超时取消功能

    2023-09-17 06:20:42
  • Android中BaseAdapter的用法分析与理解

    2022-07-23 19:35:37
  • c# 如何实现代码生成器

    2023-11-13 19:23:35
  • Java使用wait和notify实现线程之间的通信

    2022-07-20 16:05:02
  • C#实现的24点游戏实例详解

    2023-03-01 16:38:51
  • Android自定义EditText右侧带图片控件

    2023-08-30 03:09:47
  • Android图片添加水印图片并把图片保存到文件存储的实现代码

    2022-06-03 21:48:54
  • Android实现将已发送的短信写入短信数据库的方法

    2021-08-20 05:40:57
  • Android RecyclerView实现点击条目删除

    2022-09-09 11:20:38
  • java实现短信通信的完整教程

    2023-03-23 23:03:40
  • C#多线程异步执行和跨线程访问控件Helper

    2022-02-09 12:24:32
  • Java读文件修改默认换行符的实现

    2023-11-29 08:24:32
  • 关于HashMap相同key累加value的问题

    2022-02-13 13:32:18
  • springmvc无法访问/WEB-INF/views下的jsp的解决方法

    2023-11-23 15:15:38
  • 通过实例解析java过滤器和拦截器的区别

    2022-12-21 05:02:45
  • Spring Cloud整合XXL-Job的示例代码

    2021-06-09 06:01:59
  • asp之家 软件编程 m.aspxhome.com