1

I am trying to create multiple shortcuts that are installed with my application. For an unknown reason, one is missing from the Start Menu and I can’t figure out why.

We create a new version in format "7.8.9.subversion_revision_number" for every build. On each build, we assign a new random UpgradeCode to the <Product> element (mostly because UpgradeCode="*" is invalid). Each version must be installed independently from each other. I want the shortcuts to be associated only this specific current version installed. They should be removed if the version is uninstalled. They should not be modified if a future version is installed. The life cycle of the shortcuts must follow the specific version that was installed.

Most online examples and official documentation suggest to use Guid="PUT-GUID-HERE" in the <Component> definition. This is a problem for me. We release our application once or twice per year. When a developer will be assigned to update the installer, he/she will likely forget to generate a new guid for the shortcuts component. I would prefer to use Guid="*" in the <Component> definition. The less human intervention when creating a new release, the better.

The Component documentation about the Guid attribute states :

* indicates that the linker should generate a stable guid. Generatable guids are supported only for components with a single file as the component's keypath or no files and a registry value as the keypath.

Since I do have multiple shortcuts file (*.lnk files) in my component, I am using a <registry> element as keypath. The path of the registry is "version based" to make it "unique per version". For example :

<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]\[ProductVersion]" Name="installed_extra_links" Type="integer" Value="1" KeyPath="yes"/>

If I understand this correctly, because the keypath is "unique in each version", a stable GUID should be generated and it should also be unique per "version" (per build).

Here is the problem.

  • If I install version 7.8.9.100, all shortcuts are available in the Start Menu.
  • If I install version 7.8.9.101, then my "Foobar Command Prompt" is not visible in the Start Menu. Other shortcuts are fine.
  • If I install version 7.8.9.102, then (again) my "Foobar Command Prompt" is missing from in the Start Menu. Other shortcuts are fine.

The *.lnk files are correctly created. For example, file "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Foobar v7.8.9.102 \Foobar Command Prompt.lnk" is created and working but missing from Start Menu.

What is so special about the "Foobar Command Prompt" shortcut ? Is that Windows that is hiding the shortcut for a weird reason ?

Here is my definition of my shortcuts:

    <!-- Define shortcuts that will be added to the Start Menu directory -->
    <DirectoryRef Id="START_MENU_DIR">
      <Component Id="FOOBAR_SHORTCUTS" Guid="*">

        <Shortcut Id="BinDirectoryShortcut"
                  Name="Binary files"
                  Target="[INSTALL_BIN_DIR]"/>

        <Shortcut Id="CommandPromptShortcut"
                  Name="Foobar Command Prompt"
                  WorkingDirectory="INSTALL_BIN_DIR"
                  Target="[SystemFolder]cmd.exe"/>

        <Shortcut Id="PrepareConfigShortcut"
                  Name="Prepare configuration"
                  WorkingDirectory="INSTALL_ROOT_DIR"
                  Target="[INSTALL_ROOT_DIR]prepare_configuration.bat"/>

        <Shortcut Id="OpenFoobarShortcut"
                  Name="Open Foobar GUI"
                  WorkingDirectory="INSTALL_BIN_DIR"
                  Target="[INSTALL_BIN_DIR]foobar_gui.exe"/>

        <!-- Delete shortcuts directory on uninstall -->
        <RemoveFolder Id="START_MENU_DIR" On="uninstall"/>

        <!-- Use a custom registry value as keypath -->
        <RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]\[ProductVersion]" 
                       Name="installed_extra_links" Type="integer" Value="1" KeyPath="yes"/>

      </Component>
    </DirectoryRef>

Note: I might be incorrect in using Guid="*" in the definition of the Component element but I have also observed that CMake/CPack 3.22.0 is also doing this (here and here) when building an MSI with multiple shortcuts.

