C# 窗口程序自动更新AutoUpdate

背景

之所以用到自动更新,是因为客户端经常用老版本作业,造成各种各样的问题.
导入此机制的目的是:
只要系统管理者和开发者,上传了压缩文件到服务器,无论作业员愿不愿意,系统打开时都将自动触发更新后台
之所以放在系统打开时,是为了避免影响到正在作业中数据,并且一般状况下,上下班时都会打开或关闭系统.

特别说明

1. 需要特别引用两个DLL,其他都是系统自带:
   ICSharpCode.SharpZipLib处理压缩文件
   Renci.SshNet用于从sftp服务器上下载文件
2. 版本比对,用到了:System.Version,例如: Version LocalAPtVersion = new Version(APVersion);
3. 不能用7z的压缩后缀,如果是7z的后缀会报错:Wrong Local header signature: 0xAFBC7A37
4. 压缩档可以用自己写的程序,也可以用FileZilla上传,只要sftp空间没问题,下载和解压缩都不会有问题
5. 程序中有sftp上传的逻辑,可以参考

程序逻辑

1.首先编辑两个项目:MES和AutoUpdate
2.在MES的Login Form中检查判断是否有新版本,有则打开AutoUpdate程序
3.AutoUpdate是一个Console程序,AutoUpdate的功能是:
  3.1 关闭MES系统
  3.2 从sftp下载MES.zip压缩文件
  3.3 解压缩并覆盖原文件
  3.4 打开MES
  3.5 删除压缩文件

Login程序中有关检查版本的代码

 /*1.先查c_ap Table中定义的程序版本,然后判断ap_version的值是否大於当前版本,大於則開始升級邏輯
   2.升級邏輯是:先檢查升級程序是否存在,如果已經存在則先Kill,然后再打開AutoUpdate*/
  string CurrentProjectPath = Environment.CurrentDirectory.ToString();//程序所在目录
  string APPName = Application.ProductName;//程序名称
  string APVersion = Application.ProductVersion;//程序版本
  DataTable GetAPDefineInfo = GetAPInfoByAPName(APPName);//获取AP定义内容
  if (GetAPDefineInfo.Rows.Count > 0)
  {
      string TempVersion = GetAPDefineInfo.Rows[0]["ap_version"].ToString();
      Version DBDefineVersion = new Version(TempVersion);
      Version LocalAPtVersion = new Version(APVersion);
      if (DBDefineVersion > LocalAPtVersion)
      {
      //特别注意:不能把.exe后缀名修进来,否则procList会是空的,就算AutoUpdate已经打开也是找不到的
      //Process在System.Diagnostics中
      Process[] procList = Process.GetProcessesByName("AutoUpdate");
      foreach (Process proc in procList)
        {
          proc.Kill();
        }
     //如果文件不存在,则会报:系统找不到指定档案
     Process.Start(CurrentProjectPath + "\\" + "AutoUpdate.exe");
      }
  }

AutoUpdate程序逻辑

using System;
using System.IO;
using System.Diagnostics;
using Renci.SshNet;
using ICSharpCode.SharpZipLib.Zip;
namespace AutoUpdate
{
    class Program
    {  //程序所在文件夾
        private static string APPath = Environment.CurrentDirectory.ToString();
        //压缩文件后缀需要是zip,因为7z的后缀在解压时会报错
        private static string ZIPFileName = "MES.zip";
        static void Main(string[] args)
        {
            string Message="Begin";
            //关闭MES主程序
            Message = CloseMESAP();
            Console.WriteLine(Message);
            //从SFTP下载zip
            Message = DownloadZIPFromSFTP();
            Console.WriteLine(Message);
            //解压缩时覆盖原文件
            Message = UnMESZIP();
            Console.WriteLine(Message);
            //打开主程序
            Message = OpenMESAP(); 
            Console.WriteLine(Message);
            //Delete压缩文件
            Message = DeleteZIP();//ok
            Console.WriteLine(Message);
            Console.WriteLine("Auto Update Finish");
            Console.ReadKey(); //如果没有这一行,Console界面会立即消失            
        }
        //关闭MES主程序
        public static string CloseMESAP()
        {
           Process[] procList = Process.GetProcessesByName("MES");
            foreach (Process proc in procList)
            {
                proc.Kill();
            }
            return "Close MES OK";
        }
         //从SFTP下载zip
        public static string  DownloadZIPFromSFTP()
        {
            string ServerIP = "192.168.1.2";
            string UseID = "shmes";
            string UserPWD = "mes123456!"; 
            DownloadFile(ZIPFileName, ServerIP, UseID, UserPWD); 
            return "DownLoad Zip OK";
        }
         //解压缩时覆盖原文件
        public static string UnMESZIP()
        {
            string MesStr = UnZip(ZIPFileName, APPath, "MESZIPPassword");
            return MesStr;
        }
        //主程序MES.exe
        public static string OpenMESAP()
        {  
            //前面已经Close MES了,所以不再检查Kill,
            //此文件夹内如果没有MES.exe文件,这里会报错:系统找不到指定文件
            System.Diagnostics.Process.Start(APPath + "\\" + "MES.exe");
            return "Open MES OK";
        }
        //Delete压缩文件
        public static string DeleteZIP()
        {
            File.Delete(APPath + "\\" + ZIPFileName);
            return "DeleteZIP OK";
        }
        
