8
votes

I'm trying to generate an installer for a per-machine application. Here's my component:

<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 spits out the following:

Error ICE43: Component ProductComponent has non-advertised shortcuts. It's KeyPath registry key should fall under HKCU.

I can't understand why it requires a per-user KeyPath for what I intend to be a per-machine component. Would this key not be left behind during uninstall performed by another user? Or its absence result in a duplicate during repair?

It seems so, as after changing it to HKCU, I still receive the following:

Warning ICE57: Component 'ProductComponent' has both per-user and per-machine data with an HKCU Registry KeyPath.

So I'm really at loss for how to avoid any of these errors/warnings short of installing everything to the user profile.

2

2 Answers

6
votes

Strictly speaking it is not WIX throwing up these errors but the Microsoft Windows Installer Internal Consistency Evaluators. WIX runs all of the ICEs during the build of an MSI and throws an error if any fail. On the whole this is a good thing as it eliminates a lot of potential errors in the MSI database during the build phase.

You can make the ICE43 error go away by using HKMU as the registry root. HKMU is a special constant used by WIX to put the value -1 in the Root column of the Registry Table of the Windows Installer Database. This causes Windows Installer to place the registry entry in HKLM on per machine installs and HKCU on per-user installs.

To date I've found no way of fixing the ICE57 error for non-advertised shortcuts in per-machine installations without moving the shortcut to its own component and using HKCU as the registry root. However an installer database authored this way can leave behind a registry key in User A's registry hive if User A installs the product and User B removes the product.

I've long felt that ICE57 produces false positive errors when checking non-advertised shortcuts in per-machine installations. This is difficult to prove with 100% certainty because we don't have access to the logic behind ICE57.

What I tend to do in this situation is use separate components for the EXE and shortcut. Use HKMU in the registry root in the registry value in the shortcut and suppress ICE57 in the WIX tool settings:

<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>

Looking at the above example, the directory of ShortcutComponent is MenuFolder which normally derives from ProgramMenuFolder.

Windows Installer will redirect ProgramMenuFolder to the All Users menu folder in a per-machine installation, or the Current User menu folder in a per-user installation. See Installation Context for details of how folders are redirected depending on whether the installation is per-machine or per-user.

Similarly the registry root HKMU should be redirected to HKLM in a per-machine installation and HKCU in a per-user installation.

In both the per-machine and per-user scenarios the installation locations of the shortcut and registry setting are consistent. Despite the apparent consistency you will still get an ICE57 error. This implies ICE57 is producing a false positive error in this scenario.

There is more discussion in Wix create non advertised shortcut for all users / per machine and Why do we get an ICE57 error for non advertised shortcuts in per machine installations?

4
votes

I've narrowed the issue down to the fact that the shortcut Directory,MenuFolder (my folder under ProgramMenuFolder), is perceived as a user profile directory (irrespective of the ALLUSERS/InstallScope properties), and so seems to object to having its KeyPath set in HKLM. I've inferred this from the fact that Error ICE43 along with all errors and warnings disappear when simply setting this attribute to InstallFolder (my folder under ProgramFilesFolder).

I've arrived at two viable options:

Install file (per-machine) and non-advertised shortcut (per-user) as separate components

<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>

The above requires ALLUSER property disabled (InstallScope="perUser") to prevent a different user from uninstalling (resulting in a registry remnant).

Install file and advertised shortcut as per-machine component

<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>