NET客户端应用程序:.NET应用程序更新组件

智能升级、自动更新,这是我们以前开发Windows应用程序时经常碰到而且必须注意的问题。在.NET应用程序更新组件没有出现之前这个问题曾经令人非常头疼(除非你就不打算进行升级:))。谁又不希望自己开发的应用程序象Windows XP自身或Microsoft Money那样具备自动更新的功能呢?使用.NET应用程序更新组件将会使这一切变得非常简单。在微软的DevDay2004上曹老师曾演示过的智能客户端IssueVision的一个非常重要的亮点就是智能升级,自动更新。其中就采用的是.NET应用程序更新组件。本组文章将通过实例来讨论一种生成可自动更新自身的.NET客户端应用程序的方法。

我最近收到一封来自微软IT团队的邮件提醒我他们已经探测到我的桌面计算机上的一些应用程序没有安装最新的补丁,并建议我安装这些最新的补丁。我将是第一个承认我并不是手动运行应用程序更新尽管我可以这样做。不管是在我的家用计算机还是我的工作机器上。让我去安装更新,就象应用程序中的中断特征或来自IT部门的一封(或有时是多封)邮件一样通常会带来问题。遗憾的是,相比那些来更新他们的应用程序的用户我更遵守规则。

这种需要用户或管理员手动安装更新的需求就是轮询客户端更新的原因,一直以来这也成为一个很大的问题并且代价昂贵。一个解决方案是将更新的职责从用户那边转移到应用程序自身。取代用户获取并安装一个软件的更新,客户端应用程序自身负责从一个已知服务器下载并安装更新。用户唯一需要进行干预的是决定是否愿意现在或以后安装新的更新。你现在可以看到象这类更新应用程序的方法的实际产品,比如Windows XP和Microsoft Money。

.NET应用程序更新组件
包括在这份白皮书中的是一个可使.NET客户端应用程序自动更新自身的组件。该组件使用.NET框架编写,这使得你可以通过简单的把该组件拖放到你现存的应用程序中并设置一些属性(比如从何处获取更新)就可以使你的应用程序具备自动更新的功能。

该组件不是微软的产品。它将被试图作为一个示例使得你能够开始,并且与该白皮书一起的还有尽可能多的资源。不过,值得提出的是该组件在真实应用中已经被大量的使用。在微软内部用.NET开发的游戏Terrarium的自动更新就使用了该组件(<http://www.gotdotnet.com/terrarium/>)。从2001年十月 Terrarium露出面纱到现在,已经被超过1万多个人用户安装并使用。

该组件是讨论如何让一个应用程序自动可更新的基础。这篇文章将集中论述.NET应用程序更新组件是如何工作的,并且教你如何将它应用到你自己的应用程序中。

为了更新自身,应用程序首先需要做的就是弄清楚可使用的更新放在什么地方。为做到这一点,应用程序需要知道三件事情:1)到什么地方为更新做检查;2)什么时候进行更新检查;3)如何进行更新检查。应用程序更新组件使用HTTP进行所有的网络通讯。这就允许通过企业网或外网来更新应用程序。这样的话进行更新检查所需要的地址就成了已知的Web服务器的一个URL地址。

要获取何时做更新检查,.NET应用程序更新组件在组件生成的基础上产生一个线程,该线程负责进行更新检查。该线程在大多数时间处于休眠状态,但会在设置好的间隔苏醒并实现一次更新检查。应用程序为新的更新所做的检查的频率依赖于各应用自身。但是进行更新检查的间隔常用值的范围一般是从一个小时到几天。对于所有的应用程序,这种轮询的基本方法并不都合适。比如Microsoft Money只是在用户让它去进行更新检查时它才去检查。在这种情况下,更新轮询线程可被禁用,通过用命令调用更新组件的CheckForUpdate()方法来实现更新检查。

关于如何进行更新检查的方法有如下几种:

方法一.直接文件检查----通过检查来更新最简单的方法是使用HTTP来比较服务器和客户端的应用程序的最后的修改日期/时间戳是否一致。如果服务器上有更新的文件,客户端就知道是时间更新自己了。对于Web浏览器来讲,也是同样的道理,它知道是否需要重新下载一个html页面或图片还是是否可以重复使用先前已下载的。 对于管理员来说这当然是最简单的。当应用程序有一个新的版本可用时,管理员简单的拷贝一个更新的版本来覆盖Web服务器上的旧版本。这种方法的问题在于更新不是自动进行,由此会出现潜在的失败窗体。比如,如果管理员在更新Web服务器上的应用程序版本,同时有个客户在下载更新之前的版本,那么这个客户的计算机上就会既存在更新之前的一些文件,也存在更新之后新版本的一些文件。基于上述原因,对于重要的应用程序不提倡使用直接文件检查来更新。

方法二.显式检查----为解决直接文件检查存在的原子问题,有必要进行一定级别的间接检查。为产生一间接级别,在服务器上使用一个显式的文件。一个可和.NET应用程序更新组件使用的有效的服务器显式文件大致是这个样子:
<VersionConfig>
<AvailableVersion>1.0.0.0</AvailableVersion>  <ApplicationUrl>http://localhost/demos/selfupdate/V1/</ApplicationUrl>
</VersionConfig>
AvailableVersion指定最新的可用程序集的版本号。ApplicationURL属性指定该版本应用程序所在的URL地址。当管理员想要更新客户端应用程序时,他们就会将应用程序的新版本拷贝到Web服务器上并且适当的修改服务器显式文件。客户端自身会探测到服务器显式文件已被修改,然后下载显式文件。客户端随后比较显式文件中指定的程序集版本号与应用程序EXE文件的版本号。如果服务器显式文件中的可用版本号较新,应用程序就知道是时间实现更新了。这种方法没有前一种方法的原子问题,对大多数应用程序而言是推荐使用的方法。

方法三:XML  Web Service 检查----XML Web Services提供一种更高级的更新检查的方式。比如,假定你希望在转出更新你的其他用户之前先对一系列早期用户进行更新,如果客户端应用程序调用一个XML Web Service来检查一项更新是否可用,那个XML Web Service还可以在数据库中查询该用户并判断该用户是否是早期用户。如果他们是早期用户,XML Web Service就返回一个值表示更新可用。如果不是,Web Service就返回一个值表示更新不可用。因为采用Web Service为更新做检查能够根据客户端所希望的功能采取许多形式,.NET 应用程序更新组件并不提供直接XML Web Service支持。采用XML  Web Service来进行更新检查,首先建立XML Web Service并挂钩OnCheckForUpdate事件。这样就允许你自己的自定义检查代替轮询者线程更新检查。OnCheckForUpdate事件有一个返回值,该值表示更新是否被检测到。

前面我们讨论了.NET应用程序更新是如何工作的,现在我们来将它应用在实例中。

第一步:建立应用程序来进行更新

在这一步我们将建立应用程序来演示如何实现自动更新。

1.        使用VS.NET生成一个新的Windows应用项目,命名为“SampleApp”。

2.        给窗体一个你选择的有趣的背景色。我们将使用背景色来与后面更新的版本区别。

3.        现在让我们给这个应用程序增加一个细微的功能,首先给你的窗体增加一个按钮。压缩文件中包含一个拥有简单Windows窗体的程序集。给压缩文件中Samples/SampleApp/SimpleForm程序集增加一个引用。然后在你的按钮事件句柄中添加两行代码:

    SimpleForm.Form1 F = new SimpleForm.Form1();

    F.Show();

4.        将你的build标志从debug转换为RELEASE。这将允许我们避免稍后当我们生成一个应用程序的新版本而同时原始拷贝正在运行产生的pdb文件锁定问题。

生成并测试你的应用程序。

第二步:添加.NET应用程序更新组件

在这一步我们将给SampleApp添加.NET应用程序更新组件。

1.        VS.NET工具栏的组件标签上,右击选择“自定义工具栏”。选择‘.NET框架组件’标签。点“浏览”并选择位于压缩文件中AppUpdater项目下的AppUpdater.dll,单击OK

2.        一个AppUpdater图标现在应该出现在工具栏的组件列表的底部。AppUpdater组件拖放到SampleApp窗体上。一个名为appUpdater1.NET应用程序更新组件的实例会出现在窗体的底部。

第三步:设置.NET应用程序更新组件   

在这一步我们将设置.NET应用程序更新组件。注意这个示例你只需改变最开始的四个属性,其它的,默认值就够了。

AppUpdater  属性 ――这是.NET Application应用程序更新的核心,对于本程序需要做以下设置:

 

 

 

 

 