        //上传文件,已经测试过OK
        public static string UploadFile(string FileName, string sftpServerIP, string sftpUserID, string sftpUserPWD)
        {
            string LocalFileName = FileName;//SFTP服务器上的文件名和下载到本地一致
            string LocalFullName = Path.Combine(APPath, LocalFileName);//E:\SFC\MES\MES.zip
            string rndWorkPathName = "MES";//SFTP上MES相关程序所在文件夾
            string RemoteFullPath = "/uploads/" + rndWorkPathName + "/" + LocalFileName;
            using (var sftpclient = new SftpClient(sftpServerIP, sftpUserID, sftpUserPWD))
            {
                sftpclient.Connect();               
                rndWorkPathName = "/uploads/" + rndWorkPathName + "/";               
                sftpclient.ChangeDirectory("/uploads/");//切换目录   
                if (!sftpclient.Exists("MES"))
                {
                    sftpclient.CreateDirectory("MES");
                }
               //切换目录/uploads/MES
                sftpclient.ChangeDirectory(rndWorkPathName);
                using (var fileStream = new FileStream(LocalFullName, FileMode.Open))
                {
                    sftpclient.BufferSize = 8 * 1024; // bypass Payload error large
                    //如果文件已经存在则覆盖
                    sftpclient.UploadFile(fileStream, RemoteFullPath);
                    fileStream.Close();
                }
                sftpclient.Dispose();
            }
            return "OK";
        }
        //下载文件,已经验证过OK
        public static string DownloadFile(string FileName,string sftpServerIP,string sftpUserID,string sftpUserPWD)
        {
            string LocalFileName = FileName;//SFTP服务器上的文件名和下载到本地一致
            string LocalFullName = Path.Combine(APPath, LocalFileName);//E:\SFC\MES\MES.zip
            string rndWorkPathName = "MES";//SFTP上MES相关程序所在文件夾
            // RemoteFullPath: /uploads/MES/MES.zip
            string RemoteFullPath = "/uploads/" + rndWorkPathName + "/" + LocalFileName;
            using (var sftpclient = new SftpClient(sftpServerIP, sftpUserID, sftpUserPWD))
            {
                sftpclient.Connect();
                //切换到指定文件夹(如果文件夾不存在,则报错:No such file)
                rndWorkPathName = "/uploads/" + rndWorkPathName + "/";// /uploads/MES/
                sftpclient.ChangeDirectory(rndWorkPathName);
                using (var fileStream = new FileStream(LocalFullName, FileMode.Create))
                {
                    //如果文件不存在,也會報:No such file
                    sftpclient.DownloadFile(RemoteFullPath, fileStream);                   
                    fileStream.Dispose();
                }
                sftpclient.Dispose();
            }
            return "OK";
        }
        
