WiX 条件功能/组件在卸载时孤立

发布于 2024-12-04 18:45:01 字数 4383 浏览 3 评论 0原文

编辑:引用我自己的话,因为我在下面的评论之一中更好地总结了这个问题......

安装软件包时我有一个条件为真,但是 删除后不正确。我希望 MSI 能够记住它有 安装了条件组件并通过卸载将其删除, 但事实并非如此。我试图找出 A) 的正确方法 清理这个孤立的组件,B)最好的保护方法 以后再解决这个问题。

我想我的问题归结为,在卸载产品后删除孤立的功能/组件是否安全?有什么方法可以检查哪些组件(如果有的话)仍在引用我认为是孤儿的组件?我该如何修复我的安装程序以防止将来发生这种情况?

我们有一个 wix 项目来安装库 Foo。默认情况下,此安装程序会将 Foo.dll 的副本放入 GAC 和 Program Files\Reference Assemblies\Foo\ 文件夹中。安装程序还添加了两个注册表项,一个是自定义项,用于存储 Foo 文件夹的路径以供将来安装时重用,另一个告诉 Visual Studio 将完整的 文件夹路径包含在它搜索已安装的库,以便 Foo 显示在“添加引用”对话框中。计算机上一次可以安装多个版本的 Foo 库,每个版本都位于 Foo 下相应的 文件夹中。

Foo 2.0.0 有一个错误未能通过测试,Foo 2.0.1 包含错误修复,没有其他更改。我们决定,由于错误修复是唯一的更改,因此我们将向 GAC 添加一个策略文件,该文件会将 Foo 2.0.0 的引用重定向到 Foo 2.0.1。该策略文件作为新功能内的新组件添加到安装程序中。添加了升级标签,用于在安装 Foo 2.0.1 时检测并删除 Foo 2.0.0。策略功能的安装以检测到 Foo 2.0.0 为条件。一切似乎都正常,Foo 2.0.1 被推出了。

现在,一年后,我们发现我们再次错过了一个错误,这次是在安装程序设置中,而不是在库代码中。事实证明,当 Foo 2.0.1 替换 2.0.0 然后被卸载时,策略文件将被孤立并保留在 GAC 中,而所有其他文件和密钥都将被删除。我已经在 Windows 的全新安装(虚拟机非常有用)上对此进行了测试,并确认该问题可以复制,即没有对该组件的其他引用潜入导致其留下来。

所有这些最初都是在 WiX 3.0 中完成的,但我们最近升级为使用 WiX 3.5。我们的 WiX 代码如下所示:

<Product Id="Guid 1" Name="Foo v2.0.1" Language="1033" Version="2.0.1" Manufacturer="My Team" UpgradeCode="Guid 2">

  <Package InstallerVersion="300" Compressed="yes" />

  <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

  <Upgrade Id="Guid 2">
    <UpgradeVersion Minimum="2.0.0" Maximum="2.0.0" IncludeMaximum="yes" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADE2X0X0"></UpgradeVersion>
  </Upgrade>

  <Property Id="FOODIR">
    <RegistrySearch Id="FooPath" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
  </Property>

  <Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="ProgramFilesFolder">
      <Directory Id="RefAssemb" Name="Reference Assemblies">
        <Directory Id="FOODIR" Name="Foo">
          <Component Id="FooLibPath" Guid="Guid 3">
            <RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
              <RegistryValue Name="InstallPath" Type="string" Value="[FOODIR]" KeyPath="yes"></RegistryValue>
            </RegistryKey>
          </Component>
          <Directory Id="FOOVERSION" Name="v2.0.1">
            <Component Id="Foo_VSFile" Guid="Guid 4">
              <File Id="Foo_DLL" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes"></File>
            </Component>
            <Component Id="Foo_VSRegKey" Guid="Guid 5">
              <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v3.5\AssemblyFoldersEx\Foo v2.0.1" Action="createAndRemoveOnUninstall">
                <RegistryValue Type="string" Value="[FOOVERSION]" KeyPath="yes"></RegistryValue>
              </RegistryKey>
            </Component>
            <Directory Id="FOOGAC" Name="GAC">
              <Component Id="Foo_GAC" Guid="Guid 6">
                <File Id="Foo" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes" Assembly=".net"></File>
              </Component>
              <Component Id="Foo_Policy_2x0x1" Guid="Guid 7">
                <File Id="Foo_PolicyDLL" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.dll" KeyPath="yes" Assembly=".net"></File>
                <File Id="Foo_PolicyConfig" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.config" CompanionFile="Foo_PolicyDLL"></File>
              </Component>
            </Directory>
          </Directory>
        </Directory>
      </Directory>
    </Directory>
  </Directory>

  <Feature Id="ProductFoo" Level="1">

      <ComponentRef Id="Foo_GAC"/>

      <Feature Id="Foo_VSSupport" Level="1">
        <ComponentRef Id="FooLibPath"/>
        <ComponentRef Id="Foo_VSFile"/>
        <ComponentRef Id="Foo_VSRegKey"/>
      </Feature>

      <Feature Id="Foo_Policy_v2x0x1" Level="0">
        <ComponentRef Id="Foo_Policy_2x0x1"/>
        <Condition Level="1">UPGRADE2X0X0</Condition>
      </Feature>

  </Feature>

