【问题标题】:Wix installer with both COM dlls and a service带有 COM dll 和服务的 Wix 安装程序
【发布时间】:2017-12-21 13:12:23
【问题描述】:

我们有一个 wix 安装项目,可以安装多个 COM dll 和一个使用 ServiceInstall 的服务。 COM dll 还具有使用 heat.exe 提取的关联注册表项,以避免 SelfRegCost 出现问题。

但是,两者似乎有冲突的要求:

  • COM dlls 注册表项需要 'RemoveExistingProducts After="InstallInitialize"' 以避免在安装后卸载时擦除注册表信息,例如如果升级时修改了 dll 路径。
  • 服务需要 'RemoveExistingProducts After="InstallExecute"'(或更高版本)以避免在升级时丢失服务帐户凭据。

我已经阅读了大量关于 msi、wix、服务和 COM 的相关问题/答案,但没有找到解决方案。

解决这个问题的正确方法是什么?

编辑:

安装程序使用“自动”生成的组件 GUID,并且每个组件只有一个文件。例外是由热量生成的 COM dll 组件,即:

<Component ...>
    <File ...>
        <TypeLib ...>
            <Class ...>
            ...
    </file>
    <RegistryValue ... HKCR...>
    ...
</Component>

它有 2 个自定义操作,用于注册和取消注册 COM 服务器 (exe),因为我不知道还能做什么,因为热量无法提取它。

它确实将注册表项写入 HKCR 和 HKLM,但没有写入 HKCU。

它安装了约 20 个第三方文件 COM 文件 (.ocx),目前已安装到 System32 中。它还在我们自己的文件夹中安装了许多第三方文件。

然后它会在我们自己的文件夹中安装约 15 个专有 COM dll 和一些非 COM 文件(包括服务)。

该服务是使用默认帐户“LocalSystem”的 Wix“ServiceInstall”安装的,但用户在第一次安装后会更改此设置。我们不知道帐户信息。不幸的是,在许多情况下,该服务需要访问网络共享来读取大图像,所以我看不出这如何与内置帐户一起使用。

据我所知,没有共享文件。

我同意 RemomveExisting AfterInstallFinalize 更可取,所以如果我们可以让它与 COM 注册一起工作,那就太好了。

包括帮助文件(chm 和 pdf 177MB),它以 250MB 结尾。

更新

如果我们使用“AfterInstallFinalize”,服务问题将得到解决。然而,这给我们留下了 COM dll 问题。

我们创建了一个测试安装程序,它只安装一个 COM dll 及其相应的注册表项 (TypeLib...)。

正如预期的那样,如果组件没有被修改,升级时它可以正常工作。 IE。 dll路径和自动生成的component-guid都没有改变。

但是,如果修改了 dll 路径,实际上我们安装了一个新组件,那么相关的 COM 注册表项在安装后会被删除,可能是通过 RemoveExistingProducts。我们尝试使用自动生成的 guid,并将其硬编码为与之前安装的 guid 相同的 guid。

问题似乎是 dll 路径发生了变化,但大多数注册表项都没有。例如。所有“类”键都丢失了。这就是我说“擦除注册表信息”时的意思。修复安装会恢复 COM 注册表项。

所以我想我的问题可以归结为: 我们如何正确安装/更新 COM dll,以便在更改文件路径时不会卸载 COM 注册表项?这可以使用 REP=AfterInstallFinalize 吗?

【问题讨论】:

  • 非常感谢大家的帮助。有很多信息需要我去消费,我试过了再回来。
  • 从以上判断,老实说,您的安装程序应该已经可以正常工作了。我肯定错过了什么。您确定所有 COM 服务器都启用了自动 GUID 吗?您确定 COM EXE 注册的自定义操作正常工作吗?这些是如何调节和排序的?它们是作为什么实现的?脚本?批处理文件?动态链接库?可执行文件?这是一个正常大小的设置——听起来很普通。任何 .NET COM 互操作?任何GAC安装?我确实相信内置帐户可以访问网络共享,但我从未尝试过。可以尝试 NetworkService - 然后您作为机器访问。
  • 您声明 COM dll 的 dll 路径可能会在升级时被修改。为什么会发生这种情况,频率如何?如果这些是自动 GUID 组件,它实际上应该仍然可以工作 - 至少在自我修复之后。这是怎么回事?您更新然后在该 COM 组件的实例化时调用自我修复?如果是这种情况,那么我希望我能看到源代码或至少两个编译的 MSI 文件(版本 1 和 2)来确定实际发生了什么。
  • 您解决了这个问题吗?
  • 对不起,我还没有机会尝试它,因为我在假期中离开了。我期待下周尝试您的建议。

标签: wix windows-installer


【解决方案1】:

简短总结