属性名称

描述

AutoFileLoad

这个控制后面要描述的命令下载特征,现在将它设置为true

ChangeDetectionMode

 

 

 

 

 

该枚举决定如何为更新进行检查。在该例中,我们将使用一个服务器显式检查,因此将这个值设置为“ServerManifestCheck”。

ShowDefaultUI

 

 

 

 

 

.NET应用程序更新组件具有一系列用户界面来通知用户一些事件,对于该例我们将使用默认的用户界面,因此将这个值设置为true

UpdateUrl

 

 

 

 

 

UpdateUrl是决定更新程序到何处去寻找更新的。在该例中设置为服务器显式文件的URL

http://yourWebserver/SampleApp_ServerSetup/UpdateVersion.xml. 

用你的Web服务器名称来代替”yourWebserver”

 

 

 

 

 

Downloader 属性――AppUpdater组件有两个子组件。第一个称之为Downloader,它控制组件的下载和安装。下面是该属性的描述,对于我的示例来说默认的属性值就能工作的很好。

属性名称

描述

DownloadRetryAttempts

 

 

 

 

 

在下载期间如果有错误发生(比如Web服务器宕机)downloader会稍后重试。这个属性控制downloader认为是彻底的应用程序更新错误之前重试网络请求的次数。

SecondsBeteweenDownloadRety

重试网络请求之前等待的秒数。

UpdateRetryAttempts

这个属性控制试图更新的次数。

ValidateAssemblies

 

 

 

 

 

这个属性控制下载程序集有效完成的级别。更多信息参见这篇文章的安全一节。

 

 

 

 

 

Poller 属性――AppUpdater的第二个子组件是PollerPoller控制更新检查。下面是该属性的描述,对于我们的示例而言,所有的默认属性值就工作的很好。

属性名称

描述

AutoStart

 

 

 

 

 

布尔值,在应用程序启动时控制Poller是否应当开始轮询或它是否应当等待直到有计划的显式开始。

DownloadOnDetection

布尔值,控制Poller在一个新的更新发现时是否立即开始下载更新,或者是否通过调用DownloadUdpate()方法必须开始显式下载。

InitialPollInterval

应用程序启动后在第一次执行更新检查前等待的秒数。

PollInterval

 

 

 

 

 

第一次更新检查之后,PollInterval控制后续每次更新检查之间间隔的秒数,注意:默认为每30秒进行一次检查。

 

 

 

 

 

所有这一切完成之后,你的属性表格看起来应当是下面这个样子:

Samples/SampleApp/SampleApp_Complete目录包含应用程序正确安装的一个版本。

第四步:生成并在客户端部署应用程序V1版本。

在这一步我们将生成应用程序V1版本并将它部署在客户端。

SampleApp项目中,打开AssemblyInfo.cs文件。将AssemblyVersion的值从“1.0”修改为“1.0.0.0.这会引起在生成程序集时获得值为“1.0.0.0”的标记,该标记代替VS.NET通常指定为递增的值。

1.        生成应用程序。

2.        从压缩文件中将Samples/SampleApp/SampleApp_ClientSetup目录拷贝到你的本地机器上。要注意SampleApp_ClientSetup目录已经包含了AppStart.exeAppStart.config已经设置为指向1.0.0.0目录并且启动SampleApp.exe

SampleApprelease生成目录下拷贝SampleAppAppupdater.dllSimpleForm.dllSampleApp.exe)到你客户端的SampleApp_ClientSetup/1.0.0.0目录下。

在这个时候,一个功能完整的应用程序版本应当被“安装”到了客户端,可以通过运行AppStart.exe来执行。

第五步:安装Web服务器

在这一步我们将安装Web服务器以在轮询应用程序更新时使用。.NET应用程序更新组件使用HTTP-DAV来下载应用程序更新因此需要一个支持HTTP-DAVWeb服务器。Windows 2000上的IIS5.0和更新的操作系统都支持HTTP-DAV

1.        从压缩文件中将Samples/SampleApp_ServerSetup目录拷贝到你的Web服务器上的wwwroot目录下。

2.        为了完整,将SampleAppV1版本拷贝到Web服务器的1.0.0.0文件夹。

3.        在你的Web服务器上为SampleApp_ServerSetup目录启用IIS的“目录浏览”。

