T O P

[资源分享]     c# 自动更新程序

  • By - 楼主

  • 2020-11-16 17:01:49
  • 首先看获取和更新的接口

    更新程序Program.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Diagnostics;
      4 using System.IO;
      5 using System.Linq;
      6 using System.Threading.Tasks;
      7 using System.Windows.Forms;
      8 
      9 namespace Update
     10 {
     11     static class Program
     12     {
     13         /// <summary>
     14         /// 更新程序启动后复制自身,使用副本进行更新
     15         /// -h 不显示界面
     16         /// -c 不使用copy更新程序
     17         /// -d 更新完成删除自身,通常用在copy的更新程序
     18         /// -b 更新下载到备份文件,不替换原文件
     19         /// -r 更新完成运行的文件,下一个参数为文件路径
     20         /// -k 如果系统正在运行则干掉
     21         /// </summary>
     22         [STAThread]
     23         static void Main(string[] args)
     24         {
     25             Application.EnableVisualStyles();
     26             Application.SetCompatibleTextRenderingDefault(false);
     27             Application.ThreadException += Application_ThreadException;
     28             
     29             List<string> lst = args.ToList();
     30             if (!lst.Contains("-b") && !lst.Contains("-k"))
     31             {
     32                 //这里判断成程序是否退出
     33                 if (Process.GetProcessesByName("serviceclient").Length > 0)
     34                 {
     35                     MessageBox.Show("服务正在运行,请退出后重试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
     36                     return;
     37                 }
     38             }
     39 
     40             if (lst.Contains("-k"))
     41             {
     42                 var ps = Process.GetProcessesByName("serviceclient");
     43                 if (ps.Length > 0)
     44                 {
     45                     ps[0].Kill();
     46                 }
     47             }
     48 
     49             //副本更新程序运行
     50             if (!lst.Contains("-c"))//不存在-c 则进行复制运行
     51             {
     52                 string strFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), Guid.NewGuid().ToString() + ".exe");
     53                 File.Copy(Application.ExecutablePath, strFile);
     54                 lst.Add("-c");
     55                 lst.Add("-d");
     56                 Process.Start(strFile, string.Join(" ", lst));
     57             }
     58             else
     59             {
     60                 Action actionAfter = null;
     61                 //将更新文件替换到当前目录
     62                 if (!lst.Contains("-b"))
     63                 {
     64                     actionAfter = () =>
     65                     {
     66                         string strUpdatePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "UpdateCache\\");
     67                         if (Directory.Exists(strUpdatePath) && Directory.GetFiles(strUpdatePath).Length > 0)
     68                         {
     69                             CopyFile(strUpdatePath, System.AppDomain.CurrentDomain.BaseDirectory, strUpdatePath);
     70                             if (File.Exists(Path.Combine(strUpdatePath, "ver.xml")))
     71                                 File.Copy(Path.Combine(strUpdatePath, "ver.xml"), Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "ver.xml"), true);
     72                             Directory.Delete(strUpdatePath, true);
     73                         }
     74                     };
     75                 }
     76                 try
     77                 {
     78                     //隐藏运行
     79                     if (!lst.Contains("-h"))
     80                     {
     81                         Application.Run(new FrmUpdate(actionAfter, true));
     82                     }
     83                     else
     84                     {
     85                         FrmUpdate frm = new FrmUpdate(actionAfter);
     86                         frm.Down();
     87                     }
     88                 }
     89                 catch (Exception ex)
     90                 { }
     91                 //运行更新后的文件
     92                 if (lst.Contains("-r"))
     93                 {
     94                     int index = lst.IndexOf("-r");
     95                     if (index + 1 < lst.Count)
     96                     {
     97                         string strFile = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, lst[index + 1]);
     98                         if (File.Exists(strFile))
     99                         {
    100                             Process.Start(strFile, "-u");
    101                         }
    102                     }
    103                 }
    104                 //删除自身
    105                 if (lst.Contains("-d"))
    106                 {
    107                     DeleteItself();
    108                 }
    109             }
    110             Application.Exit();
    111             Process.GetCurrentProcess().Kill();
    112         }
    113 
    114         private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    115         {
    116             throw new NotImplementedException();
    117         }
    118         private static void CopyFile(string strSource, string strTo, string strBasePath)
    119         {
    120             string[] files = Directory.GetFiles(strSource);
    121             foreach (var item in files)
    122             {
    123                 string strFileName = Path.GetFileName(item).ToLower();
    124 
    125                 if (strFileName == "ver.xml ")
    126                 {
    127                     continue;
    128                 }
    129                 //如果是版本文件和文件配置xml则跳过,复制完成后再替换这2个文件
    130                 string strToPath = Path.Combine(strTo, item.Replace(strBasePath, ""));
    131                 var strdir = Path.GetDirectoryName(strToPath);
    132                 if (!Directory.Exists(strdir))
    133                 {
    134                     Directory.CreateDirectory(strdir);
    135                 }
    136                 File.Copy(item, strToPath, true);
    137             }
    138             string[] dires = Directory.GetDirectories(strSource);
    139             foreach (var item in dires)
    140             {
    141                 CopyFile(item, strTo, strBasePath);
    142             }
    143         }
    144 
    145 
    146         private static void DeleteItself()
    147         {
    148             ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 1000 > Nul & Del " + Application.ExecutablePath);
    149             psi.WindowStyle = ProcessWindowStyle.Hidden;
    150             psi.CreateNoWindow = true;
    151             Process.Start(psi);
    152         }
    153     }
    154 }
    View Code

    更新程序界面

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Xml;
    
    namespace HW.Print.ServiceClient.Update
    {
        public partial class FrmUpdate : Form
        {
            private static string m_strkey = "sdfadsfdsfasdf";//定义一个密钥用以验证权限,不适用ticket
            Random r = new Random();
            Action m_actionAfter = null;
            bool m_blnShow = false;
            public FrmUpdate(Action actionAfter, bool blnShow = false)
            {
                m_blnShow = blnShow;
                m_actionAfter = actionAfter;
                InitializeComponent();
            }
    
            private void Form1_VisibleChanged(object sender, EventArgs e)
            {
                if (Visible)
                {
                    var rect = Screen.PrimaryScreen.WorkingArea;
                    this.Location = new Point(rect.Right - this.Width, rect.Bottom - this.Height);
                }
            }
    
            private void FrmUpdate_Load(object sender, EventArgs e)
            {
                Thread th = new Thread(() =>
                {
                    Down();
                    this.BeginInvoke(new MethodInvoker(delegate ()
                    {
                        this.Close();
                    }));
                });
                th.IsBackground = true;
                th.Start();
            }
            private string CheckIsXP(string strUrl)
            {
                bool blnXp = false;
                if (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1)
                {
                    blnXp = true;
                }
                if (blnXp && strUrl.StartsWith("https"))
                {
                    strUrl = "http" + strUrl.Substring(5);
                }
                return strUrl;
            }
    
            private void SetProcess(string strTitle, int? value, int? maxValue = null)
            {
                this.lblMsg.BeginInvoke(new MethodInvoker(delegate ()
                {
                    if (maxValue.HasValue)
                    {
                        this.progressBar1.Maximum = maxValue.Value;
                    }
                    if (value.HasValue)
                    {
                        this.progressBar1.Value = value.Value;
                    }
                    if (!string.IsNullOrEmpty(strTitle))
                    {
                        this.lblMsg.Text = strTitle;
                    }
                    lblValue.Text = this.progressBar1.Value + "/" + this.progressBar1.Maximum;
                }));
            }
    
            public void Down()
            {
                if (m_blnShow)
                    SetProcess("正在检查版本", null);
                try
                {
                    //先清理掉旧文件
                    try
                    {
                        if (Directory.Exists(System.AppDomain.CurrentDomain.BaseDirectory + "UpdateCache"))
                        {
                            Directory.Delete(System.AppDomain.CurrentDomain.BaseDirectory + "UpdateCache", true);
                        }
                    }
                    catch { }
                    if (!File.Exists(System.AppDomain.CurrentDomain.BaseDirectory + "setting.dat"))
                    {
                        Log.WriteLog("配置文件setting.dat不存在!");
                        return;
                    }
                    string strFileUrl = File.ReadAllText(System.AppDomain.CurrentDomain.BaseDirectory + "setting.dat");
    
    
                    strFileUrl = CheckIsXP(strFileUrl);
                    //获取列表文件
                    string json = HttpGet(strFileUrl.Trim('/') + "/getUpdaterList?key=" + Encrypt(m_strkey), Encoding.UTF8);
                    ResponseMessage rm = fastJSON.JSON.ToObject<ResponseMessage>(json);
                    if (rm == null)
                    {
                        Log.WriteLog("获取更新文件错误");
                        return;
                    }
                    if (!rm.Result)
                    {
                        Log.WriteLog("获取更新文件错误:" + rm.ErrorMessage);
                        return;
                    }
                    //云列表
                    Dictionary<string, DateTime> lstNewFiles = new Dictionary<string, DateTime>();
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(rm.KeyValue);
                    var documentElement = doc.DocumentElement;
                    var nodes = documentElement.SelectNodes("//files/file");
                    foreach (XmlNode item in nodes)
                    {
                        lstNewFiles[item.InnerText] = DateTime.Parse(item.Attributes["time"].Value);
                    }
    
                    List<string> lstUpdateFile = new List<string>();
                    string locationXml = System.AppDomain.CurrentDomain.BaseDirectory + "ver.xml";
                    if (!File.Exists(locationXml))
                    {
                        lstUpdateFile = lstNewFiles.Keys.ToList();
                    }
                    else
                    {
                        XmlDocument docLocation = new XmlDocument();
                        docLocation.Load(locationXml);
                        var documentElementLocation = docLocation.DocumentElement;
                        var nodesLocation = documentElementLocation.SelectNodes("//files/file");
                        foreach (XmlNode item in nodesLocation)
                        {
                            if (!lstNewFiles.ContainsKey(item.InnerText))
                            {
                                lstUpdateFile.Add(item.InnerText);
                            }
                            else if (lstNewFiles[item.InnerText] < DateTime.Parse(item.Attributes["time"].Value))
                            {
                                lstUpdateFile.Add(item.InnerText);
                            }
                        }
                    }
                    if (lstUpdateFile.Count > 0)
                    {
                        string strRootPath = System.AppDomain.CurrentDomain.BaseDirectory + "UpdateCache";
                        if (!System.IO.Directory.Exists(strRootPath))
                        {
                            System.IO.Directory.CreateDirectory(strRootPath);
                        }
                        SetProcess("", null, lstUpdateFile.Count);
                        for (int i = 0; i < lstUpdateFile.Count; i++)
                        {
                            if (m_blnShow)
                                SetProcess("正在下载:" + lstUpdateFile[i], i + 1);
    
                            string filejson = HttpGet(strFileUrl.Trim('/') + "/downloadUpdaterFile?key=" + Encrypt(m_strkey) + "&file=" + System.Web.HttpUtility.UrlEncode(lstUpdateFile[i]), Encoding.UTF8);
                            ResponseMessage filerm = fastJSON.JSON.ToObject<ResponseMessage>(filejson);
                            if (rm == null)
                            {
                                Log.WriteLog("下载更新文件错误");
                                return;
                            }
                            if (!rm.Result)
                            {
                                Log.WriteLog("下载更新文件错误:" + rm.ErrorMessage);
                                return;
                            }
    
                            string saveFile = Path.Combine(strRootPath, lstUpdateFile[i]);
                            if (!Directory.Exists(Path.GetDirectoryName(saveFile)))
                            {
                                System.IO.Directory.CreateDirectory(Path.GetDirectoryName(saveFile));
                            }
                            string strbase64 = filerm.KeyValue;   
                            MemoryStream stream = new MemoryStream(Convert.FromBase64String(strbase64));
                            FileStream fs = new FileStream(strRootPath + "\\" + lstUpdateFile[i], FileMode.OpenOrCreate, FileAccess.Write);
                            byte[] b = stream.ToArray();
                            fs.Write(b, 0, b.Length);
                            fs.Close();
    
                        }
    
                        doc.Save(System.AppDomain.CurrentDomain.BaseDirectory + "UpdateCache//ver.xml");
    
                        if (m_actionAfter != null)
                        {
                            if (m_blnShow)
                                SetProcess("替换文件", null);
                            m_actionAfter();
                        }
    
                        if (m_blnShow)
                            SetProcess("更新完成。", null);
                    }
                    else
                    {
                        if (m_blnShow)
                            SetProcess("没有需要更新的文件。", null);
                    }
                }
                catch (Exception ex)
                {
                    if (m_blnShow)
                        SetProcess("获取更新列表失败:" + ex.Message, null);
                    Log.WriteLog(ex.ToString());
                }
                finally
                {
                    if (m_blnShow)
                        Thread.Sleep(3000);
                }
            }
    
            private static string encryptKey = "111222333444555666";
    
            //默认密钥向量
            private static byte[] Keys = { 0x41, 0x72, 0x65, 0x79, 0x6F, 0x75, 0x6D, 0x79, 0x53, 0x6E, 0x6F, 0x77, 0x6D, 0x61, 0x6E, 0x3F };
            /// <summary>
            /// 加密
            /// </summary>
            /// <param name="encryptString"></param>
            /// <returns></returns>
            public static string Encrypt(string encryptString)
            {
                if (string.IsNullOrEmpty(encryptString))
                    return string.Empty;
                RijndaelManaged rijndaelProvider = new RijndaelManaged();
                rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32));
                rijndaelProvider.IV = Keys;
                ICryptoTransform rijndaelEncrypt = rijndaelProvider.CreateEncryptor();
    
                byte[] inputData = Encoding.UTF8.GetBytes(encryptString);
                byte[] encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length);
    
                return System.Web.HttpUtility.UrlEncode(Convert.ToBase64String(encryptedData));
            }
            public static string HttpGet(string url, Encoding encodeing, Hashtable headht = null)
            {
                HttpWebRequest request;
    
                //如果是发送HTTPS请求  
                //if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                //{
                //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
                request = WebRequest.Create(url) as HttpWebRequest;
                request.ServicePoint.Expect100Continue = false;
                request.ProtocolVersion = HttpVersion.Version11;
                request.KeepAlive = true;
                //}
                //else
                //{
                //    request = WebRequest.Create(url) as HttpWebRequest;
                //}
                request.Method = "GET";
                //request.ContentType = "application/x-www-form-urlencoded";
                request.Accept = "*/*";
                request.Timeout = 30000;
                request.AllowAutoRedirect = false;
                WebResponse response = null;
                string responseStr = null;
                if (headht != null)
                {
                    foreach (DictionaryEntry item in headht)
                    {
                        request.Headers.Add(item.Key.ToString(), item.Value.ToString());
                    }
                }
    
                try
                {
                    response = request.GetResponse();
    
                    if (response != null)
                    {
                        StreamReader reader = new StreamReader(response.GetResponseStream(), encodeing);
                        responseStr = reader.ReadToEnd();
                        reader.Close();
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                return responseStr;
            }
        }
    }
    View Code

    定义服务端接口,你可以用任意接口都行,我这里用webapi

    获取文件列表

     1 [HttpGet]
     2         public HttpResponseMessage GetUpdaterList(string key)
     3         {
     4             HttpResult httpResult = new HttpResult();
     5             if (!CheckKey(key))
     6             {
     7                 httpResult.KeyValue = "";
     8                 httpResult.Result = false;
     9                 httpResult.ErrorMessage = "无权限访问";
    10             }
    11             else
    12             {
    13                 //获取printupdate目录下update.exe的修改日期返回
    14                 string path = Path.Combine(HttpRuntime.AppDomainAppPath, "printupdate");
    15                 StringBuilder strXml = new StringBuilder();
    16                 strXml.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
    17                 strXml.AppendLine("<files>");
    18                 if (Directory.Exists(path))
    19                 {
    20                     string[] fs = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
    21                     var _p = path.ToLower().Trim().Length + 1;
    22                     foreach (var item in fs)
    23                     {
    24                         var dt = File.GetLastAccessTime(item);
    25                         strXml.AppendLine("<file time=\"" + dt.ToString("yyyy-MM-dd HH:mm:ss") + "\">" + item.Substring(_p) + "</file>");
    26                     }
    27                 }
    28                 strXml.AppendLine("</files>");
    29 
    30                 httpResult.KeyValue = strXml.ToString();
    31                 httpResult.Result = true;
    32                 httpResult.ErrorMessage = "";
    33             }
    34             return new HttpResponseMessage { Content = new StringContent(httpResult.ToJson(), Encoding.GetEncoding("UTF-8"), "application/json") };
    35         }
    View Code

    下载文件,我这里将文件序列号为base64字符串了,你可以直接返回文件流也行

     1 [HttpGet]
     2         public HttpResponseMessage DownloadUpdaterFile(string key, string file)
     3         {
     4             HttpResult httpResult = new HttpResult();
     5             if (!CheckKey(key))
     6             {
     7                 httpResult.KeyValue = "";
     8                 httpResult.Result = false;
     9                 httpResult.ErrorMessage = "无权限访问";
    10             }
    11             else
    12             {
    13                 string path = Path.Combine(HttpRuntime.AppDomainAppPath + "printupdate", file);
    14                 if (!File.Exists(path))
    15                 {
    16                     httpResult.KeyValue = "";
    17                     httpResult.Result = false;
    18                     httpResult.ErrorMessage = "文件不存在";
    19                 }
    20                 else
    21                 {
    22                     httpResult = ConvertToBase64Type(path);
    23                 }
    24             }
    25             return new HttpResponseMessage { Content = new StringContent(httpResult.ToJson(), Encoding.GetEncoding("UTF-8"), "application/json") };
    26 
    27         }
    View Code
    1   HttpResult ConvertToBase64Type(string fileName)
    2         {
    3             HttpResult httpResult = new HttpResult();
    4             var byts = File.ReadAllBytes(fileName);
    5             httpResult.KeyValue = Convert.ToBase64String(byts);
    6             return httpResult;
    7         }
    View Code
    1  bool CheckKey(string key)
    2         {
    3             return key == Encryption.Encrypt(m_strkey);
    4         }
    View Code
     1  private static string encryptKey = "111222333444";
     2 
     3         //默认密钥向量
     4         private static byte[] Keys = { 0x41, 0x72, 0x65, 0x79, 0x6F, 0x75, 0x6D, 0x79, 0x53, 0x6E, 0x6F, 0x77, 0x6D, 0x61, 0x6E, 0x3F };
     5         /// <summary>
     6         /// 加密
     7         /// </summary>
     8         /// <param name="encryptString"></param>
     9         /// <returns></returns>
    10         public static string Encrypt(string encryptString)
    11         {
    12             if (string.IsNullOrEmpty(encryptString))
    13                 return string.Empty;
    14             RijndaelManaged rijndaelProvider = new RijndaelManaged();
    15             rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32));
    16             rijndaelProvider.IV = Keys;
    17             ICryptoTransform rijndaelEncrypt = rijndaelProvider.CreateEncryptor();
    18 
    19             byte[] inputData = Encoding.UTF8.GetBytes(encryptString);
    20             byte[] encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length);
    21 
    22             return Convert.ToBase64String(encryptedData);
    23         }
    View Code

    需要注意的地方:

    1、我这里用到了json,那么不能直接饮用json的dll文件,会出现更新时候占用的问题,可以使用fastjson的开源代码,放进来解决,你可以直接使用xml格式的返回内容,这样就不需要json了,这样更方便

    2、如果你的下载接口是返回的文件流,那么你更新程序里面直接接收流保存文件就行了

    3、Program.cs里面,停止服务的功能,其实是可以通过传递参数的形式来停止,我这里写死了,你们根据自己需求修改

    效果

     

     你可以根据自己的需求,修改下界面效果,这是最简单的示例界面而已。

    本帖子中包含资源

    您需要 登录 才可以下载,没有帐号?立即注册