zoukankan      html  css  js  c++  java
  • Windows程序通用自动更新模块(C#,.NET4.5以上)

    本通用自动更新模块适合所有Windows桌面程序的自动更新,不论语言,无论Winform还是wpf。

    一、工作流程:
    1. 主程序A调起升级程序B
    2. B从服务器获取更新程序列表,打印更新信息。
    3. B杀死A进程(此步骤可以放在步骤2~5任意位置)
    4. B根据更新信息中指示的地址,下载更新程序包(.zip文件)
    5. 解压缩.zip文件到一个新创建的文件夹
    6. 将解压后的文件拷贝到原始文件目录,做替换。
    7. 删除下载的.zip文件以及解压后创建的文件夹
    8. B打开A

    二、源码介绍:

    升级程序B的实现:

    更新信息列表用于存储版本信息,以及更新说明信息。通常为json或xml文件。本文为json文件。

    存储列表信息的类

    public class UpdateItem
    {
        public string Version { get; set; }  //版本号
        public string UpdateContent { get; set; }  //更新信息
        public string DownloadUri { get; set; }  //更新包的下载地址
        public string Time { get; set; }  //更新时间
        public string Size { get; set; }  //更新包大小
    }

     获取更新信息使用WebClient.DownloadData(Uri),其中使用Newtonsoft.Json进行json序列化及反序列化。

    WebClient client = new WebClient();
    byte[] data = client.DownloadData(uri);
    //json转为UpdateItem类对象
    UpdateInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateItem>(Encoding.UTF8.GetString(data));

     获取更新信息以后在界面上进行输出。
    下面介绍一下生成更新信息的json文件并使用FTP上传到服务器的代码。json文件也可以手动写,手动上传。不是重点,不想看可以跳到下一部分。

    UpdateItem UpdateInfo = new UpdateItem();
    ...  //赋值
    string json = JsonConvert.SerializeObject(UpdateInfo);
     //连接服务器
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://...");
    request.Method = WebRequestMethods.Ftp.UploadFile;
    request.Credentials = new NetworkCredential("账号", "密码");
    // 复制字符  
    byte[] fileContents = Encoding.UTF8.GetBytes(json);
    request.ContentLength = fileContents.Length;
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(fileContents, 0, fileContents.Length);
    requestStream.Close();
    FtpWebResponse response = (FtpWebResponse)request.GetResponse();
    response.Close();

    下载更新包zip文件

    下载使用WebClient.DownloadFile(Uri, string)

    string dir = System.IO.Directory.GetCurrentDirectory();  //程序所在文件夹路径
    string zipfile = System.IO.Path.Combine(dir, "file.zip");  //下载后zip文件的完整路径
    
    WebClient client = new WebClient();
    client.DownloadFile(UpdateInfo.DownloadUri, zipfile);
    return true;

    解压缩zip文件

    解压缩需要用到System.IO.Compression.ZipFile,需要.NET4.5及以上。如果用不了(找不到ZipFile),请检查是否已添加引用System.IO.Compression.FileSystem程序集。

    代码只有一行,两个参数分别为zip文件完整路径,解压后的文件夹完整路径。

    ZipFile.ExtractToDirectory(zipfile, extractPath);

    使用第三方压缩软件生成的.zip文件有可能会解压失败,建议使用下方代码生成。两个参数分别为需要压缩的文件夹完整路径,生成的zip文件完整路径。

    ZipFile.CreateFromDirectory(startPath, zipfile);

    拷贝解压后的文件到原始目录

    foreach (string item in Directory.GetFiles(extractPath))
    {
        File.Copy(item, System.IO.Path.Combine(dir, System.IO.Path.GetFileName(item)), true);
    }

    删除临时文件

    File.Delete(zipfile);
    DirectoryInfo di = new DirectoryInfo(extractPath);
    di.Delete(true);

    B进程杀死A进程

    string appname = "file"; //A名字,不要路径,不要.exe
    Process[] processes = Process.GetProcessesByName(appname);
    foreach (var p in processes)
    p.Kill();

    B进程调起A进程

    System.Diagnostics.Process.Start(@"path");  //完整路径

    主要流程就是这些,建议用异步操作实现。
    现在上号比较少,私信很多要代码的都没回…以后都会把完整代码附上的。
    下面是完整代码。

    private Uri uri = new Uri(@"http://...");  //更新信息列表文件路径
    private UpdateItem UpdateInfo;  //UpdateItem类的定义在前面第一部分说明
    
    public MainWindow()
    {
        InitializeComponent();
        LoadingData();
    }
    
    //用户按下更新按钮
    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        //下载
        Tb_State.Text = "正在下载新版本文件,请耐心等待";
        string dir = System.IO.Directory.GetCurrentDirectory();
        string zipfile = System.IO.Path.Combine(dir, "File.zip");
        bool success = await Task.Run(() =>
        {
            try
            {
                WebClient client = new WebClient();
                client.DownloadFile(UpdateInfo.DownloadUri, zipfile);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        });
    
        if (success)
            Tb_State.Text = "文件已下载,正在复制文件";
        else
        {
            Tb_State.Text = "下载新版本文件失败,请重试";
            return;
        }
    
        //杀死主程序进程
        string appname = "File";
        Process[] processes = Process.GetProcessesByName(appname);
        foreach (var p in processes)
            p.Kill();
    
    
        //解压缩+拷贝+删除
        bool success2 = await Task.Run(() =>
        {
            try
            {
                string extractPath = System.IO.Path.Combine(dir, "NewVersion");
                ZipFile.ExtractToDirectory(zipfile, extractPath);
                foreach (string item in Directory.GetFiles(extractPath))
                    File.Copy(item, System.IO.Path.Combine(dir, System.IO.Path.GetFileName(item)), true);
                File.Delete(zipfile);
                DirectoryInfo di = new DirectoryInfo(extractPath);
                di.Delete(true);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        });
    
        if (success2)
            Tb_State.Text = "更新完成,您可以点击下方按钮启动应用";
        else
            Tb_State.Text = "复制更新文件出错,请重试";
    }
    
    //读取更新列表文件
    private async Task<bool> LoadingData()
    {
        Tb_State.Text = "正在下载更新文件信息";
        bool success = await Task.Run(() =>
        {
            try
            {
                WebClient client = new WebClient();
                byte[] data = client.DownloadData(uri);
                UpdateInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateItem>(Encoding.UTF8.GetString(data));
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        });
    
        if (success)
            Tb_State.Text = "已获取新版本信息,可进行更新";
        else
            Tb_State.Text = "无法获取更新信息";
        return success;
    }
    
    //更新完成,更新程序B调起主程序A
    private void Btn_Open_Click(object sender, RoutedEventArgs e)
    {
        System.Diagnostics.Process.Start(@"D:...");   //A程序完整路径
    }

    再贴一个用于下载过程中,能实时显示已下载文件大小的代码
    此部分需写在下载部分之前。

    System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
    dt.Interval = TimeSpan.FromMilliseconds(100);  //100毫秒
    dt.Tick += (x, y) => {
        if (File.Exists(zipfile) == false)
            return;
        string size = ((new FileInfo(zipfile).Length) / 1024.0 / 1024).ToString("f2");
        if (download == false)  //是否下载完毕
            Tb_State.Text = size + "MB / " + UpdateInfo.Size;  //输出:已下载/总大小
        else
            dt.Stop();
    };
    dt.Start();

    代码部分执行时,再次执行代码可能会报错。可能原因包括下载zip文件后未删除再次下载报错等。如要做项目还有很多地方需要进行判断,如检测路径的合法性等。为了代码便于阅读,没有加入这些错误检测部分,需自行补充。几乎所有操作前都应判断路径合法性,以及目标位置的文件是否存在(是否需要先删除)等。只能说是在输入合法的情况下,完整执行此代码是没有问题的。

    代码通过Visual Studio 2019测试,.NET4.5。

  • 相关阅读:
    通讯录封装实现
    简单通讯录的实现 main..h .m文件全部
    iOS 开发 OC编程 字典和集合 排序方法
    iOS 开发 OC编程 数组冒泡排序.图书管理
    iOS 开发 OC编程 属性和字符串练习
    iOS 开发 OC编程 属性和字符串
    iOS 开发 OC编程 便利构造器以及初始化方法
    iOS 开发 OC编程 方法的书写
    IOS 开发 OC编程 类和对象
    iOS 开发 c语言阶段考试题
  • 原文地址:https://www.cnblogs.com/tuyile006/p/12699972.html
Copyright © 2011-2022 走看看