做开发的人,尤其是做客户端(C/S)系统开发的人都会遇到一个头疼的问题,就是软件的自动更新;系统发布后怎样自动的更新程序,在下有幸开发过一个自动更新程序,更新程序与任何宿主程序是完全独立的;只要在主程序里面启动更新程序就行了;更新程序也是一个可执行文件,在启动的时候可以设置是否是自动更新和是否是手动更新,自动更新的意思就是说不需要人工的干预实现从远程服务器下载更新包,而如果是手动更新就会涉及到用户点击程序中的按钮实现更新;在自动更新与手动更新中可以根据项目的需要进行选择,有的程序必须要求用户进行更新才能继续使用,所以程序自动更新是有必要的;手动更新就是用户可以随时更新程序,不需要严格的控制版本问题;下面本人就来讲一下具体的实现细节,我贴出部分代码,源码属公司财产本人不宜上传;

自动更新的目的就是将服务器上的DLL文件拷贝到本地执行目录中,并且覆盖本地同名的文件;流程很简单,但是实现起来有几个地方需要注意:

1.大批量的DLL文件怎么下载到本地来,有多个DLL文件在下载过程中如果网速慢的情况下可能出现丢包、丢文件等情况;本人的实现是将多个文件通过ICSharpCode.SharpZipLib组件进行打包,这样可以省很多事;(如:动态连接库文件dll的名称在传输过程中大小写可能会变化)

2.下载到本地了,怎么覆盖原有的同名文件;本人的实现是先同名的文件的支持删除,然后解压缩;这个过程需要临时保存删除的文件,防止操作失败程序无法启动,要注意有事务性的原理;

3.如果更新的文件不只是单单的DLL文件可能还有一些无限极的文件夹;本人的实现是如果存在同名的文件夹,直接递归的删除,然后将其解压缩到目录中;由于压缩包解压后的顶级目录是压缩文件的名称,所有在复制的过程中需要注意目录的层次关系;

下面我们来走一下实现的整个流程,虽然没有给出整个源码,但是如果看完这篇文章的你基本实现起来没什么大问题了;

为了部署方便我建议大家麻烦点实现一个部署文件的工具,将所有的文件直接打包在里面同时生成服务器端的版本信息文件;

利用这个工具就很方便的实现了对文件进行压缩、生成HASH值、版本文件、更新地址等信息;

这个XML中保存的是服务当前的版本信息、更新文件的名称、更新文件的HASH值,为什么需要HASH就是怕更新文件在某些情况下被人调包了,如果所有的客户端更新后后果很严重;所以我们必须带上HASH值;

工具生成两个文件,一个是版本文件一个是更新包,服务器的任务已经完成,下面就是具体的客户端的实现;

为了知道何时需要进行版本更新所以要在客户端程序目录中保存一份用来记录版本信息的文件;

文件中保存着当前本地的版本号、服务器的更新地址、宿主程序的名称,需要宿主的名称就能在更新的时候将宿主程序重进程中枚举出来然后关掉,这样就不影响我们更新了,当然也可以实现宿主程序不关闭的情况下更新,如果用到某些已经被宿主程序占用的情况会直接影响更新流程,所以以防万一关了为妙;

这是客户端版本文件中保存的信息;

我们上面说了,更新分为手动和自动,我们先来说手动更新吧,手动更新就是需要用户自己去点击更新按钮然后开始更新,这个问题我们可以利用进程的参数传递解决;

当然在更新程序里面需要有这方面的逻辑判断;

入口的地方我们进行判断,更新方式;这里的下载远程更新包是用WebClient对象,也可以用其他的基于Socket的对象;更新开始之前需要先判断本地的版本号是否小于远程版本号,如果小于在进行更新;

