基于C#实现的软件自动更新程序,之前在网上搜集了两款软件自动更新程序,在实际应用中,对部分BUG进行修复,添加+完善一些功能。这里,推荐给大家。代码完全开放,可以根据实际应用场景,作调整。
先来看看第一款软件自动更新程序的效果图吧:
它的原理非常简单,它是根据文件的日期,进行比较,然后在服务端修改生成一个XML文件,软件在启动的时候,验证是否更新,如果需要更新,则终止主程序,启动自动更新程序,更新成功后,再启动主程序。
它的大体结构是这样的:
1、需要一个网站,把需要更新的文件都存放在这个网站下的UpdateServer文件夹,它自身提供了一个生成更新配置文件的UpdateListBuilder.exe工具,只需点击执行exe,便可在服务端生成更新所需的xml配置文件。格式如下:
\K3SP.exe \conf\menu.xml
这里的K3SP.exe是主程序exe。
2、主程序添加如下代码:
////// 检测更新 /// private void checkUpdate() { strUpdateURL = getConfigValue(strUpdateXmlPath, "Url"); //读取本地xml中配置的更新服务器的URL string strLastUpdateDate = getConfigValue(strUpdateXmlPath, "UpDate"); //读取本地xml中配置的最近一次更新日期 if (strUpdateURL.Substring(strUpdateURL.Length - 1) != "/") //如果配置的xml中URL没带最后一个反斜杠,则加一下,防止出错 strUpdateURL += "/"; strTheUpdateDate = getTheLastUpdateTime(strUpdateURL); //获得更新服务器端的此次更新日期 if (!String.IsNullOrEmpty(strTheUpdateDate) && !String.IsNullOrEmpty(strLastUpdateDate)) //日期都不为空 { if (DateTime.Compare( Convert.ToDateTime(strTheUpdateDate, CultureInfo.InvariantCulture), Convert.ToDateTime(strLastUpdateDate, CultureInfo.InvariantCulture)) > 0) //字符转日期,并比较日期大小 { //本次更新日期 大于 最近一次更新日期,开始更新 try { if (new K3SP.lib.ClassCheckProIsRun().checkProcess(strUpdaterProFileName, strUpdaterProPath)) { classMsg.messageInfoBox("更新程序" + strUpdaterProFileName + "已打开!"); } else { Process.Start(strUpdaterProPath); } } catch (Win32Exception ex) { classMsg.messageInfoBox(ex.Message); //主程序未更新成功或者被误删掉,再更新一遍 } Application.Exit(); //退出主程序 } } }
在窗体加载事件,调用checkUpdate方法进行检测更新。
再来看看第二种自动更新的效果吧:
该工具自身是英文的,在此基础上,进行了部分汉化,又增加了AutoUpdateBuilder自动生成更新配置文件的程序,方便我们在服务端维护。
这里,我做了个简单的demo程序。为使用改程序,我还新加了一个AutoUpdaterTool的工具,来进行软件的升级更新。
先来,看看AutoUpdaterTool都做了些什么?
////// 应用程序的主入口点。 /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); //Application.Run(new Form1()); #region check and download new version program bool bHasError = false; IAutoUpdater autoUpdater = new KnightsWarriorAutoupdater.AutoUpdater(); //主程序执行路径 string strExePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AutoUpdaterDemo.exe"); try { if (new ClassCheckProIsRun().checkProcessForProName(strExePath))//检测主程序是否执行 { MessageBox.Show(@"进程中检测到主程序正在运行,请先关闭才可更新。"); return; } autoUpdater.Update(); } catch (WebException exp) { MessageBox.Show(@"不能够找到指定的资源!"); bHasError = true; } catch (XmlException exp) { bHasError = true; MessageBox.Show(@"下载升级文件时发生错误!"); } catch (NotSupportedException exp) { bHasError = true; MessageBox.Show(@"更新地址配置错误"); } catch (ArgumentException exp) { bHasError = true; MessageBox.Show(@"下载升级文件时发生错误"); } catch (Exception exp) { bHasError = true; MessageBox.Show(@"升级过程中发生错误!"); } finally { if (bHasError == true) { try { autoUpdater.RollBack(); } catch (Exception) { //Log the message to your file or database } } } #endregion System.Diagnostics.Process.Start(strExePath, ((KnightsWarriorAutoupdater.AutoUpdater)autoUpdater).isCancel.ToString()); Application.Exit(); }
我建了一个Winform程序,不要窗口,检测更新程序,都写在了Main函数里。
接下来,在看看AutoUpdaterDemo示例程序的检测方法:
////// /// public bool IsCancelUpdate { get; set; } ////// 检测更新 /// /// /// private void Form1_Load(object sender, EventArgs e) { if (!IsCancelUpdate) CheckUpdate(); } ////// 自动更新程序路径 /// private readonly string autoUpdaterPath = AppDomain.CurrentDomain.BaseDirectory + "AutoUpdaterTool.exe"; ////// 检测更新程序 /// void CheckUpdate() { try { IAutoUpdater autoUpdater = new KnightsWarriorAutoupdater.AutoUpdater(); if (autoUpdater.CheckUpdate()) { if (new lib.ClassCheckProIsRun().checkProcess(autoUpdaterPath)) { MessageBox.Show(@"更新程序已经运行!"); return; } System.Diagnostics.Process.Start(autoUpdaterPath); Application.Exit(); } } catch (Exception exception) { MessageBox.Show(exception.Message); } }
注意:在这里,我给窗体新加了一个IsCancelUpdate的属性,这个为下面的取消更新做了铺垫。
这个AutoUpdaterDemo的入口函数,我添加了参数,来实现取消更新的功能。
////// 应用程序的主入口点。 /// [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var mainForm = new Form1(); if (args != null && args.Length > 0) mainForm.IsCancelUpdate = Convert.ToBoolean(args[0]); Application.Run(mainForm); }
妙处就在这里。为什么要这么做?因为自动更新程序和主程序,是两个进程,我们在主程序里检测更新,如果客户端取消更新,又会启动主程序,主程序又会检测更新,这样形成了一个死循环。
这个参数,是通过自动更新工具AutoUpdaterTool.exe来传递的,代码如下:
////// 应用程序的主入口点。 /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); //Application.Run(new Form1()); #region check and download new version program bool bHasError = false; IAutoUpdater autoUpdater = new KnightsWarriorAutoupdater.AutoUpdater(); //主程序执行路径 string strExePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AutoUpdaterDemo.exe"); try { if (new ClassCheckProIsRun().checkProcessForProName(strExePath))//检测主程序是否执行 { MessageBox.Show(@"进程中检测到主程序正在运行,请先关闭才可更新。"); return; } autoUpdater.Update(); } catch (WebException exp) { MessageBox.Show(@"不能够找到指定的资源!"); bHasError = true; } catch (XmlException exp) { bHasError = true; MessageBox.Show(@"下载升级文件时发生错误!"); } catch (NotSupportedException exp) { bHasError = true; MessageBox.Show(@"更新地址配置错误"); } catch (ArgumentException exp) { bHasError = true; MessageBox.Show(@"下载升级文件时发生错误"); } catch (Exception exp) { bHasError = true; MessageBox.Show(@"升级过程中发生错误!"); } finally { if (bHasError == true) { try { autoUpdater.RollBack(); } catch (Exception) { //Log the message to your file or database } } } #endregion System.Diagnostics.Process.Start(strExePath, ((KnightsWarriorAutoupdater.AutoUpdater)autoUpdater).isCancel.ToString()); Application.Exit();
就在这段代码的最后,启动这个进程的时候,我把取消isCancel这个属性传递给主程序。
isCancel是我为AutoUpdater对象添加的一个属性。
另外,我还为IAutoUpdater接口,公开了CheckUpdate方法,用来在AutoUpdaterDemo中,调用该方法,检测是否需要更新。
再来看看,为自动生成更新配置文件的程序。注意:要想支持文件夹的更新,需要简单的修改下代码即可。
using System;using System.Collections.Generic;using System.Configuration;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Xml;/****************************************************************************************************************** * * * 说 明: 生成自动更新配置文件(版本:Version1.0.0) * 作 者:李朝强 * 日 期:2015/05/19 * 修 改: * 参 考:http://my.oschina.net/lichaoqiang/ * 备 注:暂无... * * * ***************************************************************************************************************/namespace AutoUpdaterBuilder{ class Program { ////// /// /// static void Main(string[] args) { Console.Title = "更新文档生成工具!"; try { //远程服务器URL string strServerUrl = System.Configuration.ConfigurationManager.AppSettings["ServerUrl"]; //创建Xml文档 XmlDocument xml = new XmlDocument(); var declaration = xml.CreateXmlDeclaration("1.0", "utf-8", null); xml.AppendChild(declaration); var root = xml.CreateElement("UpdateFileList");//根节点 string strBaseDirectory = AppDomain.CurrentDomain.BaseDirectory;//应用程序根目录 string[] strFiles = Directory.GetFiles(strBaseDirectory);//获取目录下所有文件 //版本信息 Version v = new Version(System.Configuration.ConfigurationManager.AppSettings["Version"]); Version theNewVersion = new Version(v.Major, v.Minor, v.Build, v.Revision + 1);//生成新的版本号 ListupdataFileInfos = new List ();//更新文件列表 //循环更新文件 foreach (string strfile in strFiles) { if (strfile.Contains("AutoUpdaterBuilder.exe") || strfile.EndsWith(".xml") || strfile.EndsWith(".config")) continue; //文件名 FileInfo file = new FileInfo(strfile); var localFile = xml.CreateElement("LocalFile"); localFile.SetAttribute("path", file.Name); localFile.SetAttribute("url", string.Format("{0}/{1}", strServerUrl, file.Name)); localFile.SetAttribute("lastver", theNewVersion.ToString()); localFile.SetAttribute("size", file.Length.ToString()); localFile.SetAttribute("needRestart", "false"); root.AppendChild(localFile); updataFileInfos.Add(file); ShowMessge("正在创建文件“{0}”的更新配置", file.Name); } xml.AppendChild(root); string strSaveToPath = System.IO.Path.Combine(strBaseDirectory, "AutoupdateService.xml");//配置文件保存路径 xml.Save(strSaveToPath); //更新日志 string[] strUpdateLogs = Array.ConvertAll(updataFileInfos.ToArray(), (t) => string.Format("[{0}] {1}", t.LastWriteTime.ToString("yyyy/MM/dd HH:mm:ss"), t.Name)); File.WriteAllLines(Path.Combine(strBaseDirectory, "UpdateLog.txt"), strUpdateLogs); //修改配置文件 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = System.IO.Path.Combine(strBaseDirectory, "AutoUpdaterBuilder.exe.config"); System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); configuration.AppSettings.Settings["Version"].Value = theNewVersion.ToString(); configuration.Save(ConfigurationSaveMode.Modified); Console.WriteLine("更新文件生成成功,2s后退出!"); System.Threading.Thread.Sleep(2000);//线程休眠2s } catch (Exception exception) { Console.WriteLine(exception.Message); Console.ReadLine(); } } /// /// /// /// /// static void ShowMessge(string format, params object[] args) { Console.WriteLine(format, arg: args); } ////// /// /// /// static void Log(string format, params object[] args) { } }}
生成后的xml文件
怎样部署?
很简单,首先,我们只需要建一个网站,把AutoUpdaterBuilder.exe和AutoUpdaterBuilder.exe.config文件放进去,然后再把需要更新的文件,放入改文件夹下。
以上是AutoUpdaterBuilder.exe.config配置文件。
其次,在主程序上添加对AutoUpdater.dll的引用,主要是为了调用它的检测更新方法。
今天就写到这吧,感兴趣的朋友,可以亲手尝试下。