第六步:自动更新应用程序

OK,现在是时间来通过自动安装一个新版本来看看以上这些艰苦工作的结果了。

1.        如果你部署在客户端的SampleApp版本没有运行,加载它让它运行。记得使用AppStart.exe

2.        回到VS.NET并在SampleApp窗体中做一些可以被注意到的修改(比如修改背景色)。

3.        AssemblyInfo.cs的版本信息修改为2.0.0.0

4.        重新生成。

5.        回到Web服务器并生成一个和1.0.0.0目录同等的目录2.0.0.0。从release生成目录下将新版本应用程序拷贝到Web服务器上新建的2.0.0.0目录下。

6.        打开UpdateVersion.xml并修改AvailableVersion2.0.0.0。修改ApplicationURL为指向新的2.0.0.0路径。

7.        保存对UpdateVersion.xml所做的修改。

一旦你保存了新的UpdateVersion.xml,在30秒之内,运行中的SampleApp拷贝将会探测到新的可用的版本。SampleApp将下载新版本,实现更新,并弹出默认的用户界面询问用户是否希望重启并立即开始使用新版本。单击“Yes”回应该对话框。SampleApp将会重启并运行新版本。如果你查看客户端SampleApp的部署,你会注意到现在在原始的1.0.0.0的目录后有一个2.0.0.0的目录。1.0.0.0目录将会在下一次更新发生时被清空。

按需求安装

通过利用.NET框架可伸缩的本质,.NET应用程序更新组件能够使得另一种特性----按需求安装可行。通过使用按需求安装,只有主可执行程序被显式安装到客户机上。应用程序剩下的部分可以根据基本需要自动下载和安装。

通过.NET应用程序更新组件的AutoFileLoad属性来使得按需求安装可用或禁用。你必须仔细考虑在你的应用程序中程序集边界所处的位置以及什么动作会引起程序集被下载。由于程序集的下载涉及到网络输入输出,因此下载所花费的时间是可变的。在程序集下载期间,应用程序会被冻结等待程序集下载完成。

部署安全

自动安装应用程序更新的能力在具备很多好处时,它也伴随着带来了一些潜在的危险。当你使得安装更新变得简单时,如果不小心,你也可能使得安装恶意代码变得简单。有两种危险。第一种危险是有人会用自己的Web服务器欺骗用来部署更新的Web服务器。他们可能会利用那台Web服务器在你的应用程序路径安装一个病毒程序。阻止欺骗或其它通过网络进行的不正当干预的最简单的方法是使用HTTPS。要和.NET应用程序更新组件一起使用HTTPS,可以简单的用HTTPS  URLs来代替HTTP URLs。当然,HTTPS不是银弹。使用HTTPS有两个问题。第一是可伸缩性。使用HTTPS需要服务器加密所有从Web服务器上下载的文件。如果一个应用程序的更新文件很大,加密更新文件的代价会使服务器的负担过重。使用HTTPS的另一个问题是它对第二种安全危险毫无益处。第二种危险会使你的服务器做一些妥协,黑客既可能从内部也可能从外部来攻击你的服务器。如果你的Web服务器妥协的话,情况就非常坏了,但是如果这同时意味着成百上千的客户端也通过自动更新遭受连累的话,这种情况将是灾难性的。

为解决这个问题,.NET应用程序更新组件使用给一个.NET程序集添加强名称的能力来验证所下载的程序集。如果.NET应用程序更新组件检测到下载期间一个程序集不是使用你的密钥签名的。下载就会取消。这意味着只有拥有你的应用程序私钥的人才能够建立可自动部署的更新文件。.NET安全建立在维护你的私钥保密的基础之上。即便是在你的组织内部,也只有少数几个选择的人才能够访问你的私钥。

要验证程序集有效,.NET应用程序更新组件验证你当前安装的应用程序可执行程序的公钥和下载的更新的公钥是否匹配。如果两个程序集以相同且保密的私钥签名,那么嵌入的公钥也就相同。因为被CLR加载的程序集为了验证它的公钥,CLR计算它正常的哈希值检查来保证程序集实际上就是真正的程序集而不是被做了手脚的程序集。如果验证检查失败,就会被当做类似其它更新失败一样的情况。为了能够在下载时验证,简单的给你所有的应用程序集添加强名称并将.NET应用程序更新组件的ValidateAssemblies属性设置为true

