【IT168 应用技巧】完全支持 Windows 7 的 Microsoft Application Virtualization (App-V) 4.6 即将发布;很多计划部署 Windows 7 的客户都将 App-V 纳入为他们的桌面转换项目的组件。(操作系统部署常常会使“最新桌面”或“下一代桌面”计划中的应用程序和基础结构发生重大改变。)
当 IT 专业人员考虑同时对 App-V 和 Windows 7 进行投资时,几乎总会提出以下问题:
听说 App-V 是一个应用程序兼容性解决方案。这是否意味着它可以帮助我的应用程序与 Windows 7 兼容?
我是否需要对已为当前的 Windows XP 桌面创建的 App-V 程序包重新排序?
将 App-V 用作部署解决方案时,我该怎么做才能解决不兼容应用程序问题?
下面开始逐个探讨这些问题。
Microsoft App-V 是不是应用程序兼容性解决方案?
Microsoft App-V 首先是应用程序管理和部署解决方案,它可为企业带来明显好处:降低打包成本,提高系统稳定性,通过动态访问软件资产为现在移动性很高的工作人员提供支持。但是,在营销宣传中,人们过多使用了应用程序兼容性这个术语,随着时间的推移,该术语已被曲解为:App-V 可帮助解决应用程序与操作系统之间的兼容性问题。大多数情况下,它是无法解决这些问题的。(现在的例外情况主要是历史原因造成的,不应夸大它的作用,因此,我在此不进行详细介绍。)
对于客户因此产生的混淆,我们在措辞上进行了一定的澄清;我们现在已不使用“应用程序兼容性”这个术语,而是直接谈及基本优点:可以减少应用程序之间的冲突(请注意,我们有意避开了“兼容性”这个词),因此,可以显著减少回归测试。
产品团队对应用程序与操作系统兼容性的官方观点如下:
从以前的讨论可知,App-V 不是通用的应用程序与操作系统兼容性解决方案;不过,如果应用程序兼容性填充程序允许某个应用程序以本机方式(非虚拟化)在给定 Windows 版本上运行,则在大多数情况下,对于大多数填充程序而言,该应用程序将在对填充的应用程序虚拟化之后与 App-V 一起运行。因此,一般而言,只要填充的应用程序能够以本机方式在目标操作系统版本上运行,App-V 就支持应用程序使用填充程序(这些填充程序是作为 Microsoft 的应用程序兼容性工具的组成部分提供的)。
因此,很显然,App-V 无意作为应用程序与操作系统兼容性解决方案。(我们将在本文后面部分讨论如何将填充程序与 App-V 结合起来。)让我们看一看应用程序虚拟化对操作系统兼容性产生的其他影响。点击查
Microsoft App-V 是不是程序包兼容性解决方案?
当我们谈及应用程序兼容性时,自然要将程序包兼容性与运行时兼容性区分开。事实上,我们所建议的应用程序兼容性测试过程(如我在 2009 年 6 月的规划您的应用程序兼容性项目文章中的图 3 所示)将安装测试与运行时测试区分开。让我们从产品团队官方指南开始:
通常,可以在一个操作系统上排序,然后在另一个操作系统上运行虚拟化应用程序;但是,这种方案既取决于应用程序,也取决于操作系统,未必适用于所有应用程序/操作系统组合,因为 App-V 不是通用的操作系统兼容性解决方案。如果遇到问题,客户可能需要在运行 App-V 客户端的操作系统环境中进行排序,以便解决这些问题。
这听起来并不怎么样,官方观点基本上是“视情况而定”。但是,当您要考虑衡量风险时,请比较现在所使用的三种基本安装技术:
Setup.exe:这种方式将运行任意代码,从而该任意代码将与应用程序本身的任意代码面临相同的潜在运行时问题,因此必须彻底进行测试。
Windows Installer:这种方式也运行代码来执行安装,但所运行的代码大部分是结构化的声明代码。只有自定义操作的命令性代码是任意的,因此,除了规则中的可量化变化之外(这些规则会影响针对数据库运行的代码以及自定义操作中的任意代码的处理),有望获得更好(不过仍不完美)的兼容性,需要的测试也会更少。
Microsoft App-V:这种方法根本不必运行代码。它只是在一个作为虚拟文件系统的数据 Blob 上进行复制,然后说“好的,您的虚拟文件系统在这里。”这样,您主要关注的问题不是应用程序是否会安装(复制一个数据 Blob 毕竟真的很容易),而是应用程序的运行时兼容性。因此,在所有三种技术中,这种方式的安装测试是最短的。那真是太好了!
因此,尽管没有官方说法表示程序包会全部正常运行,但这种信息流传开来的主要原因似乎是,很多人都将安装问题与运行时问题混为一谈。一般而言,如果投资于 Microsoft App-V,就有望大大降低应用程序安装测试的预计成本。
应用程序与 Microsoft App-V 一起打包时,如何解决运行时兼容性问题?
还记得支持声明中诱人的说法吗?“App-V 将支持应用程序使用填充程序(这些填充程序是作为 Microsoft 的应用程序兼容性工具的组成部分提供的)....”实际上如何去实现这一点?这两种程序是否基本上兼容?
令人高兴的是,答案是肯定的。事实上,可以通过几种不同的方式做到这一点。
填充程序简要介绍
对于不熟悉填充程序的人来说,填充程序是 Microsoft 很少使用的四字母单词之一,它不是某种缩写词。它以英语单词“shim”(垫片)进行比喻,这是一个工程术语,用于描述插在两个物体之间,使它们更好地配合在一起的木片或金属片。在我们的特定环境下,两个物体就是应用程序和 Windows,而垫片材料是使两者更好地一起发挥作用的附加代码,如图 1 和 2 所示。
▲
图 1 应用填充程序之前,应用程序与 Windows 直接交互。
▲
图 2 应用填充程序之后,应用程序与 Windows 间接交互;填充程序代码注入后,它可以修改向 Windows 发出的请求和/或来自 Windows 的响应。
填充程序的作用是通过 API 侦听实现的。Windows API 是使用 DLL 集合实现的。每个针对 Windows 构建的应用程序都导入这些 DLL,并在内存中维护一个由所有这些功能的地址构成的表。因为 Windows 功能的地址位于一个表中,所以,填充程序引擎直接用填充程序 DLL 的地址替换该地址。应用程序通常不知道要将请求发送到填充程序 DLL 而不是发送到 Windows 本身,Windows 也不知道请求是来自应用程序以外的源(因为填充程序 DLL 只不过是应用程序进程内的另一个 DLL)。
例如,一个十分常用的填充程序是版本欺骗填充程序。为了实现此填充程序,将侦听几个用于确定运行应用程序的 Windows 版本的 API。通常,这种信息将传递给 Windows 本身,后者会如实应答。但在应用填充程序之后,这些 API 将被侦听。这样,将返回一个不同的 Windows 版本(例如,Windows XP 而不是 Windows 7),而不是将请求传递给 Windows。如果应用程序编写为只在 Windows XP 上运行,这样可以骗过应用程序,使它认为自己正在正确的操作系统上运行。(通常,这样就可以解决应用程序兼容性问题!)
使用填充程序,可以采用很多技巧。例如,
ForceAdminAccess 填充程序尝试使应用程序确信当前用户是本地管理员组的成员,即使他并不是该组成员。(如果您不是本地管理员,很多应用程序会彻底失败,不过,您可以使用其他一些技巧(如 UAC 文件和注册表虚拟化)来解决初始检查所引起的问题。)实现这种检查的方法可以非常简单。例如,此填充程序从 shell32.dll 侦听 API IsUserAnAdmin。所填充的功能的完整源代码(与实际 API 相比,具有极好的性能特性)只返回 TRUE。
WrpMitigation 填充程序使应用程序安装程序相信,它们可以写入受 Windows 资源保护 (WRP) 功能保护的文件。如果尝试写入受保护的文件,该填充程序首先创建一个新的临时文件,一旦句柄关闭就将其标记为已删除,然后将句柄返回到该临时文件,就好像它是实际受保护的文件。应用程序将旧版本 kernel32.dll 或 shell32.dll(或者在将其打包时它所选择的任何其他文件)安装到临时文件中,但随后该临时文件消失,受保护文件的经过修补的最新匹配版本保留在文件系统中。这样,WRP 仍可确保您不会在自己的计算机上最终得到来自 Windows 95 的旧版本 shell32.dll,但在使用此填充程序时,安装程序不会因 ACCESS_DENIED 而失败。
CorrectFilePaths 填充程序可将文件从一个位置重定向到另一个位置。因此,如果应用程序尝试写入 c:\myprogramdir(不会使用 UAC 文件和注册表虚拟化对它进行自动修复),则可以将在运行时修改的文件重定向到基于每个用户的位置。这样,您可以以标准用户的身份运行而不必放宽访问控制列表 (ACL) 限制,因为您知道,安全人员不愿意放宽 ACL。
有数百个通用填充程序可用来解决应用程序兼容性问题,利用这些修补程序可节省大量的成本。例如,客户常常在以下情况下使用填充程序:
供应商已停止经营,因此无法获得更新版本。如果您无法承担从新供应商处获得另外的应用程序或自己构建新版本的费用,这样可以为您争取一些时间。
应用程序不是十分重要,不值得投资于更新版本(伴随着支持声明),但您的用户乐于使用它,所以您不介意他们是否运行不受支持的版本。
应用程序是内部开发的,但您不想非要等到团队发布完全更新的版本。相反,您愿意采用临时性修补程序,允许开发团队在应用程序的下一次计划发布时发布永久性修补程序。通过这种方法,就不再需要停止所有应用程序开发活动,可以投入时间和资源来修复兼容性 Bug。您可以使用临时性修补程序,并允许团队发布包含开发过程中已实现的新功能的修补程序。
利用填充程序解决应用程序兼容性问题,可以显著节省成本,大大加速 Windows 7 的部署。
在企业环境中管理填充程序的方法
在我于 2007 年 11 月发表的在企业环境中管理填充程序白皮书中,概括了在决定如何在组织中将填充程序作为应用程序兼容性解决方案进行管理时,大多数人会选择的两种主要方式。简单地说,它们是:
集中管理的单个填充程序数据库 通过这种方式,可以部署集中管理的单个填充程序数据库,这种数据库包含所有需要使用填充程序的应用程序的条目。这之后,发现其他应用程序需要使用修补程序时,可向中央数据库推出更新。这就是 Microsoft 所使用的方法。在发布 Windows 7 时,我们提供了一个系统填充程序数据库,它可以对数千个应用程序进行修复,在我们发现其他需要修复的应用程序时,则通过 Windows Update 向该数据库发布更新。您也可以这样做:将一个填充数据库放到主映像中,通过系统管理软件(如 System Center Configuration Manager)进行更新。
基于每个应用程序的填充程序数据库 另外,您也可以直接向应用程序部署应用程序修补程序。如果只有一两个应用程序需要修补程序,某些客户会选择这种方式,因为在小型环境中,与设置一个用于管理中央数据库的进程相比,这种方式成本更低。基于每个应用程序的填充程序数据库的一个明显缺点是,如果存在问题,更新填充程序数据库将会很困难。不过,App-V 没有这个缺点,现在,您可以对基于每个应用程序的数据库进行更新,然后将更新后的版本传送给用户。(不过,您将会看到,这引入了另外一个更加明显的缺点。)
Microsoft App-V 的软件部署方法拓宽了在企业范围内部署应用程序修补程序的选择,您可以选择一种最适合组织中现有进程模型的方法。
使用集中管理的单个填充程序数据库来填充 App-V 应用程序
从策略角度看,要部署集中管理的单个填充程序数据库并由使用 App-V 进行排序的应用程序选择该数据库,您必须做什么?很简单,按常规方法安装即可!使用 App-V 安装的应用程序的启动方式与应用程序的常规启动方式大致相同,这种应用程序使用应用了填充程序的加载程序机制。它们只是通过代理进程来启动。具体地说,sfttray.exe(而不是资源管理器)负责启动新的进程。因此,进程树如图 3 所示。
▲
图 3 代理进程树
应用程序启动时,它像任何其他应用程序一样运行加载程序。Microsoft Application Virtualization 客户端接口层 (sftintf.dll) 调用 CreateProcessW,后者调用内部的 API CreateProcessInternalW。填充程序引擎是在 CreateProcessInternalW API 中调用的,填充程序与该进程绑定。
那么,这就相当容易了。还有什么问题吗?是的,有一个。它不能很好地处理提升。例如,您不能简单地要求对某个应用程序进行提升(使用 RunAsAdmin 填充程序),也不能对需要使用 ElevateCreateProcess 进行提升的应用程序的问题进行修复。为什么呢?原因是气泡图。
例如,我们来看一个尝试自行启动一次自动更新的应用程序(遗憾的是,这是一个十分常见的任务)。在以本机方式运行时,它产生了一个问题,即它使用了无法调用提升的 CreateProcess API。它随后返回错误 -1073740756 – STATUS_ELEVATION_REQUIRED。ElevateCreateProcess 填充程序捕获这个返回值,然后调用应用程序信息服务来提供提升。但该服务无法找到要提升的应用程序,因为该服务位于气泡图之外!
因此,只要应用程序不需要提升,在已创建一个进程的情况下,部署单个填充程序数据库解决方案就十分容易,您只需继续做相同的事情。
使用基于每个应用程序的填充程序数据库来填充 App-V 应用程序
通过部署填充程序来解决应用程序兼容性问题的另一种方法是随应用程序部署填充程序。在 MSI 环境中,通常将 .sdb 文件包括在安装程序中,然后加入一个自定义操作,该自定义操作针对它所放置的 .sdb 文件调用 sdbinst。这样,会将自定义填充程序数据库作为安装程序的一部分来安装。(如果您随后需要对该应用程序的填充程序进行更新,则需要找到所有已安装的客户端,并运行一个脚本来安装更新,这会使事情变得有些复杂。)
您可以使用 App-V 完成几乎相同的工作。您可将填充程序数据库 (.sdb) 文件包括在序列本身之中,这样 .sdb 文件就位于气泡图内。首先,对需要使用填充程序的应用程序进行排序,以便其正常运行。然后,将自定义填充程序数据库放入 system32 中。
(尽管我通常会建议人们绝不要将任何内容放到 system32 中,除非您的工资单是由 Steve Ballmer 签发的,并且您的上级包括 Steven Sinofsky;在这种特定情况下,我才会选择这样做,因为这样可简化脚本编写,system32 将在您的路径中,并且不会在生产系统的 system32 中实际留下任何内容,就像是在虚拟文件系统的 system32 中那样)。
做完这项工作后,我将编辑 .sprj 文件。我浏览到“虚拟文件系统”选项卡,然后单击“视图”|“虚拟文件系统”|“添加”。然后,我浏览到放置 .sdb 文件的位置 (c:\windows\system32\appshim.sdb),并单击“确定”。此时,该 .sdb 文件将位于气泡图内,并在安装 softgrid 软件包时进行安装。因为该文件是容器的一部分,所以它将作为应用程序的一部分进行传送(可在更新应用程序时进行更新)。
当然,要使用应用程序修补程序,只是将 .sdb 文件放到文件系统中还不够,还必须安装这些修补程序。不过,填充程序数据库必须安装在 App-V 气泡图的外面。如果将填充程序数据库安装在气泡图内,则在创建进程之前,不会发现虚拟化的 .sdb 文件;创建进程之后,填充程序找到该文件并针对它进行操作已经太晚。您需要使填充程序数据库在创建进程时能够被气泡图外面的加载程序访问。
令人高兴的是,App-V 包含一个将脚本作为应用程序启动进程的一部分来运行的机制。如果您使用最喜欢的文本编辑器来查看应用程序的 OSD 文件,就会发现只能对 XML 进行编辑。
<DEPENDENCY> <CLIENTVERSION VERSION="4.6.0.0"/> <SCRIPT EVENT="LAUNCH" PROTECT="TRUE" TIMING="PRE" WAIT="TRUE" EXTERN="TRUE"> <SCRIPTBODY LANGUAGE="Batch">sdbinst /q appshims.sdb</SCRIPTBODY> </SCRIPT> <SCRIPT EVENT="SHUTDOWN" PROTECT="TRUE" TIMING="POST" WAIT="TRUE" EXTERN="TRUE"> <SCRIPTBODY LANGUAGE="Batch">sdbinst /u appshims.sdb</SCRIPTBODY> </SCRIPT> </DEPENDENCY>
通过该脚本,您可从 App-V 气泡图的外面调用填充程序数据库安装程序 (sdbinst.exe),但要使用位于 App-V 气泡图内部的 .sdb 的一个参数,该 .sdb 随后将作为应用程序部署序列的一部分进行部署。因此,应用程序修补程序的部署和更新十分容易,就像它们是该应用程序的任何其他部分一样。
自定义填充程序数据库的安装过程要求拥有管理员权限;在运行时进行安装这是否会发生问题?您不希望您的用户拥有管理员权限,您的用户也不希望每次启动应用程序时都不得不单击 UAC 对话框。令人高兴的是,您无需那样做;对 sdbinst.exe 的调用会自动提升。安装填充程序的进程树如图 4 所示。
▲
图 4 填充程序安装进程树
将创建一个命令 Shell,该命令 Shell 随后调用 sdbinst.exe 来安装自定义填充程序数据库。在调用应用程序信息服务(提供提升的服务)之前,会失败两次,其错误为 STATUS_ELEVATION_REQUIRED。上述服务调用 consent.exe,后者对策略进行检查以确定是否应提升应用程序。策略(在虚拟环境中)返回“是”而无需提示用户;consent.exe 批准提升,并创建 sdbinst.exe 的一个提升实例,该实例将安装自定义填充程序数据库。但是请务必注意,不提示就提升的策略只有在您能提升的情况下才会无提示运行。如果您是标准用户,则每次启动应用程序都会出现一条需要管理员凭据的提示,每次关闭应用程序也是如此。因为在标准用户环境中人们非常不希望出现这种情况,所以,只有在已部署本地管理员用户的情况下,这种方法才有效。我们希望只有极少的用户需要应用这种方法!
结论
Microsoft Application Virtualization 是一个功能强大的应用程序管理和部署工具。如果在 Windows 7 迁移计划中使用该工具,有可能降低安装测试过程中的成本(不过,成本不会降到 0)。
继续利用大多数同样的过程,还可以使用填充程序解决应用程序兼容性问题。如果要在组织范围内部署单个填充程序数据库,要进行的转换任务很少,只需小心避免提升填充程序(与在 App-V 环境中通常应避免提升的情况大体一样)。如果要部署基于每个应用程序的填充程序,可以使用一些适当的方式来打包和部署应用程序的填充程序数据库,不过,这种情况下,有一个显著的缺点,即无法合理地以非管理员用户的身份运行应用程序。因此,强烈建议(就像在基于 MSI 的应用程序部署中那样)尽量以集中管理的单个数据库的形式部署填充程序,这种情况下,可以使用现有的系统管理软件进行部署。如果采用这种方式,将来在您的用户中,以非管理员身份运行的用户的比重会越来越大。