应用程序更新的方法一般有两种:一是通知用户(比如发E-mail),让用户到指定的网站地址下载更新的程序;二是将更新的职责从用户那边转移到应用程序自身,由应用程序自身取代用户获取并安装一个软件的更新,客户端应用程序自身负责从一个已知服务器下载并安装更新,用户唯一需要进行干预的是决定是否愿意现在或以后安装新的更新。显然,后者比前者更友好。你现在可以看到类似后一种方法的实际产品,比如Windows XP和Microsoft Money。本文所介绍的.NET应用程序更新组件就可以提供类似的功能。
一、.NET应用程序更新组件介绍
.NET 应用程序更新组件AppUpdater 是使用.NET框架开发的。尽管AppUpdater 不是微软的产品,但是只要你在VS.NET工具栏中添加了该组件,就可以象使用其它组件一样通过拖拽的方式将该组件从工具栏中拖放到你的应用程序中,并设置一些属性(比如获取更新的位置、频率等)之后就可以使得你的客户端应用程序具备自动更新的功能。
二、工作原理
要深入理解.NET客户端应用程序更新组件的工作原理,需要仔细研究一下实现客户端应用程序更新有那些必须要做的事情。第一步需要做的是检查是否有更新;当发现有更新时,开始第二步工作——下载更新;当更新下载完成时,进行是最后一步的工作——实现更新。
(一) 为更新做检查
作为开发者,首先你得告诉应用程序到什么地方去做更新检查,否则它岂不是要大海捞针?其次,确定何时做更新检查。不可能用户每次一运行客户端程序,而它就在后台不停的进行更新检查,那多浪费资源!最后还有一项重要的事情需要解决,那就是如何进行更新检查。.NET应用程序更新组件使用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 WebServices提供一种更高级的更新检查方式。比如,假定你希望在进行更新你的其他用户之前先对一系列早期用户进行更新,如果客户端应用程序调用一个XML WebService来检查一项更新是否可用,那个XML Web Service还可以在数据库中查询该用户并判断该用户是否是早期用户。如果他们是早期用户,XML Web Service就返回一个值表示更新可用。如果不是,Web Service就返回一个值表示更新不可用。但本文介绍的.NET应用程序更新组件并不提供直接的XML Web Service支持。
要采用XML Web Service来进行更新检查,首先建立 XML Web Service并挂钩一个OnCheckForUpdate事件。这样就允许你自己的自定义检查代替轮询者线程更新检查。OnCheckForUpdate事件有一个返回值,该值表示更新是否被检测到。
(二) 下载更新
当.NET应用程序更新组件检测到一项新的更新可用时它会自动启动另一个线程并开始异步后台下载更新。
下载使用HTTP-DAV来完成。DAV 是一种扩展的HTTP,.. 它提供诸如目录和文件枚举这样的功能。一项完整的下载过程始于指定一个URL。采用URL来下载依赖于完成更新检查所使用的方式。比如,如果使用服务器显式文件,下载更新所用的URL通过在服务器显式文件 中的ApplicationURL属性指定。
更新下载显然需要一定的健壮性。在下载和更新之后让客户端应用程序处于任何不稳定的状态是不可接受的。下载过程中任何问题可能都会出现:更新文件所属的Web 服务器可能会宕机,客户端机器可能会崩溃,或者因为某种原因用户只是简单的关闭了应用程序。由于
应用程序正在进行下载,如果它被关闭的话,下载将会停止。一种可选的设计方案是使用单独的系统服务来进行应用程序的下载和更新。使用系统服务,即便应用程序自身没有在运行,更新下载也会继续。实际上,Windows XP就有一种称之为BITS的内建的下载服务,其目的即在此。BITS是Windows XP用来对Windows自身进行下载更新的。想查看更多关于BITS的信息,请参阅http://msdn.microsoft.com/library/en-us/dnwxp/html/WinXP_BITS.asp。在.NET应用程序更新组件中没有使用这种服务,因此它可以在不支持系统服务的Windows 9x中使用。
(三) 实现更新
.NET应用程序更新组件通过将下载和更新过程分为单独的两个阶段来获得健壮性。当每一阶段都完成时,在位于客户端应用程序目录下的一个更新显式文件(manifest file)中记载下来。如果下载或更新任何一个阶段中的过程被打断,它就会在下一次应用程序启动时从上一次完成的断点继续原来的工作。每一阶段都可以重新运行,因此如果在一个阶段中间出现失败,重新运行该阶段就可以成功。如果有错误发生,比如在下载过程中与服务器的链接丢失,.NET更新组件将会在稍后重试。如果报告了非常多的错误(例如web服务器再也没有回到在线状态),下载和更新将会被放弃并且报告出错误。
我们的第一个方法是简单地启动一个单独的进程来实现更新。这个单独的进程将会首先关闭应用程序进程,实现更新(因为这时候已被解锁),重启应用程序进程并且在完成之后关闭自己。因此,这种设计存在三个基本的问题:
. 在某些情况下它不起作用。在更新应用程序时,更新进程关闭原始的应用程序进程,更新进程自身也要被关闭,因此也就不会实现更新。
. 我们希望能够自动更新所有要实现更新的代码。我们希望自动安装修补的能力不仅仅发生在应用程序上,而且.NET应用程序更新组件自身也可以。使用这种模式,我们不能更新实现更新的进程。
. 强制用户关闭应用程序并在使用过程中等待,这是很不礼貌的。
用来实现应用程序更新的最后一种方法是使用.NET框架并行程序集模式。作为试图更新应用程序自身的替代方案,生成一个比目前存在版本新的应用程序版本。
新版本可以通过合并目前现存的应用程序目录与下载的更新版本来生成。当新版本完成时,用户在下次重新打开应用程序时会自动使用新版本。原始应用程序的拷贝就可以被移除了。棘手问题是弄清在某个指定时刻哪个版本该被载入。我们介绍一个名称为Appstart的应用程
序。Appstart是进入你应用程序的入口点,使用这种模式,你的应用程序目录看起来是这个样子:..
--> Program Files
--> MyApp
--> Appstart.exe
--> Appstart.config
--> V1 Folder
--> MyApp.exe
--> V1.1 Folder
--> MyApp.exe
要运行你的应用程序,你通常是启动Appstart.exe。如果你想在桌面上有个快捷键,那个快捷键必须应该指向Appstart而不是直接指向应用程序(注意,你可以重命名AppStart.exe 为任何你想要的名字,例如YourApp.exe)Appstart.exe是个非常简单的程序,它读取Appstart.config文件并且载入指定的应用程序。一个有效Appstart.config文件如下所示:
<Config> <AppFolderName>V1 Folder</AppFolderName> <AppExeName>MyApp.exe</AppExeName> <AppLaunchMode>appdomain</AppLaunchMode> </Config> |
AppFolderName指定包含当前要运行的应用程序版本的子文件夹。AppExeName包含在那个文件夹下要载入的exe文件名。当一个应用程序更新完成时,最后一步就是修改AppFolderName的值为指向应用程序的新版本。这样,下次用户运行应用程序时,就会运行新的应用程序更新后的版本。AppLaunchMode指定如何加载应用程序。有两种方式加载应用程序:第一种方式是使用AppDomains。AppDomains是.NET框架公
用语言运行时的特性,也是独立的逻辑单元和管理对象。公用语言运行时允许每个进程中存在多个应用程序域。这样Appstart.exe就能够在单独的AppDomain中同时却是相同的AppStart.exe进程中加载你的应用程序。尽管事实是两个不同的exe 程序在运行(即Appstart.exe和MyApp.exe),但只有一个进程在使用。对于大多数应用程序AppDomains会工作得很好,当然,在一个单独的AppDomain中运行和在一个单独
的进程中运行还是有些细微区别的。在这种情况下,AppLaunchMode可以设置为“process”,这样就会使应用程序在单独进程中加载。
一旦Appstart启动应用程序,它就会进入休眠状态等待应用程序终止。一旦应用程序终止,Appstart也会关闭。