我在下面要说的基本上是:断开与旧的错误状态的链接,在这种状态下,新旧设置对多个 GUID 错误指向的文件感到困惑。每个组件使用一个文件来解决各种引用计数问题。在以后的版本中保持 GUID 稳定 - 使用 WiX 的自动 GUID 功能自动做到这一点。在主要升级期间强制延迟卸载现有产品。您的问题应该得到解决,但请阅读建议的步骤以了解一两个问题。

建议步骤

  • 每个组件强制一个文件。这解决了各种问题。 There are a few exceptions with multi-file assemblies and some fringe cases
  • 同时应用 WiX 自动 GUID 功能,您将 GUID 设置为“*”。这应该有效地处理组件引用计数,以使它们保持稳定,直到组件的密钥文件/注册表项的安装位置发生更改(通常不应该,没有必要)。
  • 安装到新位置(新文件夹名称)。这是为了“重新开始”并消除旧版本的任何干扰。这对于前往共享位置的组件并不总是可行的。如果您进入 system32 文件夹,只需将组件设置为永久。如果您去其他地方,请告诉我们在哪里。
  • 在 InstallFinalize 之后移动 RemoveExistingProduct
  • 对于您未来的版本,您应该有一个可行的解决方案。
  • 缺点:如果没有“重新安装”(重新应用服务凭据和新的安装位置),您的服务将无法生存,但从这一点开始,它应该能够承受任何升级。不过,您仍然应该测试修复过程中发生的情况 - 我不确定(尽管该问题已经存在)。
  • 请记住,使用用户凭据安装服务通常是一种部署反模式,如第 12 节所述:How do I avoid common design flaws in my WiX / MSI deployment solution?。您是否有机会将其删除并使其以 LocalSystemNetworkServiceLocalService 或其他标准帐户运行? (built-in account info - 值得一读吗?See this as well)。

下面是一些更冗长的沉思。值得一看,我希望。我会把它留在里面,但我希望这个分步说明更容易理解。


这里已经有了很好的建议。基本上,旧版本的卸载可以发生在新版本的卸载之前或之后。这显然已经很清楚了。

现在,如果您的组件 GUID 遵循最佳实践并在各个版本之间保持稳定,那么如果您将 RemoveExistingProducts 放在 InstallFinalize 之后,则不会出现上述任何问题,但您必须 100% 遵守组件规则(否则您可能会看到丢失的文件升级后之类的东西)。

了解组件规则的真正含义很重要。本质上,绝对安装位置(关键路径)和 GUID 之间存在 1:1 映射。如果键路径更改,则 GUID 必须更改。如果 GUID 更改,则密钥路径应更改。否则,所有这些都应该在不同版本中保持稳定。为了简单和可靠,我喜欢为所有文件每个组件使用一个文件——这使得升级更容易可靠地实现。也许这个解释更好:Change my component GUID in wix?

在 InstallFinalize 之后延迟放置 RemoveExistingProducts 使得主要升级基本上作为“补丁”工作 - 只安装新的,只删除过时的。这可确保您的服务组件不会被卸载和重新安装(这可能会清除您的凭据),而是会安装它包含的任何更高版本的文件。

  • 这里有类似的升级行为描述:MSI Major Upgrade overwriting rules
  • 我总结了 WiX 和 MSI 软件包中常见的问题。有点乱,但这里是:How do I avoid common design flaws in my WiX / MSI deployment solution?
  • 这里有一个讨论,展示了如果每个组件不使用一个文件通常会发生什么:https://www.symantec.com/connect/forums/upgrade-problem-and-removeexistingproducts。他的问题的解决方案是更改组件 GUID 和文件名 - 断开与旧的错误状态的链接。他将新的密钥路径设置为与先前版本不同的文件(以及删除先前的密钥文件)打破了绝对路径(密钥路径)和 GUID 之间的 1:1 链接。重命名和分配一个新的 GUID 会给文件“一个新的身份”——它不再与旧状态纠缠在一起。这里有类似的解释:Safely resolving duplicate component GUIDs in Wix
  • 更极端的版本是更改之前版本的安装文件夹名称。这将要求您重新生成安装到该(子)目录中的所有组件 GUID(因为所有绝对路径都已更改)。我当天对此进行了测试,它确实有效,但是自从我采用每个组件一个文件的策略以来,多年来一直没有处理它。真正解决各种问题。

我会问一些问题来澄清事情:

  • 这个安装程序的文件数量和兆字节有多大?只是为了感受一下它的复杂性和易于维护性。
  • 有多少 COM 文件,它们是您自己的专有文件,还是安装到共享位置的共享文件?
  • 您是每个组件使用一个文件,还是每个组件安装多个文件?
  • 你会写很多注册表数据吗?如果是这样,你怎么写?多个组件?你写信给 HKCU, HKLM 吗?我建议从应用程序本身编写所有 HKCU 设置,而不是从设置中编写。这将把它与任何部署纠缠解耦。这将更加可靠。
  • 如何设置服务凭据?您是通过自定义操作还是通过普通 MSI 表安装服务?正如 Phil 所说,这对于维修操作非常重要。
  • 总体上是否有许多自定义操作,如果有,它们的作用是什么?

