10

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.

Drazen Bjelovuk
  • 5,201
  • 5
  • 37
  • 64

2 Answers2

8

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?

Community
  • 1
  • 1
bradfordrg
  • 1,863
  • 2
  • 21
  • 34
  • This gives me: `Error ICE57: Component 'ShortcutComponent' has both per-user data and a keypath that can be either per-user or per-machine.` And in reinforcing my original assumption, when I set its directory to `InstallFolder` instead, error disappears. So it really seems to suggest an issue of attempting to install a per-user component (inferred from `ProgramMenuFolder` and irrespective of `ALLUSER` property) with a per-machine KeyPath. – Drazen Bjelovuk May 16 '16 at 20:52
  • I agree - you will still get an ICE57 error. I've updated my answer to provide more details of why I think the ICE57 error is wrong and can be suppressed and ignored. – bradfordrg May 16 '16 at 22:50
  • Oh, dope, I completely overlooked your recommendation to suppress the warning. Well the logic of using HKMU to ensure consistency seems totally sound and I can definitely see the flaw in my original example of hardcoding HKLM, with the potential reassignment of `ALLUSER`. Weird about the false positive. Do you think WiX is to blame or Windows Installer? – Drazen Bjelovuk May 16 '16 at 23:38
  • I don't think WiX or Windows Installer is at fault. The problem is with one the Microsoft Installer Consistency Evaluators (ICE) in quite specific circumstances. Apart from this one case the ICE checks a do a very thorough job of checking installer databases before they are run. – bradfordrg May 17 '16 at 06:19
  • That's the answer I was looking for, awoseme explanation, thanks! – Cabuxa.Mapache Apr 24 '18 at 05:54
6

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>
Drazen Bjelovuk
  • 5,201
  • 5
  • 37
  • 64
  • 1
    I prefer the second solution. I had it like that already but got a warning as I missed the `KeyPath` attribute on `File`. Good fix! – letmaik May 23 '17 at 08:40
  • 1
    This second option was the ONLY way I could find to make this work. Thank you for the answer. Advertise="yes" is necessary to avoid errors. – Ed Bayiates Jun 18 '22 at 00:10