UpdateVersion.xml解释:
<AvailableVersion>2.0.0.0</AvailableVersion>
告诉客户端目前可用的版本,客户端appupdater组件会比较本地主应用程序版本号和该项配置的版本号,如果比本地的版本号更高,则进行下载更新。
<ApplicationUrl>http://YourServerDomainName/SmartServer/Ver/</ApplicationUrl>
告诉客户端到哪个网址进行下载更新
建议大家把这个属性设置成Dynamic,即从配置文件中读出来,不过,要让服务器的.config文件允许被下载,我们还要在IIS中 ASP.net应用程序”配置“中对它进行设置,默认是不允许访问的。
ShowDefaultUI表示下载结束时用一个简单的界面提示你要不要启动新版本。你还可以用自己的窗体显示,方法是设成False,并在OnUpdateComplete事件中写代码把你的窗体显示出来。
当然,还有一些其他的属性,比如,是否要使用公钥(可以是一个,也可以提供一个Assembly,里面包括多个公钥)验证要下载的文件。由于时间关系,我就不多说了。
(3) 使用AppStart.exe启动实际的应用程序。
(4) 在IIS把下载文件夹设成允许目录浏览,因为AppUpdater要枚举里面的所有文件,并把它们都下载到本地。如果是Windwos 2003,麻烦一点,要允许WebDAV,并把.config文件设置成允许下载。
(5) 做个msi安装文件,打包应用程序,这样,当应用程序运行时,它就能自动判断有无最新版本,并下载到本地运行。
实例:
应 用程序更新的方法一般有两种:一是通知用户(比如发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> |
<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也会关闭。
三、实例演练
前面我们讨论了.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应用程序更新组件
1. 在VS.NET工具栏的组件标签上,右击选择“自定义工具栏”。选择“.NET框架组件”标签。点“浏览”并选择位于压缩文件中AppUpdater 项目下的AppUpdater.dll,单击OK。
2. 一个AppUpdater图标现在应该出现在工具栏的组件列表的底部。将AppUpdater 组件拖放到SampleApp窗体上。一个名为appUpdater1的.NET应用程序更新组件的实例会出现在窗体的底部。
第三步:设置.NET应用程序更新组件
在这一步我们将设置.NET应用程序更新组件。注意这个示例你只需改变最开始的四个属性,其它的采用默认值。
AppUpdater属性:这是.NET Application应用程序更新的核心,对于本程序需要做以下设置:
(1)AutoFileLoad:这个控制后面要描述的命令下载特征,现在将它设置为true。
(2)ChangeDetectionMode:该枚举决定如何为更新进行检查。在该例中,我们将使用一个服务器显式检查,因此将这个值设置为“ServerManifestCheck ”。
(3)ShowDefaultUI: .NET 应用程序更新组件具有一系列用户界面来通知用户一些事件,比如有一个新的更新可以使用了或者在更新期间发生错误等。这种用户界面可以通过设置默认的用户界 面为无效而用自定义应用程序指定的用户界面来代替它,挂钩适当的事件(比如
OnUpdateComplete)并弹出自定义用户界面。对于该例我们将使用默认的用户界面,因此将这个值设置为true 。
(4)UpdateUrl :UpdateUrl 是决定更新程序到何处去寻找更新的。在该例中我们使用一个服务器显式文件来检查更新,因此这个属性应当设置为服务器显式文件的URL 。
在该例中将它设置为:http://yourWebserver/SampleApp_ServerSetup/UpdateVersion.xml。请用你的Web 服务器名
称来代替“yourWebserver ”。
Downloader 属性:AppUpdater 组件有两个子组件。第一个称之为Downloader,它控制组件的下载和Poller属性:AppUpdater的第二个子组件是Poller,Poller控制更新检查。
(1)AutoStart:布尔值,在应用程序启动时控制Poller 是否应当开始轮询或它是否应当等待直到有计划的更新查询开始。
(2)DownloadOnDetection:布尔值,控制Poller 在一个新的更新发现时是否立即开始下载更新,或者是否通过调用DownloadUdpate() 方法开始显式下载。
(3)InitialPollInterval:应用程序启动后在第一次执行更新检查前等待的秒数。
(4)PollInterval:第一次更新检查之后,PollInterval 控制后续每次更新检查之间间隔的秒数,注意:默认为每30 秒进
行一次检查;显然,你会希望你的应用程序减少更新检查的频率。
所有这一切完成之后,你的属性表格看起来应当是下面这个样子:
Samples\SampleApp\SampleApp_Complete目录包含应用程序正确安装的一个版本。 |
安装:
(1)DownloadRetryAttempts:在下载期间如果有错误发生(比如Web 服务器宕机)downloader 会稍后重试。这个属性控制downloader 认为是彻底的应用程序更新错误之前重试网络请求的次数。
(2)SecondsBeteweenDownloadRety:重试网络请求之前等待的秒数。
(3)UpdateRetryAttempts: 在更新期间如果发生严重错误,(例如downloader 超过了试图重试的次数),就会产生一个应用程序更新错误。默认的,将会停止试图更新。但是在应用程序下一次启动时会试图恢复(比如更新Web 服务器可能会宕机好几天)。这个属性控制试图更新的次数。如果超过这个值,updater 就会取消更新,重置它的状态并返回更新检查。
(4)ValidateAssemblies: 这个属性控制下载程序集有效完成的级别。更多信息参见这篇文章的安全一节。
第四步:生成并在客户端部署应用程序V1版本。
在SampleApp项目中,打开AssemblyInfo.cs文件。将AssemblyVersion的值从“1.0”修改为“1.0.0.0”。这会引起在生成程序集时获得值为“1.0.0.0”.. 的标记,该标记代替VS.NET通常指定为递增的值。
1. 生成应用程序。
2.从压缩文件中将Samples\SampleApp\SampleApp_ClientSetup目录拷贝到你的本地机器上。要注意此目录已经包含 了AppStart.exe。AppStart.config已经设置为指向1.0.0.0目录并且启动SampleApp.exe。
从SampleApp的release目录下拷贝SampleApp(Appupdater.dll,SimpleForm.dll和SampleApp.exe)
到客户端SampleApp_ClientSetup\1.0.0.0目录下。至此,一个功能完整的应用程序版本已被“安装”到了客户端,可以通过运行AppStart.exe来执行。
第五步:安装Web服务器
在这一步我们将安装Web服务器以提供更新轮询功能。.NET应用程序更新组件使用HTTP-DAV来下载应用程序更新,因此需要一个支持HTTP-DAV的Web服务器。Windows 2000上的IIS5.0和更新的操作系统都支持HTTP-DAV。
1.将Samples/SampleApp_ServerSetup目录拷贝到你的Web服务器上的wwwroot目录下。
2. 将SampleApp的V1版本拷贝到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 并修改AvailableVersion为2.0.0.0。修改ApplicationURL为指向新的2.0.0.0路径。
7. 保存对UpdateVersion.xml所做的修改。
一旦你保存了新的UpdateVersion.xml,在30秒之内,运行中的SampleApp拷贝将会探测到新的可用版本。
四、按需安装、安全、可扩展性及调试
(一)按需求安装
所谓按需求安装,指只有主可执行程序被显式安装到客户机上。应用程序剩下的部分可以根据基本需要自动下载和安装。
通过.NET应用程序更新组件的AutoFileLoad属性来启动按需求安装。你必须仔细考虑在你的应用程序中程序集边界所处的位置以及什么动作会引 起程序集被下载。由于程序集的下载涉及到网络输入输出,因此下载所花费的时间是可变的。在程序集下载期间,应用程序会被冻结等待程序集下载完成。
(二)部署安全
自动安装应用程序更新的能力具备很多好处,但它也伴随着一些潜在的危险。当你使得安装更新变得简单时,如果不小心,你也可能使得安装恶意代码变得简单。 有两种危险,第一种危险是有人会用自己的Web服务器欺骗用来部署更新的Web服务器。他们可能会利用那台Web服务器在你的应用程序路径安装一个病毒程 序。阻止欺骗或其它通过网络进行的不正当干预的最简单的方法是使用HTTPS。要和.NET应用程序更新组件一起使用HTTPS,可以简单地用HTTPS URLs来代替HTTP URLs。当然,HTTPS 不是银弹。使用HTTPS有两个问题,第一是可伸缩性。使用HTTPS需要服务器加密所有从Web服务器上下载的文件。如果一个应用程序的更新文件很大, 加密更新文件的代价会使服务器的负担过重。使用HTTPS的另一个问题是它对第二种安全危险毫无益处。第二种危险是黑客既可能从内部也可能从外部来攻击你 的服务器。一旦攻击成功,则有可能意味着成百上千的客户端也通过自动更新遭受连累,这种情况将是灾难性的。
为解决这个问题,.NET 应用程序更新组件使用给.NET程序集添加强名称的特性来验证所下载的程序集。如果.NET应用程序更新组件检测到下载期间一个程序集不是使用你的密钥签 名的,下载就会取消。这意味着只有拥有你的应用程序私钥的人才能够建立可自动部署的更新文件。
要验证程序集有效,.NET应用程序更 新组件验证你当前安装的应用程序可执行程序的公钥和下载的更新的公钥是否匹配。如果两个程序集以相同且保密的私钥签名,那么嵌入的公钥也就相同。因为被 CLR加载的程序集为了验证它的公钥,CLR计算它正常的哈希值检查来保证程序集实际上就是真正的程序集而不是被做了手脚的程序集。为了能够在下载时验 证,可以给你所有的应用程序集添加强名称并将.NET应用程序更新组件的ValidateAssemblies属性设置为true。
在下载时进行程序集验证会起很大的作用,但实际上,应用程序会经常有不同私钥签名的组件。比如,你的应用程序可能有两个文件:使用你的私钥签名的可执行程序集和另一个包含你购买的应用在你的应用程序中的第三方图表控件的dll程序集。第三方程序集可能使
用 第三方而不是你自己的私钥来签名。使情况变得更为复杂的是,在你的应用程序中用来签名程序集的有效私钥的设置随着版本号的改变可能会发生变化。你该如何自 动更新那些应用的类型?为解决这个问题,你可以在你的应用程序中生成一个包含有效公钥的列表的程序集。将该程序集使用应用程序的主私钥(应用程序的exe 文件签名的密钥)签名并把该程序集放到Web服务器上和应用程序更新文件一起的目录下。在更新下载过程开始之前,.NET应用程序更新组件将会检查Web 服务器上应用程序更新目录下一个名为"AppUpdaterKeys.dll"的的程序集。如果存在,该程序集就会被下载。该程序集会被拿来和主应用程序 的公钥对比验证。如果签名有效,密钥列表会被提取出来。从此之后,任何处于该列表中的密钥会被认为是更新文件的有效签名。
有关安全方面推荐的方法是使用HTTPS URLs来实现更新检查。这会提供第一级别的欺骗保护。对于更新下载,最好不要使用HTTPS RLs以避免使你Web服务器的负荷过重。而是给你的应用程序的程序集添加强名称并使用程序集验证特性。
(三)可扩展性
在这篇文章前面讲过的示例中我们简单地通过拖放一个组件到应用程序中并设置一些属性来实现自动部署。
虽然这在许多应用程序中工作的很好,但在一些应用程序中会需要高级别的控制,这只能通过写代码来获得。我们可以编写自己的代码来替换掉.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应用程序更新组件不失为开始建立自动更新应用程序的一种重要的方法
出自:csdn,在天极上看到的,还没仔细研究,留做以后参考
问题一:
自动升级组件中的错误:没法实例化Http请求流
在下载新版本时,需要请求列举目录文件,获得服务器该目录下所有文件,然后下载。
但发出XML请求的时候报错了。
HttpWebRequest Request = (HttpWebRequest)HttpWebRequest.Create(url);
Request.Headers.Add("Translate: f");
Request.Credentials = CredentialCache.DefaultCredentials;
string requestString = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"+
"<a:propfind xmlns:a=\"DAV:\">"+
"<a:prop>"+
"<a:displayname/>"+
"<a:iscollection/>"+
"<a:getlastmodified/>"+
"</a:prop>"+
"</a:propfind>";
Request.Method = "PROPFIND";
if (deep == true)
Request.Headers.Add("Depth: infinity");
else
Request.Headers.Add("Depth: 1");
Request.ContentType = "text/xml";
Request.ContentLength = requestString.Length;
Stream requestStream = null;
try
{
requestStream = Request.GetRequestStream();
//创建流的时候失败了。501错误。服务器不支持该函数!
//但我已经把该目录设为可“浏览目录“了. 不支持DAV??不明白了
}
catch (Exception er)
{
Debug.WriteLine("Z.J.Chen: Can't create a http request stream! "+er.Message);
requestStream.Close();
}
问题二:
最近一直忙于smartClient,对于自动更新方面,使用了AppUpdater组件,原因是简单易用,taskVision中用的就是它:)可以通过下面的地址获得更加详细的信息:
http://windowsforms.net/articles/appupdater.aspx
哪知刚部署就发现了一个严重的问题,每次update完成之后,应用程序目录下非装载Assembly的目录老是被自动删除(譬如我有一个 Setup目录下面有products.mdb文件,此Setup目录老是被自动删除),然后重新启动应用程序后又重新自动安装了,非常讨厌,同时也是百 般不解,本来对于VS.NET的Setup Project也不是特别了解,一直以为是配置有问题,试了N次还是找不到解决办法,最后才把目标锁定在AppUpdater组件上面,经过一翻追踪调 试,发现问题所在,在Downloader.cs中的Scavenge方法,这个”清道夫”实在太霸道了,将应用程序目录下的非装载Assembly的目 录全部都删除了。找到问题所在,简单将那个if语句修改一下,我简单的修改为:
if(Directory.Name.ToLower() == "appupdater") {//HardDirectoryDelete}
也可根据实际要求修改,反正不要让他这么“狂妄”就是了:)
简单的一点东西,花去了1整个上午的时间去调试,希望能对碰到此问题的朋友有帮助,不要再花费这个冤枉时间了。另外,如果更新服务器是windows2003,记得设置WEB_DAV为允许。