在下载时进行程序集验证会起很大的作用,但实际上,应用程序会经常有不同私钥签名的组件。比如,你的应用程序可能有两个文件:使用你的私钥签名的可执行程序集和另一个包含你购买的应用在你的应用程序中的第三方图表控件的dll程序集。第三方程序集可能使用第三方而不是你自己的私钥来签名。使情况变得更为复杂的是,在你的应用程序中用来签名程序集的有效私钥的设置随着版本号的改变可能会发生变化。你该如何自动更新那些应用的类型?为解决这个问题,你可以在你的应用程序中生成一个包含有效公钥的列表的程序集。将该程序集使用应用程序的主私钥(应用程序的exe文件签名的密钥)签名并把该程序集放到Web服务器上和应用程序更新文件一起的目录下。在更新下载过程开始之前,.NET应用程序更新组件将会检查Web服务器上应用程序更新目录下一个周知的程序集“AppUpdaterKeys.dll”。如果存在,该程序集就会被下载。该程序集会被拿来和主应用程序的公钥对比验证。如果签名有效,密钥列表会被提炼出来。从次之后,任何处于该列表中的密钥会被认为是更新文件的有效签名。

有关安全方面推荐的方法是使用HTTPS  URLs来实现更新检查。这会提供第一级别的欺骗保护。对于更新下载,最好不要使用HTTPS  URLs以避免使你Web服务器的负荷过重。而是给你的应用程序的程序集添加强名称并使用程序集验证特性。

可扩展性

在这篇文章前面讲过的示例中我们简单的通过拖放一个组件到应用程序中并设置一些属性来实现自动部署。虽然这在许多应用程序中工作的很好,但一些应用程序中会需要高级别的控制,这只能通过写代码来获得。.NET应用程序更新组件有可用来自定义并替代应用程序更新的应用程序接口。

.NET应用程序更新组件也包含另外的允许显式控制当类似更新检查和更新的动作实现时的应用程序接口。这些动作分别通过CheckForUpdate () ApplyUpdate()方法来控制。

调试

这一节将指出一些首选的调试选项,以及描述使用该组件的用户大多数常见的问题。

.NET应用程序更新器在和AppStart.exe相同的目录下生成一个名为AppUpdate.log的隐藏日志文件。所有的更新成功和失败信息都记录在该日志中。当有一个特殊的客户端不能成功更新时日志文件会特别有用。你可以使用日志来判断在什么时间以及是如何更新失败的。另外,.NET应用程序更新组件使用.NET框架的Debug类来输出大量有用的信息。如果你在调试器中运行你的应用程序,你会在输出窗口中看到这些信息。你可以循着.NET应用程序更新器的脚步重点观察并找到出问题的地方。

如果由于某种原因,你无法使得.NET应用程序更新器工作。在你深入调试之前确定以下几点,你遇到的问题很可能就是如下之一:

你是否将IIS目录浏览给打开了?如果没有,更新器将不会下载安装任何文件。

你是否正确的部署了一切并正确设置了URL

如果你的应用程序安装在program files目录下,确定你是该机的超级管理员或超级用户吗?如果不是,你将不会有写权限来更新应用程序。

你是在应用程序的主用户界面线程中生成AppUpdater对象的吗?如果不是,更新器将不能显示用户界面并且在激发事件回到用户界面时失败。

是否更新成功,但应用程序使用新的更新自动重启时失败?.NET应用程序更新组件试图通过调用Application.Exit方法来重启应用程序。然而,该方法并不能保证关闭一个应用程序。如果你生成并遗留了单独的线程在运行,该方法就无法关闭进程。保证所有线程终止的解决的方案是通过调用Application.OnExit事件,或者挂钩.NET应用程序更新器的OnUpdateComplete事件并自己处理关闭。

总结

客户端应用程序部署方便是.NET框架第一个版本的重要的目标。没有比用.NET框架建立解决部署问题的客户端应用程序更好的其它技术。部署方便仍然是未来.NET框架新版本的一个重要目标。就方案而言,这里描述的.NET应用程序更新组件代表了我们的一些想法,在未来版本的.NET框架中我们将可以直接使用。然而,在那个时候到来之前的这段期间,.NET应用程序更新组件不失为开始建立自动更新应用程序的一种重要的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值