I am installing on Windows Server 2016 Standard.

  • A verbose log file will explain all. PS: I wouldn't look to CPack for best practices using the WiX Toolset. :) – Rob Mensching Oct 16 '22 at 06:05
  • Could you be a little more specific. Do you mean to look at the compilation logs or the installation logs ? Any hint on what I should be looking for ? – end2endzone Oct 17 '22 at 14:08
  • @RobMensching I have enable build verbose output and set warnigs level to `Pedantic`. I also fixed warning ICE90. Directory ids are in PascalCase now. The id `START_MENU_DIR` is now `StartMenuDir`. I do not have build warnings now. I cannot find something wrong when looking at the build logs but the problem is still reproducible. I think the issue is somethig else: I tried to create new component guids for 3 consecutive builds and the problem persist. – end2endzone Oct 18 '22 at 21:04
  • He wanted you to look at a verbose installer log. /l*v install.log – Christopher Painter Oct 19 '22 at 13:11

1 Answers1

1

I have found a solution to both problems covered in this post.

The first one is why the Command Prompt shortcut of my 2nd and 3rd installations was not showing in the Start Menu? This problem is not related to Wix Toolset but to how Windows deals with shortcuts. Basically, Windows Start Menu will not show multiple shortcuts that have the same target. A detailed answer is explained in this superuser post: Windows 10 Start Menu can't have multiple shortcuts with the same target.
My shortcut to cmd.exe was using a unique working directory (Start in) but that was not sufficient. To work around that, I changed the target string to be unique per version with :

<Shortcut Id="CommandPromptShortcut"
          Name="Foobar Command Prompt"
          WorkingDirectory="InstallBinDir"
          Target="[SystemFolder]cmd.exe"
          Arguments="/K title $(var.ProductName) $(var.ProductVersion) &amp;&amp; set PATH=%CD%;%PATH%"/>

 

The second problem is about the uninstallation of the shortcut component. Installing version 100, 101, 102 and then uninstalling 101 was not removing version's 101 shortcuts. This is because Wix Toolset is generating the same pseudo-random GUID number for the shortcut component in every build. This is unexpected for me because I was using [ProductVersion] in the keypath.

As suggested, looking at the verbose installation logs, I see the generated GUID is identical for every build :

MSI (s) (E0:E4) [08:57:03:707]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-2779E3684883},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.100\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

MSI (s) (E0:B4) [08:56:18:888]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-2779E3684883},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.101\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

MSI (s) (E0:10) [08:56:41:250]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-2779E3684883},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.102\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

This results in the shortcuts to be "shared" by all installations but installed at different locations. This is bad practice and it is breaking component rules.

I assume that Wix Toolset does not expands the keypath string at compilation time to generate the auto-guid value. In other words, it uses the literal string Software\[Manufacturer]\[ProductName]\[ProductVersion] instead of using Software\My Company Inc.\Foobar\7.8.9.102. The same literal string is used for every builds which always generates in the same auto-guid value.

I solved the problem by using preprocessor variables to force the auto-guid generator to use the expanded string :

<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\$(var.ProductName)\$(var.ProductVersion)" Name="installed_extra_links" Type="integer" Value="1" KeyPath="yes"/>

Now if I look at the installation logs again, a unique auto-guid value is used per version :

MSI (s) (88:50) [10:06:54:023]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-9BC82F75EB70},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.181\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

MSI (s) (88:90) [10:07:14:367]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-494A8D9A862E},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.182\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

MSI (s) (88:98) [10:07:34:026]: Executing op: ComponentRegister(ComponentId={xxxxxxxx-xxxx-xxxx-xxxx-C717337B78C7},KeyPath=01:\Software\My Company Inc.\Foobar\7.8.9.183\installed_extra_links,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)

I don't know if this is good practice or not. I have not found examples online using preprocessor variables in a registry keypath to solve this particular problem. I find this to be a better and more elegant solution than commiting all my files with Guid="PUT-GUID-HERE" tokens and having to implement my own auto-guid generator in a prebuild script.