(接上篇)
按需求安装
通过利用.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应用程序更新组件不失为开始建立自动更新应用程序的一种重要的方法。
(全文完)