</Product>

Edit: Quoting myself because I summarized the issue much better in one of the comments below…

I have a condition that is true when the package is installed, but
not true when it is removed. I expected MSI to remember that it had
installed the conditional component and remove it with the uninstall,
but this is not the case. I am trying to find out A) the proper way to
clean up this orphaned component, and B) the best way to protect
against this problem in the future.

I guess my question boils down to, is it safe to just delete an orphaned feature/component after a product is uninstalled? And is there any way to check what, if anything, is still referencing a component that I believe to be an orphan? And how do I fix my installer to keep this from happening in the future?

We have a wix project to install a library, Foo. This installer puts copies of Foo.dll into the GAC, and a folder, Program Files\Reference Assemblies\Foo\<version> by default. The installer also adds two registry keys, one is a custom key which stores the path of the Foo folder for reuse in future installs, the other tells Visual Studio to include the full <version> folder path in its search for installed libraries so that Foo shows up in the “Add References” dialog. Multiple versions of the Foo library can be installed on the machine at a time, each will be located in the appropriate <version> folder under Foo.

Foo 2.0.0 had a bug that slipped through testing, Foo 2.0.1 contained the bug fix, no other changes. It was decided that since the bug fix was the only change, we would add a policy file to the GAC which would redirected references for Foo 2.0.0 to Foo 2.0.1. This policy file was added to the installer as a new component inside of a new feature. An upgrade tag was added to detect and remove Foo 2.0.0 when Foo 2.0.1 was installed. The installation of the policy feature was made conditional on Foo 2.0.0 being detected. Everything seemed to be working and Foo 2.0.1 was pushed out.

Now, a year later, we discover that we again missed noticing a bug, this time in the installer setup rather than the library code. It turns out that when Foo 2.0.1 replaces 2.0.0, and is then uninstalled, the policy file is orphaned and remains in the GAC while all other files and keys are removed. I have tested this on a clean install of windows (virtual machines can be so useful) and confirmed that the problem can be replicated, i.e. no additional references to the component have snuck in to cause it to stay behind.

All of this was originally done in WiX 3.0 but we have recently moved up to using WiX 3.5. Our WiX code looks like this:

<Product Id="Guid 1" Name="Foo v2.0.1" Language="1033" Version="2.0.1" Manufacturer="My Team" UpgradeCode="Guid 2">

  <Package InstallerVersion="300" Compressed="yes" />

  <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

  <Upgrade Id="Guid 2">
    <UpgradeVersion Minimum="2.0.0" Maximum="2.0.0" IncludeMaximum="yes" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADE2X0X0"></UpgradeVersion>
  </Upgrade>

  <Property Id="FOODIR">
    <RegistrySearch Id="FooPath" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
  </Property>

  <Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="ProgramFilesFolder">
      <Directory Id="RefAssemb" Name="Reference Assemblies">
        <Directory Id="FOODIR" Name="Foo">
          <Component Id="FooLibPath" Guid="Guid 3">
            <RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
              <RegistryValue Name="InstallPath" Type="string" Value="[FOODIR]" KeyPath="yes"></RegistryValue>
            </RegistryKey>
          </Component>
          <Directory Id="FOOVERSION" Name="v2.0.1">
            <Component Id="Foo_VSFile" Guid="Guid 4">
              <File Id="Foo_DLL" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes"></File>
            </Component>
            <Component Id="Foo_VSRegKey" Guid="Guid 5">
              <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v3.5\AssemblyFoldersEx\Foo v2.0.1" Action="createAndRemoveOnUninstall">
                <RegistryValue Type="string" Value="[FOOVERSION]" KeyPath="yes"></RegistryValue>
              </RegistryKey>
            </Component>
            <Directory Id="FOOGAC" Name="GAC">
              <Component Id="Foo_GAC" Guid="Guid 6">
                <File Id="Foo" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes" Assembly=".net"></File>
              </Component>
              <Component Id="Foo_Policy_2x0x1" Guid="Guid 7">
                <File Id="Foo_PolicyDLL" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.dll" KeyPath="yes" Assembly=".net"></File>
                <File Id="Foo_PolicyConfig" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.config" CompanionFile="Foo_PolicyDLL"></File>
              </Component>
            </Directory>
          </Directory>
        </Directory>
      </Directory>
    </Directory>
  </Directory>

  <Feature Id="ProductFoo" Level="1">

      <ComponentRef Id="Foo_GAC"/>

      <Feature Id="Foo_VSSupport" Level="1">
        <ComponentRef Id="FooLibPath"/>
        <ComponentRef Id="Foo_VSFile"/>
        <ComponentRef Id="Foo_VSRegKey"/>
      </Feature>

      <Feature Id="Foo_Policy_v2x0x1" Level="0">
        <ComponentRef Id="Foo_Policy_2x0x1"/>
        <Condition Level="1">UPGRADE2X0X0</Condition>
      </Feature>

  </Feature>

</Product>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

老娘不死你永远是小三 2024-12-11 18:45:01

在执行后删除孤立的功能/组件是否安全?
产品已卸载?

不,不是。如果您只是将其删除,其组件注册信息仍保留在机器上。

有什么方法可以检查仍在引用的内容(如果有的话)
我认为是孤儿的组件?

并不真地。但是,如果有某些内容引用您的某个组件,则很可能是您开发的另一产品或当前产品的旧版本未正确卸载。

随机产品不太可能引用您的组件或组件。

我该如何修复我的安装程序以防止这种情况发生
未来?

使用重大升级,卸载旧组件并安装新组件。没有特殊的策略文件,没有条件安装或删除。

机器上可以安装多个版本的Foo库
每次,每个都将位于适当的文件夹中
在 Foo 下。

为什么?如果您只有单一产品,则可以使用重大升级。这样,用户将只安装一个版本的程序集。

并排安装的版本化程序集仅对不同的产品有意义。

我们决定,由于错误修复是唯一的更改,因此我们将
将策略文件添加到 GAC,该文件将重定向 Foo 的引用
2.0.0 到 Foo 2.0.1。该策略文件已作为
新功能中的新组件。

这是一个黑客行为,很可能这就是导致问题的原因。您的新安装应该已经卸载了旧版本以及 Foo 2.0.0。

主要升级应该始终是独立的。

is it safe to just delete an orphaned feature/component after a
product is uninstalled?

No, it's not. If you just delete it, its component registration information is still left on the machine.

And is there any way to check what, if anything, is still referencing
a component that I believe to be an orphan?

Not really. But if there is something referencing one of your components, it's most likely another product developed by you or an older version of your current product which wasn't uninstalled correctly.

It's very unlikely that a random product would reference your component or assembly.

And how do I fix my installer to keep this from happening in the
future?

Use major upgrades which uninstall the old component and install the new one. No special policy files, no conditional installations or removals.

Multiple versions of the Foo library can be installed on the machine
at a time, each will be located in the appropriate folder
under Foo.

Why? If you have a single product, you can use major upgrades. This way the user will have only one version installed with only one version of your assembly.

Versioned assemblies installed side by side make sense only for different products.

It was decided that since the bug fix was the only change, we would
add a policy file to the GAC which would redirected references for Foo
2.0.0 to Foo 2.0.1. This policy file was added to the installer as a
new component inside of a new feature.

This is a hack and most likely this is what is causing the problem. Your new installed should have uninstalled the old version along with Foo 2.0.0.

Major upgrades should always be standalone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文