C#软件自动更新程序

原创
2017/03/24 21:03
阅读数 6.1K

 基于C#实现的软件自动更新程序,之前在网上搜集了两款软件自动更新程序,在实际应用中,对部分BUG进行修复,添加+完善一些功能。这里,推荐给大家。代码完全开放,可以根据实际应用场景,作调整。

先来看看第一款软件自动更新程序的效果图吧:

软件自动更新程序1

它的原理非常简单,它是根据文件的日期,进行比较,然后在服务端修改生成一个XML文件,软件在启动的时候,验证是否更新,如果需要更新,则终止主程序,启动自动更新程序,更新成功后,再启动主程序。

它的大体结构是这样的:

1、需要一个网站,把需要更新的文件都存放在这个网站下的UpdateServer文件夹,它自身提供了一个生成更新配置文件的UpdateListBuilder.exe工具,只需点击执行exe,便可在服务端生成更新所需的xml配置文件。格式如下:

<AutoUpdater>
  <UpdateInfo>
    <UpdateTime Date="2014-2-21" />
    <Version Num="1.0.0.0" />
    <UpdateSize Size="32046" />
  </UpdateInfo>
  <UpdateFileList>
    <UpdateFile>\K3SP.exe</UpdateFile>
    <UpdateFile>\conf\menu.xml</UpdateFile>
  </UpdateFileList>
</AutoUpdater>

这里的K3SP.exe是主程序exe。

2、主程序添加如下代码:

        /// <summary>
        /// 检测更新
        /// </summary>
        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都做了些什么?

        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [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示例程序的检测方法:

        /// <summary>
        /// 
        /// </summary>
        public bool IsCancelUpdate { get; set; }

        /// <summary>
        /// 检测更新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            if (!IsCancelUpdate) CheckUpdate();
        }

        /// <summary>
        /// 自动更新程序路径
        /// </summary>
        private readonly string autoUpdaterPath = AppDomain.CurrentDomain.BaseDirectory + "AutoUpdaterTool.exe";

        /// <summary>
        /// 检测更新程序
        /// </summary>
        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的入口函数,我添加了参数,来实现取消更新的功能。

        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [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来传递的,代码如下:

  /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [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
    {


        /// <summary>
        /// <![CDATA[主程序入口]]>
        /// </summary>
        /// <param name="args"></param>
        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);//生成新的版本号

                List<FileInfo> updataFileInfos = new List<FileInfo>();//更新文件列表

                //循环更新文件
                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();
            }
        }


        /// <summary>
        /// <![CDATA[输出消息]]>
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        static void ShowMessge(string format, params object[] args)
        {
            Console.WriteLine(format, arg: args);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        static void Log(string format, params object[] args)
        {

        }
    }
}

生成后的xml文件

<?xml version="1.0" encoding="utf-8"?>
<UpdateFileList>
  <LocalFile path="AutoUpdater.dll" url="http://localhost:8833//AutoUpdater.dll" lastver="1.0.0.8" size="43520" needRestart="false" />
  <LocalFile path="AutoUpdaterDemo.exe" url="http://localhost:8833//AutoUpdaterDemo.exe" lastver="1.0.0.8" size="9728" needRestart="false" />
  <LocalFile path="phpStudy.rar" url="http://localhost:8833//phpStudy.rar" lastver="1.0.0.8" size="45633237" needRestart="false" />
  <LocalFile path="update.txt" url="http://localhost:8833//update.txt" lastver="1.0.0.8" size="45" needRestart="false" />
  <LocalFile path="新增测试文件.txt" url="http://localhost:8833//新增测试文件.txt" lastver="1.0.0.8" size="0" needRestart="false" />
</UpdateFileList>

怎样部署?

很简单,首先,我们只需要建一个网站,把AutoUpdaterBuilder.exe和AutoUpdaterBuilder.exe.config文件放进去,然后再把需要更新的文件,放入改文件夹下。

  <appSettings>
    <remove key="ServerUrl"/>
    <remove key="Version"/>
    <add key="ServerUrl" value="http://localhost:8833/"/>
    <add key="Version" value="1.0.0.1"/>
  </appSettings>

以上是AutoUpdaterBuilder.exe.config配置文件。

其次,在主程序上添加对AutoUpdater.dll的引用,主要是为了调用它的检测更新方法。

今天就写到这吧,感兴趣的朋友,可以亲手尝试下。

展开阅读全文
加载中

作者的其它热门文章

打赏
2
4 收藏
分享
打赏
2 评论
4 收藏
2
分享
返回顶部
顶部