Winform自动更新程序

本文采用自动更新策略为“访问Web站点获取最新版本,下载更新文件压缩包,并控制解压覆盖旧文件完成更新”。

注意事项:

因为是覆盖文件进行更新,故更新程序和主程序不能有关联

  • 不能引用相同的DLL文件(覆盖安装会告知文件占用无法覆盖)
  • 更新过程中主程序不能在启动状态(理由同上)

思路:

  • 部署一个Web站点,用于判断更新信息和下载更新文件
  • 主程序和更新程序启动时,访问Web站点获取版本号并和主程序版本进行对比,不一致则提醒更新

 

建立项目

项目资源如下图:

 

ps:主程序和更新程序不能共用更新处理类,故各放了1个。

主程序-Main代码:

using System;
using System.Windows.Forms;

namespace WinFormUpdaterDemo
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        private void Main_Load(object sender, EventArgs e)
        {
            label1.Text = "程序集版本:" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\n";
            //label1.Text += "文件版本:" + Application.ProductVersion.ToString() + "\n";
        }
    }
}
View Code

 

主程序-更新处理类SoftUpdate.cs:

using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Xml;

namespace WinFormUpdaterDemo
{
    /// <summary>  
    /// 更新完成触发的事件  
    /// </summary>  
    public delegate void UpdateState();
    /// <summary>  
    /// 程序更新  
    /// </summary> 
    public class SoftUpdate
    {
        private string download;
        private const string updateUrl = "http://127.0.0.1:7000/update.xml";//升级配置的XML文件地址  

        #region 构造函数
        public SoftUpdate() { }

        /// <summary>  
        /// 程序更新  
        /// </summary>  
        /// <param name="file">要更新的文件</param>  
        public SoftUpdate(string file, string softName)
        {
            this.LoadFile = file;
            this.SoftName = softName;
        }
        #endregion

        #region 属性
        private string loadFile;
        private string newVerson;
        private string softName;
        private bool isUpdate;

        /// <summary>  
        /// 或取是否需要更新  
        /// </summary>  
        public bool IsUpdate
        {
            get
            {
                checkUpdate();
                return isUpdate;
            }
        }

        /// <summary>  
        /// 要检查更新的文件  
        /// </summary>  
        public string LoadFile
        {
            get { return loadFile; }
            set { loadFile = value; }
        }

        /// <summary>  
        /// 程序集新版本  
        /// </summary>  
        public string NewVerson
        {
            get { return newVerson; }
        }

        /// <summary>  
        /// 升级的名称  
        /// </summary>  
        public string SoftName
        {
            get { return softName; }
            set { softName = value; }
        }

        #endregion

        /// <summary>  
        /// 检查是否需要更新  
        /// </summary>  
        public void checkUpdate()
        {
            try
            {
                WebClient wc = new WebClient();
                Stream stream = wc.OpenRead(updateUrl);
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(stream);
                XmlNode list = xmlDoc.SelectSingleNode("Update");
                foreach (XmlNode node in list)
                {
                    if (node.Name == "Soft" && node.Attributes["Name"].Value.ToLower() == SoftName.ToLower())
                    {
                        foreach (XmlNode xml in node)
                        {
                            if (xml.Name == "Verson")
                                newVerson = xml.InnerText;
                            else
                                download = xml.InnerText;
                        }
                    }
                }

                Version ver = new Version(newVerson);
                //读取版本信息(直接使用Assembly.Load会导致资源占用,故使用如下方法读取文件,解决资源占用问题)
                byte[] fileData = File.ReadAllBytes(loadFile);
                Version verson = Assembly.Load(fileData).GetName().Version;
                int tm = verson.CompareTo(ver);

                if (tm >= 0)
                    isUpdate = false;
                else
                    isUpdate = true;
            }
            catch (Exception ex)
            {
                throw new Exception("更新出现错误,请确认网络连接无误后重试!");
            }
        }

        /// <summary>  
        /// 获取要更新的文件  
        /// </summary>  
        /// <returns></returns>  
        public override string ToString()
        {
            return this.loadFile;
        }
    }
}
View Code

 

主程序入口Program.cs:

using System;
using System.Windows.Forms;