        // ZIP:解压一个zip文件,不能用7z
        //ZipFile:需要解压的Zip文件名,例如:MES.zip,加不加上完整路径都可以
        //TargetDirectory:解压到的文件夹
        //Password:解压密码,如果压缩时没有密码,这里填了密码,不影响解压
        //OverWrite:是否覆盖已存在的文件,Default覆盖
        public static string UnZip(string ZipFile, string TargetDirectory, string Password, bool OverWrite = true)
        {
            string ReturnStr = "UnZIP OK";           
            //TargetDirectory结尾一定要有\\
            if (!TargetDirectory.EndsWith("\\"))
            {
                TargetDirectory = TargetDirectory + "\\";
            }
            //读取压缩文件(zip文件),准备解压缩
            using (ZipInputStream zipfiles = new ZipInputStream(File.OpenRead(ZipFile)))
            {
                if (!string.IsNullOrEmpty(Password))
                {
                    zipfiles.Password = Password;
                }                
                ZipEntry theEntry;               
                //如果是.7z后缀,程序会报:Wrong Local header signature: 0xAFBC7A37
                //改为: .zip 则OK
                while ((theEntry = zipfiles.GetNextEntry()) != null)
                {
                    string directoryName = "";
                    //解压出来的文件保存路径
                    string pathToZip = "";
                    pathToZip = theEntry.Name;
                    if (pathToZip != "")
                    {
                        directoryName = Path.GetDirectoryName(pathToZip) + "\\";
                        //如果不加下面的一行内容,解压后会变成.\MES\MES\,实际上我们不想有第二个MES
                        directoryName = directoryName.Replace("MES\\","");
                    }
                    //得到根目录下的第一级子文件夹下的子文件夹名称
                    string fileName = Path.GetFileName(pathToZip);
                    if (!Directory.Exists(TargetDirectory + directoryName))
                    {
                        Directory.CreateDirectory(TargetDirectory + directoryName);
                    }
                    //根目录下的文件名称
                    if (fileName != "")
                    {
                        if ((File.Exists(TargetDirectory + directoryName + fileName) && OverWrite) || (!File.Exists(TargetDirectory + directoryName + fileName)))
                        {
                            using (FileStream streamWriter = File.Create(TargetDirectory + directoryName + fileName))
                            {
                                int size = 2048;
                                byte[] data = new byte[2048];
                                while (true)
                                {
                                    size = zipfiles.Read(data, 0, data.Length);

                                    if (size > 0)
                                        streamWriter.Write(data, 0, size);
                                    else
                                        break;
                                }
                                streamWriter.Close();
                            }
                        }
                    }
                }

                zipfiles.Close();
            }
            return ReturnStr;
        }
  }
}

SimpAutoUpdater c#自动升级 模块源码 可以集成到自己程序: 首先在VS中为当前的主程序项目添加引用,引用“客户端”中的“SimpleUpdater.exe”。 在VS中,点开“解决方案管理器”中相应项目的“属性”节点,打开 AssemblyInfo.cs 文件,在最下面添加上一行自动更新声明: //--添加这行标记表示支持自动更新, 后面的网址为自动更新的根目录. [assembly: FSLib.App.SimpleUpdater.Updateable("http://ls.com/update.xml")] 这步是必须的,否则请求检查更新时会抛出异常;代码中的网址即上面提到的能访问到xml文件的网址。 如果您希望更加简单的使用而不用去加这样的属性,或者您想程序运行的时候自定义,您可以通过下列方式的任何一种方式取代上面的属性声明: 使用 FSLib.App.SimpleUpdater.Updater.CheckUpdateSimple("升级网址") 的重载方法。这个重载方法允许你传入一个升级包的地址; 在检查前手动设置 FSLib.App.SimpleUpdater.Updater.UpdateUrl 属性。这是一个静态属性,也就是说,您并不需要创建 FSLib.App.SimpleUpdater.Updater.UpdateUrl 的对象实例就可以修改它。 无论使用哪种方式,请确保在检查更新前,地址已经设置。 到这里,准备工作即告完成,为代码添加上检查更新的操作即可。 static class Program { /// /// 应用程序的主入口点。 /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var updater = FSLib.App.SimpleUpdater.Updater.Instance; //当检查发生错误时,这个事件会触发 updater.Error += new EventHandler(updater_Error); //没有找到更新的事件 updater.NoUpdatesFound += new EventHandler(updater_NoUpdatesFound); //找到更新的事件.但在此实例中,找到更新会自动进行处理,所以这里并不需要操作 //updater.UpdatesFound += new EventHandler(updater_UpdatesFound); //开始检查更新-这是最简单的模式.请现在 assemblyInfo.cs 中配置更新地址,参见对应的文件. FSLib.App.SimpleUpdater.Updater.CheckUpdateSimple(); /* * 如果您希望更加简单的使用而不用去加这样的属性,或者您想程序运行的时候自定义,您可以通过下列方式的任何一种方式取代上面的属性声明: * 使用Updater.CheckUpdateSimple 的重载方法。这个重载方法允许你传入一个升级包的地址; * 在检查前手动设置 FSLib.App.SimpleUpdater.Updater.UpdateUrl 属性。这是一个静态属性,也就是说,您并不需要创建 FSLib.App.SimpleUpdater.Updater.UpdateUrl 的对象实例就可以修改它。 */ FSLib.App.SimpleUpdater.Updater.CheckUpdateSimple("升级网址"); Application.Run(new Form1()); } static void updater_UpdatesFound(object sender, EventArgs e) { } static void updater_NoUpdatesFound(object sender, EventArgs e) { System.Windows.Forms.MessageBox.Show("没有找到更新"); } static void updater_Error(object sender, EventArgs e) { var updater = sender as FSLib.App.SimpleUpdater.Updater; System.Windows.Forms.MessageBox.Show(updater.Exception.ToString()); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值