zoukankan      html  css  js  c++  java
  • 一个升级程序

    一个升级程序

    日前收到一个小任务,要做一个通用的在线升级程序。更新的内容包括一些dll或exe或、配置文件。升级的大致流程是这样的,从服务器获取一个更新的配置文件,经过核对后如有新的更新,则会从服务器下载相应的文件更新到被升级的程序目录下。如果被升级的程序在升级之前已经启动,程序则会强制关闭它,待到升级完成之后重新启动相应的程序。在升级之前程序会自动备份一次,以防升级失败造成程序不能运行。

    首先来的是数据实体

    复制代码
     1     public class FileENT
     2     {
     3         public string FileFullName { get; set; }
     4 
     5         public string Src { get; set; }
     6 
     7         public string Version { get; set; }
     8 
     9         public int Size { get; set; }
    10 
    11         public UpdateOption Option { get; set; }
    12     }
    13 
    14     public enum UpdateOption
    15     {
    16         add,
    17         del
    18     }
    复制代码

    下面这个类时程序的一些参数,包括了系统的配置参数,为了程序能通用一点,就加了配置上去。

    复制代码
     1     public class AppParameter
     2     {
     3         /// <summary>
     4         /// 备份路径
     5         /// </summary>
     6         public static string BackupPath = ConfigurationManager.AppSettings["backupPath"];
     7 
     8         /// <summary>
     9         /// 更新的URL
    10         /// </summary>
    11         public static string ServerURL = ConfigurationManager.AppSettings["serverURL"];
    12 
    13         /// <summary>
    14         /// 本地更新文件全名
    15         /// </summary>
    16         public static string LocalUPdateConfig = ConfigurationManager.AppSettings["localUPdateConfig"];
    17 
    18         /// <summary>
    19         /// 版本号
    20         /// </summary>
    21         public static string Version = ConfigurationManager.AppSettings["version"];
    22 
    23         /// <summary>
    24         /// 更新程序路径
    25         /// </summary>
    26         public static string LocalPath = AppDomain.CurrentDomain.BaseDirectory;
    27 
    28         /// <summary>
    29         /// 主程序路径
    30         /// </summary>
    31         public static string MainPath = ConfigurationManager.AppSettings["mainPath"];
    32 
    33         /// <summary>
    34         /// 有否启动主程序
    35         /// </summary>
    36         public static bool IsRunning = false;
    37 
    38         /// <summary>
    39         /// 主程序名
    40         /// </summary>
    41         public static List<string> AppNames = ConfigurationManager.AppSettings["appName"].Split(';').ToList();
    42     }
    复制代码

    接着就介绍程序的代码

    程序是用窗体来实现的,下面三个是窗体新添加的三个字段

    1         private bool isDelete=true; //是否要删除升级配置
    2         private bool runningLock = false;//是否正在升级
    3         private Thread thread; //升级的线程

    载入窗体时需要检查更新,如果没有更新就提示”暂时无更新”;如果有更新的则先进行备份,备份失败的话提示错误退出更新。

    复制代码
     1             if (CheckUpdate())
     2             {
     3                 if (!Backup())
     4                 {
     5                     MessageBox.Show("备份失败!");
     6                     btnStart.Enabled = false;
     7                     isDelete = true;
     8                     return;
     9                 }
    10 
    11             }
    12             else
    13             {
    14                 MessageBox.Show("暂时无更新");
    15                 this.btnFinish.Enabled = true;
    16                 this.btnStart.Enabled = false;
    17                 isDelete = false;
    18                 this.Close();
    19             }
    复制代码

    在这些操作之前还要检测被更新程序有否启动,有则将其关闭。

    复制代码
     1             List<string> processNames = new List<string>();
     2             string mainPro = string.Empty;
     3             processNames.AddRange(AppParameter.AppNames);
     4             for (int i = 0; i < processNames.Count; i++)
     5             {
     6                 processNames[i] = processNames[i].Substring(processNames[i].LastIndexOf('\\')).Trim('\\').Replace(".exe", "");
     7             }
     8             mainPro = processNames.FirstOrDefault();
     9             AppParameter.IsRunning = ProcessHelper.IsRunningProcess(mainPro);
    10             if (AppParameter.IsRunning)
    11             {
    12                 MessageBox.Show("此操作需要关闭要更新的程序,请保存相关数据按确定继续", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    13                 foreach (string item in processNames)
    14                     ProcessHelper.CloseProcess(item);
    15             }
    复制代码

    另外上面用到的CheckUpdate( )和Backup( )方法如下

    复制代码
     1           /// <summary>
     2         /// 检查更新 有则提示用户 确认后下载新的更新配置
     3         /// </summary>
     4         /// <returns>用户确认信息</returns>
     5         public static bool CheckUpdate()
     6         {
     7             bool result = false;
     8 
     9             HttpHelper.DownLoadFile(AppParameter.ServerURL, AppParameter.LocalPath + "temp_config.xml");
    10             if (!File.Exists(AppParameter.LocalUPdateConfig))
    11                 result = true;
    12             else
    13             {
    14                 long localSize = new FileInfo(AppParameter.LocalUPdateConfig).Length;
    15                 long tempSize = new FileInfo(AppParameter.LocalPath + "temp_config.xml").Length;
    16 
    17                 if (localSize >= tempSize) result = false;
    18 
    19                 else result = true;
    20             }
    21 
    22             if (result)
    23             {
    24                 if (File.Exists(AppParameter.LocalUPdateConfig)) File.Delete(AppParameter.LocalUPdateConfig);
    25                 File.Copy(AppParameter.LocalPath + "temp_config.xml", AppParameter.LocalUPdateConfig);
    26             }
    27             else
    28                 result = false;
    29 
    30             File.Delete(AppParameter.LocalPath + "temp_config.xml");
    31             return result;
    32         }
    33 
    34         /// <summary>
    35         /// 备份
    36         /// </summary>
    37         public static bool Backup()
    38         {
    39             string sourcePath = Path.Combine(AppParameter.BackupPath, DateTime.Now.ToString("yyyy-MM-dd HH_mm_ss")+"_v_"+AppParameter.Version + ".rar");
    40             return ZipHelper.Zip(AppParameter.MainPath.Trim() , sourcePath);
    41         }
    复制代码

    下面则是更新的部分的代码,使用了多线程,出于两方面的考虑,一是进度条需要;二是如果用单线程,万一更新文件下载时间过长或者更新内容过多,界面会卡死。

    复制代码
      1         /// <summary>
      2         /// 更新
      3         /// </summary>
      4         public void UpdateApp()
      5         {
      6             int successCount = 0;
      7             int failCount = 0;
      8             int itemIndex = 0;
      9             List<FileENT> list = ConfigHelper.GetUpdateList();
     10             if (list.Count == 0)
     11             {
     12                 MessageBox.Show("版本已是最新", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
     13                 this.btnFinish.Enabled = true;
     14                 this.btnStart.Enabled = false;
     15                 isDelete = false;
     16                 this.Close();
     17                 return;
     18             }
     19             thread = new Thread(new ThreadStart(delegate
     20             {
     21                 #region thread method
     22 
     23                 FileENT ent = null;
     24 
     25                 while (true)
     26                 {
     27                     lock (this)
     28                     {
     29                         if (itemIndex >= list.Count)
     30                             break;
     31                         ent = list[itemIndex];
     32 
     33 
     34                         string msg = string.Empty;
     35                         if (ExecUpdateItem(ent))
     36                         {
     37                             msg = ent.FileFullName + "更新成功";
     38                             successCount++;
     39                         }
     40                         else
     41                         {
     42                             msg = ent.FileFullName + "更新失败";
     43                             failCount++;
     44                         }
     45 
     46                         if (this.InvokeRequired)
     47                         {
     48                             this.Invoke((Action)delegate()
     49                             {
     50                                 listBox1.Items.Add(msg);
     51                                 int val = (int)Math.Ceiling(1f / list.Count * 100);
     52                                 progressBar1.Value = progressBar1.Value + val > 100 ? 100 : progressBar1.Value + val;
     53                             });
     54                         }
     55 
     56 
     57                         itemIndex++;
     58                         if (successCount + failCount == list.Count && this.InvokeRequired)
     59                         {
     60                             string finishMessage = string.Empty;
     61                             if (this.InvokeRequired)
     62                             {
     63                                 this.Invoke((Action)delegate()
     64                                 {
     65                                     btnFinish.Enabled = true;
     66                                 });
     67                             }
     68                             isDelete = failCount != 0;
     69                             if (!isDelete)
     70                             {
     71                                 AppParameter.Version = list.Last().Version;
     72                                 ConfigHelper.UpdateAppConfig("version", AppParameter.Version);
     73                                 finishMessage = "升级完成,程序已成功升级到" + AppParameter.Version;
     74                             }
     75                             else
     76                                 finishMessage = "升级完成,但不成功";
     77                             MessageBox.Show(finishMessage, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
     78                             runningLock = false;
     79                         }
     80                     }
     81                 }
     82                 #endregion
     83             }));
     84             runningLock = true;
     85             thread.Start();
     86         }
     87 
     88         /// <summary>
     89         /// 执行单个更新
     90         /// </summary>
     91         /// <param name="ent"></param>
     92         /// <returns></returns>
     93         public bool ExecUpdateItem(FileENT ent)
     94         {
     95             bool result = true;
     96 
     97             try
     98             {
     99 
    100                 if (ent.Option == UpdateOption.del)
    101                     File.Delete(ent.FileFullName);
    102                 else
    103                     HttpHelper.DownLoadFile(ent.Src, Path.Combine(AppParameter.MainPath, ent.FileFullName));
    104             }
    105             catch { result = false; }
    106             return result;
    107         }
    复制代码

      只开了一个子线程,原本是开了5个子线程的,但是考虑到多线程会导致下载文件的顺序不确定,还是用回单线程会比较安全。线程是用了窗体实例里的thread字段,在开启线程时还用到runningLock标识字段,表示当前正在更新。当正在更新程序时关闭窗口,则要提问用户是否结束更新,若用户选择了是则要结束那个更新进程thread了,下面则是窗口关闭的时间FormClosing事件的方法。

     

    复制代码
     1             if (runningLock )
     2             {
     3                 if (MessageBox.Show("升级还在进行中,中断升级会导致程序不可用,是否中断",
     4                           "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk) == DialogResult.Yes)
     5                 {
     6                     if (thread != null) thread.Abort();
     7                     isDelete = true;
     8                     AppParameter.IsRunning = false;
     9                 }
    10                 else 
    11                 {
    12                     e.Cancel = true;
    13                     return;
    14                 }
    15             }
    16             if (isDelete) File.Delete(AppParameter.LocalUPdateConfig);
    复制代码

    在这里还要做另一件事,就是把之前关了的程序重新启动。

    复制代码
    1             try
    2             {
    3                 if (AppParameter.IsRunning) ProcessHelper.StartProcess(AppParameter.AppNames.First());
    4             }
    5             catch (Exception ex)
    6             {
    7 
    8                 MessageBox.Show("程序无法启动!" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
    9             }
    复制代码

    在这里展示一下更新的界面。挺丑的,别笑哈。

    更新程序的配置信息如下

    复制代码
    1   <appSettings>
    2     <add key="backupPath" value="C:\Users\Administrator\Desktop\temp\backup"/>
    3     <add key="serverURL" value="http://localhost:8015/updateconfig.xml"/>
    4     <add key="localUPdateConfig" value="E:\HopeGi\Code\MyUpdate\MyUpdate\bin\Debug\updateconfig.xml"/>
    5     <add key="version" value="2"/>
    6     <add key="mainPath" value="C:\Users\Administrator\Desktop\temp\main"/>
    7     <add key="appName" value="D:\test.exe"/>
    8   </appSettings>
    复制代码

     这里有个链接下代码MyUpdate.rar

     
     
    分类: C#
  • 相关阅读:
    MongoDB,无模式文档型数据库简介
    数据说话:怎样的程序员最抢手?
    猛醒:也许我们一生追求的都错了!
    中国风电生产监控平台界面
    如何跟着趋势去赚钱
    2015年最好的员工心态培养 -- 我们需要把简单的事情做到极致
    什么是程序员的核心竞争力?
    第一篇 技术选型
    .net core 读取配置文件
    .net core nlog记录日志
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2941380.html
Copyright © 2011-2022 走看看