为什么WiX不允许我在HKLM中设置非广告快捷方式的KeyPath?

11

我正在尝试为一款全局应用程序生成安装程序。以下是我的组件:

<Component Id="ProductComponent" Directory="InstallFolder" Guid="{MY_GUID}">
    <File Id="ProductComponent" Source="$(var.MyApp.TargetPath)">
        <Shortcut        Id="StartMenuShortcut"
                       Name="MyApp"
                Description="App Description"
                  Directory="MenuFolder"
           WorkingDirectory="InstallFolder"
                       Icon="icon.ico" />
    </File>
    <RemoveFolder Id="RemoveMenuFolder" Directory="MenuFolder" On="uninstall" />
    <RegistryValue Root="HKLM" Key="Software\Microsoft\MyApp" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>

WiX输出以下内容:
错误ICE43:组件ProductComponent具有非广告快捷方式。它的KeyPath注册表键应该属于HKCU。
我不明白为什么它需要每个用户的KeyPath,而我打算将其作为每台计算机组件。这个键在另一个用户执行的卸载期间不会留下吗?或者它的缺失会导致修复期间出现重复?
似乎是这样,因为将其更改为HKCU后,我仍然收到以下内容:
警告ICE57:组件“ProductComponent”具有带有HKCU注册表KeyPath的每个用户和每台计算机数据。
所以我真的不知道如何避免任何这些错误/警告,除了将所有内容安装到用户配置文件中。
2个回答

8
严格来说,这些错误不是由WIX引起的,而是Microsoft Windows Installer Internal Consistency Evaluators。 WIX在构建MSI过程中运行所有ICE,并在任何一个失败时抛出错误。总的来说,这是一件好事,因为它消除了构建阶段MSI数据库中许多潜在的错误。
您可以通过使用HKMU作为注册表根来消除ICE43错误。 HKMU是WIX使用的特殊常量,将值-1放入Windows Installer数据库的Registry TableRoot列中。这会导致Windows Installer在每台计算机上进行安装时将注册表条目放置在HKLM上,在每个用户上进行安装时将其放置在HKCU上。
迄今为止,我还没有找到解决非广告快捷方式在每台计算机安装中出现ICE57错误的方法,除非将快捷方式移动到自己的组件中并使用HKCU作为注册表根。然而,以这种方式编写的安装程序数据库可能会在用户A的注册表中留下一个注册表键,如果用户A安装了该产品并且用户B删除了该产品。
我一直认为,在检查每台计算机安装中的非广告快捷方式时,ICE57会产生误报错误。这很难以百分之百的证明,因为我们无法访问ICE57背后的逻辑。
在这种情况下,我倾向于为EXE和快捷方式使用单独的组件。在注册表值中使用HKMU作为注册表根,并在WIX工具设置中禁用ICE57。
<Component Id="ProductComponent" Directory="InstallFolder" Guid="{MY_GUID}">
    <File Id="ProductComponent" Source="$(var.MyApp.TargetPath)" KeyPath="yes">
</Component>

<Component Id="ShortcutComponent" Directory="MenuFolder" Guid="{MY_GUID}">
    <Shortcut Id="StartMenuShortcut"
              Name="MyApp"
              Description="App Description"
              Target="[#ProductComponent]"
              WorkingDirectory="InstallFolder"
              Icon="icon.ico" />
    <RemoveFolder Id="RemoveMenuFolder" On="uninstall" />
    <RegistryValue Root="HKMU" <!-- Resolves to HKLM on per machine installs -->
              Key="Software\Microsoft\MyApp" 
              Name="installed" 
              Type="integer" 
              Value="1" 
              KeyPath="yes"/>
</Component>

从上面的示例中可以看出,ShortcutComponent 的目录是 MenuFolder,通常派生自 ProgramMenuFolder

在机器级安装中,Windows Installer 将重定向 ProgramMenuFolder 到“所有用户”菜单文件夹,而在用户级安装中将其重定向到“当前用户”菜单文件夹。有关文件夹如何根据安装是机器级还是用户级进行重定向的详细信息,请参见Installation Context

类似地,在机器级安装中,注册表根 HKMU 应重定向到 HKLM,在用户级安装中应重定向到 HKCU。

在机器级和用户级场景中,快捷方式和注册表设置的安装位置是一致的。尽管表面上一致,但在这种情况下仍会出现 ICE57 错误。这意味着 ICE57 在此场景中产生了错误的正面错误。

这里有更多关于Wix为所有用户/每台机器创建非广告快捷方式为什么在每台机器的安装中使用非广告快捷方式会出现ICE57错误?的讨论。


这给了我一个错误:ICE57 错误:组件“ShortcutComponent”既具有每个用户的数据,又具有可以是每个用户或每台计算机的关键路径。而且,当我将其目录设置为“InstallFolder”时,错误消失了,这加强了我最初的假设。因此,它确实表明了尝试安装每个用户组件(从“ProgramMenuFolder”推断出来,与“ALLUSER”属性无关)与每台计算机KeyPath的问题。 - Drazen Bjelovuk
我同意 - 您仍将收到ICE57错误。 我更新了我的答案,以提供更多详细信息,说明为什么我认为可以忽略和抑制ICE57错误。 - bradfordrg
哦,太棒了,我完全忽略了你抑制警告的建议。使用HKMU确保一致性的逻辑似乎非常合理,我肯定能看到在我的原始示例中硬编码HKLM的缺陷,可能会重新分配“ALLUSER”。关于误报的问题很奇怪。你认为是WiX的问题还是Windows Installer的问题? - Drazen Bjelovuk
我认为WiX或Windows Installer并没有问题。问题出在某些特定情况下的Microsoft Installer Consistency Evaluators (ICE)之一。除了这种情况外,ICE检查在运行安装程序数据库之前会非常彻底。 - bradfordrg
@akpp - 我也不是。严格来说,HKCU注册表键的要求是由ICE57报告的,而不是由WIX或MSI报告的。 - undefined
显示剩余2条评论

7
我已经缩小了问题范围,发现快捷方式DirectoryMenuFolder(在ProgramMenuFolder下的我的文件夹)被视为用户个人资料目录(与ALLUSERS/InstallScope属性无关),因此似乎反对在HKLM中设置其KeyPath。我从以下事实推断出这一点:Error ICE43以及所有错误和警告都会消失,只需将此属性设置为InstallFolder(在ProgramFilesFolder下的我的文件夹)即可。

我得出了两个可行的选择:

将安装文件(按机器)和非广告快捷方式(按用户)作为单独的组件安装

<Component Id="ProductComponent" Directory="InstallFolder" Guid="{MY_GUID}">
    <File Id="ProductComponent" Source="$(var.MyApp.TargetPath)" KeyPath="yes">
</Component>

<Component Id="ShortcutComponent" Directory="MenuFolder" Guid="{MY_GUID}">
    <Shortcut        Id="StartMenuShortcut"
                   Name="MyApp"
            Description="App Description"
                 Target="[#ProductComponent]"
       WorkingDirectory="InstallFolder"
                   Icon="icon.ico" />
    <RemoveFolder Id="RemoveMenuFolder" On="uninstall" />
    <RegistryValue Root="HKCU" Key="Software\Microsoft\MyApp" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>

上述要求需要禁用ALLUSER属性(InstallScope="perUser"),以防止其他用户卸载(导致注册表残留)。
将安装文件和广告快捷方式作为计算机组件安装。
<Component Id="ProductComponent" Directory="InstallFolder" Guid="{MY_GUID}">
    <File Id="ProductComponent" Source="$(var.MyApp.TargetPath)" KeyPath="yes">
        <Shortcut        Id="StartMenuShortcut"
                       Name="MyApp"
                Description="App Description"
                  Advertise="yes"
                  Directory="MenuFolder"
           WorkingDirectory="InstallFolder"
                       Icon="icon.ico" />
    </File>
    <RemoveFolder Id="RemoveMenuFolder" Directory="MenuFolder" On="uninstall" />
</Component>

1
我更喜欢第二个解决方案。我之前就是这样做的,但由于没有在“File”上添加“KeyPath”属性而收到了警告。好的修复! - letmaik
1
这第二个选项是我找到的唯一使它工作的方法。谢谢你的答案。Advertise="yes"是必要的,以避免错误。 - Ed Bayiates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接