Wix 如何避免使用Windows Installer/MSI安装两个版本的产品?

Wix 如何避免使用Windows Installer/MSI安装两个版本的产品?,wix,versioning,windows-installer,Wix,Versioning,Windows Installer,我已经编写并维护了一些Wix安装源文件,用于构建MSI文件以分发我的应用程序 我没有明确地为任何类型的升级、更新、重新安装或任何类似的东西编写过程序——有一个单一的功能,它由许多具有稳定guid的组件组成,我观察到,至少一个干净的安装可以实现我所期望的 但是,我(以及拥有我分发的MSI文件的任何人)可能会使用各自的(不同的)MSI文件并行安装我的应用程序的不同版本。这本身不是问题,只是我显然使用了与安装目标相同的文件夹--%ProgramFiles(x86)%\Foobar--在中安装应用程序(

我已经编写并维护了一些Wix安装源文件,用于构建MSI文件以分发我的应用程序

我没有明确地为任何类型的升级、更新、重新安装或任何类似的东西编写过程序——有一个单一的功能,它由许多具有稳定guid的组件组成,我观察到,至少一个干净的安装可以实现我所期望的

但是,我(以及拥有我分发的MSI文件的任何人)可能会使用各自的(不同的)MSI文件并行安装我的应用程序的不同版本。这本身不是问题,只是我显然使用了与安装目标相同的文件夹--%ProgramFiles(x86)%\Foobar--在中安装应用程序(无论版本如何)。这意味着实际上总是有一个版本最终被安装

我认为Windows Installer的行为是正确的,因为它更新了上次安装的MSI包中的文件。一个有趣的副作用是,如果最后一个MSI是早期版本,应用程序文件夹中的文件将被早期版本的副本覆盖

但这些对我来说似乎都不是真正的问题。我想修复实际安装的内容(单个应用程序版本)和Windows安装时跟踪的内容之间的差异——在我的例子中,是两个不同应用程序版本的两条记录

由于我将应用程序安装在一个不依赖于所安装版本的文件夹中,因此通过Windows跟踪多个应用程序版本是一个错误

所以我想我的问题是,我该如何解决这个问题,以便只显示一个版本(反映现实),或者在这种情况下,惯用的方法是什么?我故意没有过度指定我的Wix源代码,希望作为回报,Windows安装程序将使用一些内置的智能来自行解决所有问题。但我想我可能需要添加一些明确的升级或卸载以前版本的第一个指令

我的精简Wix源代码(文件“foobar.wxs”)如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
    <Product Name="Foobar" Manufacturer="ACME Inc." Id="*" UpgradeCode="ae9a7d6d-6c2d-446a-97d9-9dbe829d2ea8" Language="1033" Codepage="1252" Version="!(wix.PRODUCT_VERSION)">
        <Package Id="*" Languages="1033" SummaryCodepage="1252" Compressed="yes" InstallerVersion="200" />
        <Icon Id="foobar" SourceFile="!(wix.APPPATH)/foobar.ico" />
        <Property Id="ARPPRODUCTICON" Value="foobar" />
        <Property Id="ARPCOMMENTS" Value="Gives you full foobar powers" />
        <MediaTemplate EmbedCab="yes" CompressionLevel="high" />
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="DesktopFolder" />
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLDIR" Name="Foobar" FileSource="!(wix.APPPATH)">
                    <Component>
                        <File Id="foobar.exe" Name="foobar.exe" />
                    </Component>
                    <!-- There are other components like above (assets) -->
                </Directory>
            </Directory>
            <Directory Id="ProgramMenuFolder">
                <Directory Id="foobar_menu" Name="Foobar">
                    <Component Id="foobar_shortcut" Guid="e80a6b95-a145-453a-b327-65a977e741fe">
                        <Shortcut Icon="foobar" Id="foobar_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
                        <Shortcut Directory="DesktopFolder" Icon="foobar" Id="foobar_desktop_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
                        <RegistryValue KeyPath="yes" Root="HKMU" Key="Software\[Manufacturer]\[ProductName]" Type="string" Value="" /> 
                        <RemoveFolder Id="remove_foobar_menu" On="uninstall" />
                    </Component>
                </Directory>
            </Directory>
            <Directory Id="CommonAppDataFolder">
                <Directory Id="app_data_foobar" Name="foobar">
                    <Component Guid="" Id="app_data_config_folder">
                        <CreateFolder />
                    </Component>
                    <Component Guid="" Id="app_data_config_folder_log_file">
                        <File Name="foobar.log" Source="foobar.log.template">
                            <!-- Add write access permission to the log file to members of "Users" group. -->
                            <!-- PermissionEx Sddl="D:AR(A;;GWGR;;;BU)" / -->
                            <!-- Bug with Windows Installer, can't use PermissionEx/MsiLockPermissionsEx table. See https://stackoverflow.com/questions/55145282/how-to-include-inherited-permissions-when-specifying-permissions-for-a-file-inst -->
                            <util:PermissionEx Append="yes" GenericWrite="yes" User="Users" />
                        </File>
                    </Component>
                </Directory>
            </Directory>
        </Directory>
        <Feature Id="foobar">
            <ComponentGroupRef Id="foobar" />
            <ComponentRef Id="foobar_shortcut" />
            <ComponentRef Id="app_data_config_folder" />
            <ComponentRef Id="app_data_config_folder_log_file" />
        </Feature>
    </Product>
</Wix>
然后使用以下命令生成MSI文件:

light.exe -ext WixUtilExtension -spdb "-dAPPPATH=%apppath%" "-dPRODUCT_VERSION=%version%" -out %TEMP%\foobar-%version%.msi %TEMP%\foobar.wixobj
(使用Wix 3.11.1.2318)

升级代码:只要您设置了升级代码(用于标识一系列相关产品),就可以使用主要升级元素来指示将作为新MSI安装一部分卸载的产品

MajorUpgrade元素:只需在现有WiX源中插入一个默认的主要升级处理。这是一种“神奇元素”,它为你做了很多(通常是好的)假设。如果您需要更详细的控制(出于传统目的,通常情况下,auto magic不包括所有基础),可以使用更老、更灵活的方法来实现:


以上是在VisualStudio中创建的所有WiX文件的标准用法

注意:我会尝试在短时间内通过更多链接调整此答案,但是否先尝试一下

第一个链接


主要升级建议阅读:关于主要升级的一些知识。所有WiX标记基本上都围绕已编译的MSI标记。主要的升级逻辑就是在那里配置的。自定义操作也可能会影响一些事情,以及其他一些事情,例如启动条件

  • WiX文档
  • 主要升级-
    常见问题
  • 主要升级-
    手动配置
    :(使用旧式升级元素)
进一步

  • 主要升级-
    操作方法和概念

谢谢你,斯坦。我遵照您的建议,为
降级错误消息
属性添加了
MajorUpgrade
元素和我选择的文本。现在我可以看到,每当我安装一个MSI包一个新版本的应用程序时,只保留一个已安装应用程序的记录——新版本。这完全解决了我的问题。我试图允许降级(我没有任何不允许降级的要求),但Wix用一些半神秘的建议警告我允许降级,所以我决定无论如何都不允许。添加了一些链接,可能有助于更好地理解主要升级。如果您希望同时安装产品-作为单独的产品或与同时安装的版本1和版本2并排安装,我将使用“每个产品线”的单独升级代码。这意味着版本1和版本2将具有不同的升级代码,因此将分别进行升级和管理。如何做到这一点取决于场景和您需要什么。如果您不想让新升级代码使用旧版本,请不要使用新升级代码。这将是我的看法。我应该补充一点,您可以在升级表中插入多个升级代码,从而卸载其他产品线-即使是竞争产品,如果您够疯狂的话
:-)
。先打电话给法律部。显然,您只会卸载自己的产品。
light.exe -ext WixUtilExtension -spdb "-dAPPPATH=%apppath%" "-dPRODUCT_VERSION=%version%" -out %TEMP%\foobar-%version%.msi %TEMP%\foobar.wixobj