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

How to avoid having two versions of a product installed with Windows Installer / MSI?

我编写并维护着一些 Wix 安装源文件,我用它们来构建 MSI 文件以分发我的应用程序。

我没有为任何类型的升级、更新、重新安装或任何类似的事情明确编程 -- 有一个功能由许多具有稳定 GUID 的组件组成,我观察到至少一个干净的安装符合我的预期。

但是,我(以及拥有我分发的 MSI 文件的任何人)可能看似使用各自的(不同的)MSI 文件并排安装我的应用程序的不同版本。这本身不是问题,除了我显然使用与安装目标相同的文件夹——“%ProgramFiles(x86)%\Foobar”——来安装应用程序(无论版本如何)。这意味着实际上有总是安装一个版本。

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

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

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

所以我想我的问题是,如何解决这个问题以便只显示一个版本(反映现实)或者在这种情况下惯用的方法是什么?我故意没有过度指定我的 Wix 源代码,希望 return 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  -->
                            <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>

我正在使用以下 Windows 命令提示行编译目标文件:

candle.exe -ext WixUtilExtension -out %TEMP% foobar.wxs

然后生成 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 元素:只需将 MajorUpgrade element 用于对现有 WiX 源进行主要升级的默认处理。这是一种 "magic element" 为您做很多(通常是好的)假设。有更老的和更灵活的方法来做到这一点 - 如果你需要更详细的控制(通常用于遗留目的 - 自动魔术并不涵盖所有基础):

<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />

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

Note: I will try to tune up this answer shortly with more links, but give that a go first?

首先linkUsing Visual Studio to make WiX files. The Hello WiX and Visual Studio-type of scenario.


重大升级推荐阅读:有关重大升级的一些知识。所有 WiX 标记基本上都围绕已编译的 MSI Upgrade table。在那里配置了主要的升级逻辑。自定义操作也可能会影响事物,以及一些其他事物,例如启动条件。​​

进一步:

  • 重大升级 - How-To & Concept: