一个升级程序
一个升级程序
日前收到一个小任务,要做一个通用的在线升级程序。更新的内容包括一些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#
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息