namespace WinFormUpdaterDemo
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            //检查更新
            if (checkUpdateLoad())
            {
                Application.Exit();
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Main());
        }

        public static bool checkUpdateLoad()
        {
            bool result = false;
            SoftUpdate app = new SoftUpdate(Application.ExecutablePath, "WinFormUpdaterDemo");
            try
            {
                if (app.IsUpdate && MessageBox.Show("检查到新版本,是否更新?", "版本检查", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    string path = Application.StartupPath;
                    System.Diagnostics.Process process = new System.Diagnostics.Process();
                    process.StartInfo.FileName = "AutoUpdater.exe";
                    process.StartInfo.WorkingDirectory = path;//要掉用得exe路径例如:"C:\windows";               
                    process.StartInfo.CreateNoWindow = true;
                    process.Start();

                    result = true;
                }
                else
                {
                    result = false;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                result = false;
            }
            return result;
        }
    }
}
View Code

 

PS:更新程序的生成“输出位置”最好设置在主程序里,这样就省去了每次生成后都要把升级程序文件复制到主程序目录里。

 更新程序Updater代码:

using ICSharpCode.SharpZipLib.Zip;
using System;
using System.IO;
using System.Windows.Forms;

namespace AutoUpdater
{
    public partial class Updater : Form
    {
        public Updater()
        {
            InitializeComponent();
        }

        private void Updater_Load(object sender, EventArgs e)
        {
            richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 清除历史遗留文件\n";
            //清除之前下载来的rar文件
            if (File.Exists(Application.StartupPath + "\\Update_NewVersion.zip"))
            {
                try
                {
                    File.Delete(Application.StartupPath + "\\Update_NewVersion.zip");
                }
                catch (Exception) { }
            }
            if (Directory.Exists(Application.StartupPath + "\\autoupload"))
            {
                try
                {
                    Directory.Delete(Application.StartupPath + "\\autoupload", true);
                }
                catch (Exception) { }
            }

            timer1.Interval = 200;
            timer1.Enabled = true;
            timer1.Start();
            richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 检查服务端是否有新版本程序\n";
            //检查服务端是否有新版本程序
            checkUpdate();
        }
        SoftUpdate app = new SoftUpdate(Application.StartupPath, "WinFormUpdaterDemo");
        /// <summary>
        /// 检查更新
        /// </summary>
        public void checkUpdate()
        {
            app.UpdateFinish += new UpdateState(app_UpdateFinish);
            try
            {
                if (app.IsUpdate)
                {
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 开始下载更新文件\n";
                    app.Update();
                }
                else
                {
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 未检测到新版本\n";
                    MessageBox.Show("未检测到新版本!");
                    Application.Exit();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 下载更新文件完成后解压文件并覆盖旧文件完成更新
        /// </summary>
        void app_UpdateFinish()
        {
            //解压下载后的文件
            string path = app.FinalZipName;
            if (File.Exists(path))
            {
                //后改的 先解压滤波zip植入ini然后再重新压缩
                string dirEcgPath = Application.StartupPath + "\\" + "autoupload\\";
                if (!Directory.Exists(dirEcgPath))
                {
                    Directory.CreateDirectory(dirEcgPath);
                }
                richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 解压下载后的文件:" + path + "\n"; ;
                //开始解压压缩包
                UnZIP(path, dirEcgPath);
                richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 解压成功\n"; ;

                try
                {
                    //复制新文件替换旧文件
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 更新新文件(替换旧文件)\n";
                    DirectoryInfo TheFolder = new DirectoryInfo(dirEcgPath);
                    foreach (FileInfo NextFile in TheFolder.GetFiles())
                    {
                        File.Copy(NextFile.FullName, Application.StartupPath + "\\" + NextFile.Name, true);
                    }
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 更新成功\n";
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 删除残留文件\n";
                    Directory.Delete(dirEcgPath, true);
                    File.Delete(path);
                    foreach (string delfilepath in app.deleteFiles)
                    {
                        string delfile = Application.StartupPath + "\\" + delfilepath;
                        if (delfilepath.Contains("."))
                        {
                            //删除文件
                            if (File.Exists(delfile))
                                File.Delete(delfile);
                        }
                        else
                        {
                            //删除目录
                            if (Directory.Exists(delfile))
                                Directory.Delete(delfile,true);
                        }
                    }
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 删除成功\n";
                    //覆盖完成 重新启动程序
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 启动更新后的新程序\n";
                    path = Application.StartupPath;
                    System.Diagnostics.Process process = new System.Diagnostics.Process();
                    process.StartInfo.FileName = "WinFormUpdaterDemo.exe";
                    process.StartInfo.WorkingDirectory = path;//要掉用得exe路径例如:"C:\windows";               
                    process.StartInfo.CreateNoWindow = true;
                    process.Start();
                    richTextBox1.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 退出更新软件\n";
                    //Application.Exit();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("请关闭系统在执行更新操作!");
                    Application.Exit();
                }
            }
        }

        /// <summary>
        /// 定时器(用来更新进度条)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            label1.Text = "下载文件进度:" + CommonMethod.autostep.ToString() + "%";
            this.progressBar1.Value = CommonMethod.autostep;
            if (CommonMethod.autostep == 100)
            {
                timer1.Stop();
                timer1.Enabled = false;
            }

        }

        /// <summary>
        /// 解压文件
        /// </summary>
        /// <param name="zipFilePath">压缩文件路径</param>
        /// <param name="saveDirectory">解压路径</param>
        public static void UnZIP(string zipFilePath, string saveDirectory)
        {
            // Perform simple parameter checking.
            if (!File.Exists(zipFilePath))
            {
                return;
            }
            if (!Directory.Exists(saveDirectory))
            {
                //解压后存放的 文件夹路径
                Directory.CreateDirectory(saveDirectory);
            }

            using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipFilePath)))
            {

                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    string directoryName = Path.GetDirectoryName(theEntry.Name);
                    string fileName = Path.GetFileName(theEntry.Name);

                    // create directory
                    if (directoryName.Length > 0)
                    {
                        //创建目录
                        string saveDir = saveDirectory + directoryName;
                        Directory.CreateDirectory(saveDir);
                    }

                    if (fileName != String.Empty)
                    {
                        string saveFilePath = saveDirectory + theEntry.Name;
                        using (FileStream streamWriter = File.Create(saveFilePath))
                        {
                            int size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }

        }
    }
}
View Code

 

更新程序-更新处理类SoftUpdate.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Xml;

namespace AutoUpdater
{
    /// <summary>  
    /// 更新完成触发的事件  
    /// </summary>  
    public delegate void UpdateState();
    /// <summary>  
    /// 程序更新  
    /// </summary> 
    public class SoftUpdate
    {
        private string download;
        public List<string> deleteFiles;
        private const string updateUrl = "http://127.0.0.1:7000/update.xml";//升级配置的XML文件地址  