因为下载的过程是异步的所以需要用到后台线程建议大家使用System.ComponentModel.BackgroundWorker这个后台线程对象,他对Thread进行了很好的封装;下面来看一下核心的流程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//开始辅助线程操作
        private void Back_thread_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                //实例化下载对象
                downclient = new WebClient();
                downclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(downclient_DownloadProgressChanged);
                downclient.DownloadFileCompleted += new AsyncCompletedEventHandler(downclient_DownloadFileCompleted);
                //下载远程更新包down.zip压缩文件|放在应用程序目录下|相应界面事件
                downclient.DownloadFileAsync(new Uri(Util.GetUpdateUrl() + "down.zip"), Util.GetDictiory() + "\\down.zip");
            }
            catch (Exception err) { System.Diagnostics.Debug.WriteLine(err); }
        }
        //在异步下载结束时触发该事件
        void downclient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            try
            {
 
                if (e.Error != null)
                {
                    eventLog1.WriteEntry(e.Error.ToString());
                    MessageBox.Show("在进行远程更新时,发生错误""信息提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    Util.KillProcess();//关闭主进程
                    //验证哈希值
                    if (Util.IsHash(Util.GetHash(Util.GetDictiory() + "\\down.zip"), FileWork.GetDownHash()))
                    {
                        //删除无用压缩文件
                        File.Delete(Util.GetDictiory() + "\\down.zip");
                        //删除无用版本文件
                        File.Delete(Util.GetDictiory() + "\\ServerUpdateFiles.xml");
                        MessageBox.Show("远程服务器更新包已发生变化,无法更新""信息提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        eventLog1.WriteEntry("远程服务器中的更新包在制作和下载时间段中数据包发生变化,为了安全期间不给予下载!");
                        this.Close();
                    }
                    else
                    {
                        //解压压缩包文件
                        ReduceToUnReduceFile.unZipFile(Util.GetDictiory() + "\\down.zip", Util.GetDictiory());
                        //删除压缩包文件
                        File.Delete(Util.GetDictiory() + "\\down.zip");
                        //检查文件夹层次结构
                        FileWork.LookFiles(Util.GetDictiory() + "\\down", Util.GetDictiory());
                        //订阅复制文件事件
                        FileWork.CopyFileEvent += new FileWork.CopyFileDelegate(FileWork_CopyFileEvent);
                        //递归复制文件
                        FileWork.CopyFiles(Util.GetDictiory() + "\\down", Util.GetDictiory());
                        //删除临时文件夹
                        FileWork.DeleteFiles(Util.GetDictiory() + "\\down");
                        //如果库结构更新成功,则才能更新程序的版本号,否则下次继续更新
                        if (EventChainReference.GlobalEventChain.OnAutoUpdateDb())
                            //更新本地版本号信息
                            Util.UpdateLocalXml();
                        File.Delete(Util.GetDictiory() + "\\ServerUpdateFiles.xml");
 
                        MessageBox.Show("升级成功!""信息提示");
                        Util.StartProcess();
                        isupdate = true;
 
                    }
                }
            }
            catch (Exception err) { eventLog1.WriteEntry(err.ToString()); }
            Application.Exit();
        }

这部分代码是串联整个过程的代码;

自动更新大概就讲完了,几个关键的地方都给出了,希望对大家开发自动更新程序有帮助;

C#实现之(自动更新)的更多相关文章

  1. 解析大型.NET ERP系统 自动更新

    C/S架构的应用程序需要支持自动更新功能,当新版本程序发布后,正在运行的客户端能检测到新版本的程序,通知用户是否下载更新.工作以来参与过几个自动更新模块的设计与维护,撰文总结自动更新模块设计与实现. ...

  2. QML 从无到有 3 (自动更新)

    新的需求出来啦,需要自动更新功能,不怕程序升级了. 自动更新,QML不好写,需要c++来辅助,这里就涉及QML中调用c++功能(这里就不写了,百度一下,很多). 思路:获取版本>下载程序> ...

  3. 扩展BindingList,防止增加、删除项时自动更新界面而不出现“跨线程操作界面控件 corss thread operation”异常

    在做界面程序时,常常需要一些数据类,界面元素通过绑定等方式显示出数据,然而由于UI线程不是线程安全的,一般都需要通过Invoke等方式来调用界面控件.但对于数据绑定bindingList而言,没法响应 ...

  4. 分析nuget源码,用nuget + nuget.server实现winform程序的自动更新

    源起 (个人理解)包管理最开始应该是从java平台下的maven开始吧,因为java的开发大多数是基于开源组件开发的,一个开源包在使用时很可能要去依赖其他的开源包,而且必须是特定的版本才可以.以往在找 ...

  5. ClickOnce部署(2):自动更新

    上次我们说了如何用最基本的方式用ClickOnce技术部署应用程序项目,本篇我们来认识一下如何让应用程序具备自动更新的功能. 我们依然通过实例来学习. 第一步,随便建一个应用程序项目,至于是控制台.W ...

  6. Android接入百度自动更新SDK

    一:前言 公司的app,上传到百度应用市场,然后说必须要接入百度的自动更新sdk才能上架,于是从百度官网上去下载jar包,下载的时候必须要带上数据统计,如果使用自动的jar包,还需要带上广告联盟,坑爹 ...

  7. C#之tcp自动更新程序

    .NETTCP自动更新程序有如下几步骤: 第一步:服务端开启监听 ServiceHost host; private void button1_Click(object sender, EventAr ...

  8. 使用 SVN Hook 实现服务器端代码自动更新

    之前的做法是客户端提交代码之后,再去服务器端项目中 svn up 一下来更新代码,让服务器端的项目更新到最新版本.可以编写一个 post-commit 钩子脚本来实现服务器端代码的自动更新,它在 SV ...

  9. git 远程版本库,github提供服务原理,git自动更新发送邮件

    1.安装好Linux,安装好Git(192.168.1.239) 2.创建一个用户zph(让此用户提供git on server),密码设置为12345678 # useradd zph # pass ...

  10. 【原创】我所理解的自动更新-APP发布与后台发布

    发布后台 创建渠道:添加新的渠道,设置渠道名称,自动生成渠道id.    查看渠道:查看渠道基本信息,渠道app版本号,资源版本号,是否开启更新.    创建/更新APP:选择打包ios,androi ...

随机推荐

  1. 你得学会并且学得会的Socket编程基础知识

    这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Socket编程有更好地理解. 本文源代码,可以通过这里下载 http://files.cnblog ...

  2. C# 数组深拷贝

    数组深拷贝,即完全复制出一份新的数组,两个数组内容完全相同. 一般有四种方法: 1. 循环遍历复制 2. 数组的成员方法:CopyTo CopyTo方法用作将源数组全部拷贝到目标数组中,可以指定目标数 ...

  3. SQL脚本修改数据库名称

    USE master; GO DECLARE @SQL VARCHAR(MAX); SET @SQL='' SELECT @SQL=@SQL+'; KILL '+RTRIM(SPID) FROM ma ...

  4. [android] 轮播图-滑动图片标题焦点

    谷歌提供的v4包,ViewPager 在布局文件中,先添加<android.support.v4.view.ViewPager/>控件,这个只是轮播的区域 在布局文件中,布置标题描述部分 ...

  5. vue 相对路径的图片 不显示问题

    例如 data () { return { img: '../../images/jifen/index/img_list_default_pic.jpg' //路径也没问题啊,怎么不显示呢,难道他瞎 ...

  6. 设计模式(13)--Chain of Responsibility(责任链模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一 ...

  7. js-ES6学习笔记-Proxy(2)

    1.has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效.典型的操作就是in运算符. var handler = { has (target, key) { if ...

  8. svn本地连接服务器失败,但是浏览器可以

    tortoise svn无法连接到服务器,清空“Autherticate data”后,再进行更新,提交,log查看等操作,svn还是不提示输入用户名和密码,而是报: error: Unable to ...

  9. 配置ArcGIS Server使用LDAP身份认证

    1.登陆ArcGIS Server Manager,修改站点的安全设置.选择用户和角色来自现有企业系统(LDAP或Windows域). 2.选择LDAP存储类型. 3.填写LDAP用户存储连接信息.主 ...

  10. LeetCode题解之 Letter Case Permutation

    1.题目描述 2.问题分析 可以使用递归的方法解决,参考了别人的答案才写出来的. 3.代码 vector<string> letterCasePermutation(string S) { ...