如果不是很明显,结论是我会 100% 准确地遵循组件规则,并将 RemoveExistingProduct 放在 InstallFinalize 之后,以使升级表现为补丁.对于大型包裹,这也可以更快。 如果处理得当,就不应该出现你描述的问题

通过遵循组件规则,您还可以为您的产品提供小升级 - 如果需要。如果您在安装包含数千个文件的软件包后发现要“修复”六个文件,这对于商业产品可能至关重要。实际上需要非常小心才能完成这项工作,但这是可能的。

如果您到目前为止还没有遵循组件规则,我认为最简单的开始方法是在 ProgramFiles 中安装到与以前不同的文件夹(以断开与任何过去罪恶的链接),并设置组件 guid为每个组件自动生成和使用单个文件。 WiX 的自动生成 guid 旨在根据 ProgramFiles 下的安装位置保持稳定。换句话说,如果您稍后更改安装位置,所有 guid 都将与以前不同,但在此之前它们保持稳定。 自动。如果您非常彻底,这甚至允许打补丁和小幅升级。

【讨论】:

    【解决方案2】:

    您应该能够为 com 组件使用静态 GUID,然后安装程序将它们安装在何处都无关紧要,因为 GUID 与旧安装程序中的 GUID 相同。

    我相信这种方式 GUID 将有两个产品引用,当您的旧安装卸载时,它不会删除与 COM 组件关联的注册表设置,因为它们将有另一个引用。

    您必须进入旧的 MSI 并查看为每个 COM 组件自动生成的 GUID。可能最简单的方法是使用 dark.exe 或使用 ORCA 反编译旧的 MSI。

    认为这可行,但您必须对其进行测试。首先尝试仅使用一个 COM 组件作为概念证明。我不确定它是否会在旧的安装位置留下旧的 COM 组件。

    【讨论】:

      【解决方案3】:

      您需要更精确地“擦除注册表信息”。如果 COM Dll 安装程序组件 ID 不同,则 InstallExecute 之后的升级将导致旧 Dll 的引用计数(按安装程序 ID)倒计时至零(如果没有其他客户端产品),因此通常会导致组件(因此 Dll 和注册表项)在安装后被删除(因为 REP 是在安装完所有内容之后)。这也变得复杂,因为修复可能会注意到升级产品中现在缺少更新的 Dll 并尝试重新安装。 (如果注册路径发生了变化,那么 Heat.exe 可能会生成一个新的安装程序组件 ID - 不确定,抱歉。)

      如果 COM Dll 安装程序组件 ID 相同,它们将被共享,但如果您将 COM Dll 移动到另一个位置,则注册表路径需要不同,但它可能仍引用旧位置。如果是这种情况,您可能需要在写入新注册表信息之前编写 RemoveRegistryValue 以摆脱旧注册(RemoveRegistryValues 操作在 WriteRegistryValues 之前)。这是我会尝试的方法,但需要注意的是,我不清楚您在注册表中或修复后看到的确切内容。

      正如 Brian 所说,检查 COM Dll 的安装程序组件 ID,并使用详细的 MSI 日志执行 afterInstallexecute 升级。

      对于服务凭证,了解背景会有所帮助。如果这些凭据是在安装时提供的(WiX ServiceInstall)并且从未更改过,那么通常(如果不是太安全)将凭据保存在安全的地方并在升级时应用它们。作为一项实验,已安装产品的维修是否会丢失服务凭证?未初始化的 ServiceInstall 凭据的任何潜在应用都可能导致该问题。

      【讨论】:

      • 我不确定我是否理解。所以如果安装的 COM dll 的路径发生了变化,我还是要使用原来的安装程序组件 guid 来避免 refcount 变为 0?但是,经过测试,升级时它仍然会删除 COM 注册表项。
      • 问题是这里有两个资源,DLL和路径的注册表项。您无法共享这些,这就是您使用 afterInstallExecute 升级所做的事情。此外,没有足够的关于服务凭证的信息来知道是否有解决方案;例如,如果它们作为安装的一部分输入并进入 ServiceInstall 元素,那么它们可以被保存和恢复,正如我之前提到的。
      • 很抱歉,如果不清楚。服务凭证不知道,我们只使用默认凭证。然后用户使用适合其设置的帐户手动设置服务。是的,问题一定是com注册表项不能共享。如果 dll 路径发生变化,看起来唯一的选择是运行修复,因为 installFinalize 后的 REP 有很多好的属性,比如不会覆盖安装后修改的注册表值。
      • 如果您可以将 dll 与注册表项“解耦”,这样它们就可以独立升级,然后我可以在不修改注册表部分的情况下更改 dll 路径:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      • 2018-10-12
      • 2011-03-15
      • 2011-12-10
      • 2014-09-28
      • 1970-01-01
      相关资源
      最近更新 更多