        #region 构造函数
        public SoftUpdate() { }

        /// <summary>  
        /// 程序更新  
        /// </summary>  
        /// <param name="file">要更新的文件</param>  
        public SoftUpdate(string file, string softName)
        {
            this.LoadFile = file + "\\" + softName + ".exe";
            this.SoftName = softName;
        }
        #endregion

        #region 属性
        private string loadFile;
        private string newVerson;
        private string softName;
        private bool isUpdate;

        /// <summary>  
        /// 或取是否需要更新  
        /// </summary>  
        public bool IsUpdate
        {
            get
            {
                checkUpdate();
                return isUpdate;
            }
        }

        /// <summary>  
        /// 要检查更新的文件  
        /// </summary>  
        public string LoadFile
        {
            get { return loadFile; }
            set { loadFile = value; }
        }

        /// <summary>  
        /// 程序集新版本  
        /// </summary>  
        public string NewVerson
        {
            get { return newVerson; }
        }

        /// <summary>  
        /// 升级的名称  
        /// </summary>  
        public string SoftName
        {
            get { return softName; }
            set { softName = value; }
        }

        private string _finalZipName = string.Empty;

        public string FinalZipName
        {
            get { return _finalZipName; }
            set { _finalZipName = value; }
        }

        #endregion

        /// <summary>  
        /// 更新完成时触发的事件  
        /// </summary>  
        public event UpdateState UpdateFinish;
        private void isFinish()
        {
            if (UpdateFinish != null)
                UpdateFinish();
        }

