艾伟_转载:简单的自动更新程序实现

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

原理

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

其工作原理如下:

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

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

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

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

服务器端

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

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

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

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

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

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

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

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

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

书写如下代码:

[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,用以输出更新文件包:

代码:

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

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

客户端

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

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

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

然后编写代码:

[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:

为程序添加服务引用:

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

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

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

代码为:

 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">
            Height="1*" />
            Height="1*" />
            Height="1*" />
        Margin="0" Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">检测到新版本,是否下载? 
        Grid.Row="1" Height="23" Name="button1" VerticalAlignment="Center" Visibility="Hidden" Click="button1_Click">开始下载 
        Grid.Row="2" Margin="0" Name="label2" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="Hidden">更新包已下载完毕,在程序关闭后将自动执行更新操作。 

需注意的是,这里控件都被设置为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”:

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

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

然后重新编译程序。

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

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

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

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

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

下载

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

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

时间: 2024-07-31 23:41:28

艾伟_转载:简单的自动更新程序实现的相关文章

简单的自动更新程序实现

本文将演示一种桌面程序自动更新方案,其步骤比较多,但原理非常简单,通用性尚可,对于小型应用来说,直接拿去就可以用了. 原理 服务器端的结构是这样的: 其工作原理如下: Update.asmx仅提供一个功能,就是检测是否需要更新,在需要更新的时候就返回一个更新地址,通常情况下返回的地址就是Download.ashx,而在某些特殊情况下,也可以修改服务端使之从其他Url提供更新下载.检测是否需要更新的的具体做法是:首先获取Updata目录中的主程序版本号,再获取数据库中的最新版本号,两者对比.如果相

Windows8.1系统关闭应用商店自动更新程序教程

为什么要关闭应用商店自动更新程序? 原因很简单如果我们开启了应用商店自动更新功能之后电脑的网速骤降了,这个对于我们喜欢上网的朋友肯定是影响特别大吧. 关闭应用商店自动更新程序 1. 如win8.1系统会有一个超级开始菜单,然后点击超级菜单中的[应用商店]: 2. 进入应用商店以后,然后我们移动光标到右上角处就会出现设置界面,点击[设置]如下图所示: 3.在设置界面中我们点击[应用更新]之后打开进入,细节如上: 4. 然后找到自动为我的应用下载更新,我们点击关闭之后再弹出提示我们再点击[否]这样就

艾伟_转载:.NET设计模式:观察者模式(Observer Pattern)

概述 在软件构建过程中,我们需要为某些对象建立一种"通知依赖关系" --一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知.如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化.使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系.从而实现软件体系结构的松耦合. 意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新.[GOF <设计模式>] 结构图 图1 Observer模式

艾伟_转载:网站性能优化 - 数据库及服务器架构篇

1.Web Server 与 DB Server 分离 小型网站或 B/S 项目,因同时在线人数不多,尚可让同一台物理主机,既做 Web Server,又做 DB Server.但此二者皆会占用大量的 CPU.内存.磁盘 I/O,最好让二者分别用不同的服务器主机来提供服务,以分散压力.提高负载承受能力.此外,二者若在同一网段,应尽量用内网 Private IP 进行访问,而不要用 Public IP 或主机名称. 基本上跑 Web 上的应用程序,不管用什么软.硬件,同时处理多个用户的 reque

艾伟_转载:消息队列(Message Queue)简介及其使用

消息队列(Message Queue)简介及其使用 利用 MSMQ(Microsoft Message Queue),应用程序开发人员可以通过发送和接收消息方便地与应用程序进行快速可靠的通信.消息处理为您提供了有保障的消息传递和执行许多业务处理的可靠的防故障方法. MSMQ与XML Web Services和.Net Remoting一样,是一种分布式开发技术.但是在使用XML Web Services或.Net Remoting组件时,Client端需要和Server端实时交换信息,Serve

艾伟_转载:下载文件时根据MIME类型自动判断保存文件的扩展名

引言 用WebClient下载远程资源时,经常会遇到类似这样的网址: http://www.uushare.com/filedownload?user=icesee&id=2205188 http://www.guaishow.com/u/luanfujie/g9675/ 我们不知道这个Url具体代表的是一个网页,还是某种类型的文件. 而有些Url虽然带有扩展名,但可能是错误的扩展名,常见的比如把gif文件标上了jpg扩展名. 如果我们没法正确判断下载源的文件类型的话,就无法保存为正确的文件格式

艾伟_转载:使用配置文件(.settings、.config)存储应用程序配置

引言 我不知大家早先是如何保存应用程序配置,以备下次打开时使用的,反正我开始学.Net的时候就去研究序列化,以二进制或XML格式的序列化来保存应用程序配置.这样每次都要建立单独的配置类,并书写读写配置代码,相当麻烦. 期间也看了看.config文件的读写方式,感觉还是很麻烦,不如自己序列化来的踏实. 后来才猛然发现微软早提供好了settings,用以定义.config文件内容,并生成相应的强类型类,使用起来极其方便~,感觉就像在火星居住了半辈子突然发现还有地球这好地方*_*~ 鉴于网上setti

艾伟_转载:使用LINQ to SQL更新数据库(上):问题重重

在学习LINQ时,我几乎被一个困难所击倒,这就是你从标题中看到的更新数据库的操作.下面我就一步步带你走入这泥潭,请准备好砖头和口水,Follow me. 从最简单的情况入手 我们以Northwind数据库为例,当需要修改一个产品的ProductName时,可以在客户端直接写下这样的代码: // List 0NorthwindDataContext db = new NorthwindDataContext(); Product product = db.Products.Single(p =>

艾伟_转载:超级简单:ASP.NET Localization (本地化,多语言)

      有很多讨论ASP.NET localization(本地化,多语言)的文章,这篇文章是不会的深入讨论ASP.NET Localization (本地化,多语言).相反,它将给你有关asp.net页面中通常使用的内容的localization的一个快速参考,这些内容包括:ASP.NET服务端控件,html内容,SiteMap,一些其他资源.       这篇文章包括以下内容:       1. 如何本地化的ASP.NET服务器控件?       2.如何本地化HTML内容?