zoukankan      html  css  js  c++  java
  • 简单的自动更新程序实

    image 本文将演示一种桌面程序自动更新方案,其步骤比较多,但原理非常简单,通用性尚可,对于小型应用来说,直接拿去就可以用了。

    原理

    服务器端的结构是这样的:

    image

    其工作原理如下:

    Update.asmx仅提供一个功能,就是检测是否需要更新,在需要更新的时候就返回一个更新地址,通常情况下返回的地址就是Download.ashx,而在某些特殊情况下,也可以修改服务端使之从其他Url提供更新下载。检测是否需要更新的的具体做法是:首先获取Updata目录中的主程序版本号,再获取数据库中的最新版本号,两者对比。如果相同则直接与客户端提供的版本号相对比并返回结果;如果不同则将主程序版本号写入数据库,然后生成新的更新文件包,直接向客户端返回更新地址。

    Download.ashx的功能仅仅是将最新版本更新文件包输出。

    而客户端部分包含主程序、Update.exe以及其他附属文件,更新时由主程序检测并下载更新,在主程序退出时,如有更新并已成功下载,则调用Update.exe完成解包及更新覆盖工作。需注意的是:Update.exe永远不能被更新,因为它无法更新其自身,所以服务端更新时也不要将Update.exe纳入更新包。

    下面就是来实际编写一个自动更新解决方案:

    服务器端

    首先建立一个Web服务项目,项目名为“自动更新服务”:

    image

    添加一数据库,名为Database.mdf:

    image

    在数据库中创建新的数据库关系图,并如下设计数据库表:

    image

    创建一个Ado.Net Entity Data Model,名为Model.edmx:

    image

    从刚才的建立的数据库中生成模型:

    image

    在Web.Config的appSettings节点中新增两个节点,用以设置更新程序的主文件名及更新包下载地址:

    <appSettings>
        <add key="主程序文件名" value="MyApp.exe"/>
        <add key="更新包下载地址" value="Download.ashx"/>
    </appSettings>

    引入一个GZip类用以打包(该类的源码将在文章末尾随本文示例源代码一并提供):

    image

    添加一个新的Web服务,名为Update.asmx:

    image

    书写如下代码:

    [WebMethod]
    public string GetUpdate(string ClientVerison)
    {
        if (获取最新版本() != ClientVerison)
        {
            return System.Web.Configuration.WebConfigurationManager.AppSettings["更新包下载地址"];
        }
        return null;
    }
    
    static string 获取最新版本()
    {
        string v = 获取文件版本(HttpContext.Current.Server.MapPath(string.Format("~/App_Data/Update/{0}", System.Web.Configuration.WebConfigurationManager.AppSettings["主程序文件名"])));
        using (var c = new DatabaseEntities())
        {
            //从数据库取得最新版本信息
            var q = c.UpdateVersion.OrderByDescending(f => f.PublicTime).FirstOrDefault();
            if (q == null || v != q.Version)
            {
                //数据库中的版本与当前主程序版本不一致时,以主程序版本为准,写入数据库,并生成新的更新文件包
                var d = new UpdateVersion() { Version = v, PublicTime = DateTime.Now };
                c.AddToUpdateVersion(d);
                c.SaveChanges();
                打包更新文件(HttpContext.Current.Server.MapPath("~/App_Data/Update/"), HttpContext.Current.Server.MapPath("~/App_Data/Update.gzip"));
            }
        }
        return v;
    }
    
    public static void 打包更新文件(string 打包目录, string 输出文件)
    {
        GZip.压缩(输出文件, Directory.GetFiles(打包目录).Concat(Directory.GetDirectories(打包目录)).ToArray());
    }
    
    public static string 获取文件版本(string 文件路径)
    {
        FileVersionInfo f = FileVersionInfo.GetVersionInfo(文件路径);
        return f.FileVersion;
    }
    创建Download.ashx,用以输出更新文件包:

    image

    代码:

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/zip";
        context.Response.WriteFile(context.Server.MapPath("~/App_Data/Update.gzip"));
    }

    服务端至此就编写完毕了。

    客户端

    新建一个WinForm应用程序项目,名为Update:

    image

    建好之后直接删掉Form1.cs吧,此程序不需要界面,在Program.cs中写代码就可以了。

    同样需要引入GZip类用于解包:

    image

    然后编写代码:

    [STAThread]
    static void Main()
    {
        try
        {
            var d = DateTime.Now;
            while (DateTime.Now.Subtract(d).TotalSeconds < 10) Application.DoEvents();
            GZip.解压缩(Path.Combine(Application.StartupPath, "update.data"), Application.StartupPath);
        }
        catch { }
    }

    这里的作用就是等待10秒,然后解包update.data文件,覆盖到当前目录中。

    现在来建立主程序,主程序是WinForm、命令行、WPF都可以,我们新建一个WPF应用程序,命名为MyAPP:

    image

    为程序添加服务引用:

    image

    这里的地址使用的是本地的调试地址。

    为了检测主程序自身的版本号,还需要添加对System.Windows.Forms的引用。

    然后开始设计界面,这里仅为演示更新操作,所以界面上只是简单的设计了更新相关的提示、操作控件:

    image

    代码为:

    <Window x:Class="MyApp.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="377" Loaded="Window_Loaded" Closed="Window_Closed">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="1*" />
                <RowDefinition Height="1*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>
            <Label Margin="0" Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">检测到新版本,是否下载?</Label>
            <Button Grid.Row="1" Height="23" Name="button1" VerticalAlignment="Center" Visibility="Hidden" Click="button1_Click">开始下载</Button>
            <Label Grid.Row="2" Margin="0" Name="label2" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Hidden">更新包已下载完毕,在程序关闭后将自动执行更新操作。</Label>
        </Grid>
    </Window>
    

    需注意的是,这里控件都被设置为Visibility="Hidden",我们将会在需要时再将其显示出来。

    编写后台代码:

    public Uri DownloadUri
    {
        get
        {
            return _DownloadUri;
        }
        set
        {
            _DownloadUri = value;
        }
    }
    private Uri _DownloadUri;
    
    public bool UpdateReady
    {
        get
        {
            return _UpdateReady;
        }
        set
        {
            _UpdateReady = value;
        }
    }
    private bool _UpdateReady;
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        var u = new MyApp.ServiceReference.UpdateSoapClient();
        var s=u.GetUpdate(System.Windows.Forms.Application.ProductVersion);
        if (!string.IsNullOrEmpty(s))
        {
            //获取相对于Web服务所在Uri的Uri
            DownloadUri = new Uri(u.Endpoint.ListenUri, s);
            label1.Visibility = button1.Visibility = Visibility.Visible;
        }
    }
    
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var c = new WebClient();
        c.DownloadFile(DownloadUri, System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "update.data"));
        UpdateReady = true;
        label2.Visibility = Visibility.Visible;
    }
    
    private void Window_Closed(object sender, EventArgs e)
    {
        if (UpdateReady)
        {
            Process.Start(System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "update.exe"));
        }
    }

    测试

    现在将主程序、附属文件和Update.exe放在一起,并将主程序及附属文件复制一份放到服务器端的App_data/Update/目录中,再添加一个“更新说明.txt”:

    image

    然后启动客户端程序进行测试,应该看到程序界面里什么都没有,因为客户端和服务器端程序版本是一致的。

    现在我们修改客户端版本号为1.0.0.1:

    image

    然后重新编译程序。

    因为服务器仅仅是判断版本号是否不同,而不是哪个更高,所以不仅仅是升级,降级更新也是可以的,我们来测试一下:

    image

    找到所谓的新版本了^^,点开始下载:

    image

    下载完成,这时目录里就有update.data这个文件了。

    现在关闭程序,等待10秒,让Update.exe完成更新:

    image

    可以看到,程序被降级为1.0.0.0了,而且那个“更新说明.txt”也被更新出来了。

    下载

    示例源代码:http://www.uushare.com/user/icesee/file/2338431

    本文的XPS版本:http://www.uushare.com/user/icesee/file/2338436

  • 相关阅读:
    HDU4366 Successor 线段树+预处理
    POJ2823 Sliding Window 单调队列
    HDU寻找最大值 递推求连续区间
    UVA846 Steps 二分查找
    HDU3415 Max Sum of MaxKsubsequence 单调队列
    HDU时间挑战 树状数组
    UVA10168 Summation of Four Primes 哥德巴赫猜想
    UESTC我要长高 DP优化
    HDUChess 递推
    HDU4362 Dragon Ball DP+优化
  • 原文地址:https://www.cnblogs.com/jcomet/p/1693519.html
Copyright © 2011-2022 走看看