        /// <summary>  
        /// 下载更新  
        /// </summary>  
        public void Update()
        {
            try
            {
                if (!isUpdate)
                    return;
                WebClient wc = new WebClient();
                string filename = "Update_NewVersion.zip";
                filename = Path.GetDirectoryName(loadFile) + "\\" + filename;

                FinalZipName = filename;
                //wc.DownloadFile(download, filename);
                wc.DownloadFileAsync(new Uri(download), filename);
                wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
                wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted);
                //wc.Dispose();
            }
            catch
            {
                throw new Exception("更新出现错误,网络连接失败!");
            }
        }

        void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            (sender as WebClient).Dispose();
            if (e.Error != null)
                throw e.Error;
            else
                isFinish();
        }

        void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            CommonMethod.autostep = e.ProgressPercentage;
            //Thread.Sleep(100);
        }

        /// <summary>  
        /// 检查是否需要更新  
        /// </summary>  
        public void checkUpdate()
        {
            try
            {
                WebClient wc = new WebClient();
                Stream stream = wc.OpenRead(updateUrl);
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(stream);
                XmlNode list = xmlDoc.SelectSingleNode("Update");
                foreach (XmlNode node in list)
                {
                    if (node.Name == "Soft" && node.Attributes["Name"].Value.ToLower() == SoftName.ToLower())
                    {
                        foreach (XmlNode xml in node)
                        {
                            if (xml.Name == "Verson")
                                newVerson = xml.InnerText;
                            else if (xml.Name == "DownLoad")
                                download = xml.InnerText;
                            else if (xml.Name == "Delete")
                            {
                                deleteFiles = new List<string>();
                                foreach (XmlNode delfile in xml)
                                {
                                    if (delfile.Name == "file")
                                        deleteFiles.Add(delfile.InnerText);
                                }
                            }
                        }
                    }
                }

                Version ver = new Version(newVerson);
                //读取版本信息(直接使用Assembly.Load会导致资源占用,故使用如下方法读取文件,解决资源占用问题)
                byte[] fileData = File.ReadAllBytes(loadFile);
                Version verson = Assembly.Load(fileData).GetName().Version;
                int tm = verson.CompareTo(ver);

                if (tm >= 0)
                    isUpdate = false;
                else
                    isUpdate = true;
            }
            catch (Exception ex)
            {
                throw new Exception("更新出现错误,请确认网络连接无误后重试!");
            }
        }

        /// <summary>  
        /// 获取要更新的文件  
        /// </summary>  
        /// <returns></returns>  
        public override string ToString()
        {
            return this.loadFile;
        }
    }

    /// <summary>
    /// 全局进度条变量
    /// </summary>
    public static class CommonMethod
    {
        public static int autostep;
    }
}
View Code

 

Web站点:

update.xml代码:

<?xml version="1.0" encoding="utf-8" ?>
<Update>
  <Soft Name="WinFormUpdaterDemo">
    <Verson>1.0.0.1</Verson>
    <DownLoad>http://127.0.0.1:7000/Update_NewVersion.zip</DownLoad>
    <Delete>
      <file>test.txt</file>
      <file>test1.txt</file>
      <file>\test</file>
    </Delete>
  </Soft>
</Update>
View Code

 

每次更新程序做以下操作即可:

  1. 修改主程序AssemblyInfo.cs中的版本号;
  2. 把最新的程序打包成“Update_autoUpdate.zip”,放入站点;(打包程序为覆盖安装,所以可以进行增量更新,减少更新成本)
  3. 修改站点xml文件版本号,使之和主程序AssemblyInfo.cs中的版本号一致。(XML中可配置要删除的文件,防止产生垃圾文件)

 

好啦,这样自动更新程序就做好啦。

效果演示:

 

 

 源码:https://download.csdn.net/download/u012322524/14141624

懒得下源码的,照着上面自己弄一样的,说的应该很详细了。

 

 

 

posted @ 2021-01-12 18:22  BeInNight  阅读(1804)  评论(0编辑  收藏  举报