我有几个MSI文件安装不同的应用程序。所有这些包共享相同的底层运行时,基本上是一组DLL。此运行时作为合并模块引入每个安装程序。安装几个这样的软件包工作得很好,运行时的最新版本总是留在系统上,当最后一个软件包被删除时,所有东西都会从系统中删除。
现在,我不得不将其中一个DLL拆分为2个,并在运行时添加一个新组件来安装新的DLL。这个新的DLL与运行时的其他库链接。现在假设以下场景:
- 安装一个带有合并模块的旧包,用于不带新DLL的运行时
- 使用运行时的合并模块的较新版本安装一个新的不同包。现在系统上有2个包
- 再次删除新程序包
现在旧包坏了,因为:
- 新DLL的新组件的引用计数为1,因为旧包没有引用计数,因此被删除
- 其他运行时DLL保留在系统上,因为它们仍然被较旧的包引用。然而,由于它们是新的,它们已经与现在不再存在的新DLL链接
所以我的问题是:
- 是否有一种方法可以在WiX代码中明确声明文件a依赖于文件B,从而使其保留在系统上,直到所有引用都被卸载
- 或者有没有一种方法可以显式地降级依赖项,使依赖项不再存在
- 我是不是做错了什么
我在一台干净的机器上尝试的是遵循SteinÅsmul的建议:
<Component Id='OldLibsNowDependingOnNewLib' Guid='C8DCD2AB-CBE5-4853-9B25-9D6FE1F678DD'>
<File Id='LibOne' Name='LibOne.dll' Source='$(var.SourceDir)/LibOne.dll' />
<File Id='LibTwo' Name='LibTwo.dll' Source='$(var.SourceDir)/LibTwo.dll' />
</Component>
<Component Id='NewLibComponent' Guid='CD2DB93D-1952-4788-A537-CE5FFDE5F0C8' Shared='yes'>
<File Id='LibNew' Name='LibNew.dll' Source='$(var.SourceDir)/LibNew.dll' />
</Component>
然而不幸的是,这并没有改变人们的行为。
UPDATE:再次查看SDK,我看到组件的标志
msidbComponentAttributesShared
。这你描述的问题看起来很有希望。请尝试启用该标志并重新编译安装程序的版本2(除非现场)。为有问题的组件启用共享标志(最后一部分):
<Component Feature="Product" Shared="yes">
这似乎是为了支持补丁,但也许它也适用于您的情况来自MSI SDK:
"If a component is marked with this attribute value in at least one package installed on the system, the installer treats the component as marked in all packages. If a package that shares the marked component is uninstalled, Windows Installer 4.5 can continue to share the highest version of the component on the system, even if that highest version was installed by the package that is being uninstalled."
我认为以上应该有效,但现在没有时间测试。请留下以下内容以供审查。
简短回答:使用WiX的Burn(设置链接器)按顺序安装应用程序设置和可以处理的新的独立运行时设置独立于您的应用程序安装版本。
先决条件设置:有趣的案例。这就是为什么我喜欢将先决条件拆分为自己的MSI包,并通过Burn Bundle Bootstrapper
进行部署。Burn是WiX的bootstrapper
/downloader
/chainer
,本质上是一种按顺序运行多个设置的方式,有几种不同的格式,如MSI
、EXE
、MSU
、MSP
。当这样做时——将运行时放在自己的MSI中——没有纠缠,并且可以很好地解耦运行时和特定于应用程序的文件。换句话说:您可以使用自己的MSI更新运行时文件。这些文件甚至会有一个reference count
的1
,这意味着你可以轻松地卸载所有文件(如果你通过合并模块安装,也可以包含在其他软件包中-更多信息请参阅下文)。
合并模块-半静态链接:奇怪的是,合并模块有点像半静态链接。整个合并模块是一个版本——一个二进制捆绑包(想想COM)——但它的安装行为只是"更高版本的胜利"之一。因此,带有最新合并模块的单个较新MSI将更新所有使用它们的应用程序的共享文件。然后卸载将执行您所看到的操作:保留原来由旧设置安装的文件。
选项:在您的情况下,一个"解决方案"可能是用新的合并模块重新编译旧的设置,然后重新安装,我知道您不喜欢。我也不喜欢。我想这根本不是解决办法。其他一些建议:
- 永久组件:您可以将新文件的托管组件设置为系统上的永久组件。这很容易,但也很愚蠢,并不总是很可取。卸载将不会删除该文件
- 先决条件:这是我上面提到的最喜欢的选项。您编译了安装运行时组件的必备MSI安装程序。此MSI可以在不影响主应用程序的情况下向自身提供更新。这是我追求的主要好处:
Cohesion
&Coupling
好处。- 合并模块:我会完全避免合并模块,但如果您已经有了合并模块,则通常会将相同的合并模块合并到必备设置中。
- 在大多数情况下,合并合并模块是可以的,因为您可以安装先决条件,然后可以在不影响运行时的情况下随意安装和卸载应用程序版本,因为不同的产品(先决条件MSI)安装了运行时,并且该安装程序应该保留下来,而不是卸载
- 如果合并模块不起作用,并且带来了您已经存在的冲突,那么可以尝试与上面提到的
msidbComponentAttributesShared
"解决方案"相结合。到目前为止还没有经过我的测试。提出这样的建议总是有风险的,但这是"最大的努力">
- WiX Include Files:我更喜欢使用WiX Inclusive Files,它允许我在不重新创作二进制格式的整个合并模块的情况下拉入新文件(想想C++Include File,而不是合并模块的COM风格的二进制重用)
- 合并模块:我会完全避免合并模块,但如果您已经有了合并模块,则通常会将相同的合并模块合并到必备设置中。
- 并排:许多人更喜欢并排安装必备组件,以便运行时的几个版本可以共存。这可能涉及也可能不涉及GAC。切换运行时版本将是一项清单操作任务。通常有点令人困惑,但可行。您可以使用合并模块和单独的MSI文件来部署这样的运行时,如上所述。我肯定会使用先决条件MSI
我现在想不出更多了,但我知道这次我忘记了一些重要的事情。让我坚持我现在所拥有的,以防它激发你的想法。
复杂的先决条件设置:请注意,先决条件MSI文件对于公司部署来说并不那么糟糕,因为部署系统将允许定义MSI文件之间的关系并设置部署链。对于家庭用户来说,你可以很容易地将所有东西包装在一个大的setup.exe
中。
无意义选项:没有意义的选项是将新文件滚动到两个安装版本中。没有收益,开销很大。有些人喜欢将新文件本地复制到主安装文件夹中。不起作用,因为它链接到的文件可能在其他地方(运行时位置)我认为静态链接在这种情况下是不相关的。我想,这只是解决现场问题的最后手段。设置SharedDllRefCounter标志不会影响MSI引用计数,而是用于传统引用计数(非MSI设置),尽管手动调整是一个紧急"解决方案">人们最终的最后手段通常是放弃运行时安装,并将所有内容安装到同一安装文件夹中。然后,您必须始终为每个版本重新编译所有内容——这是您想要避免的?
一些链接:
- WiX(Windows安装程序Xml),创建通用变量
- 预处理器结构、功能、刻录捆绑包及其他