Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs

I've followed the official Major Upgrade guide and I seem to be missing something. Here is my MCVE:

<?xml version="1.0" encoding="UTF-8"?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

  <Product Id="*" Codepage="1252" Language="1033" Manufacturer="Bla Corporation"
           Name="Bla" UpgradeCode="PUT-GUID-HERE" Version="31.00.0000">

    <Package Comments="Contact: Refael Sheinker, [email protected]." Description="Bla"
             InstallerVersion="500"
             Compressed="yes"
             InstallScope="perMachine"
             Keywords="Installer,MSI,Database" Languages="1033" Manufacturer="Bla Corporation" Platform="x64" />

    <Media Id="1" Cabinet="my_application.cab" EmbedCab="yes" />

    <MajorUpgrade AllowDowngrades="no"
                  AllowSameVersionUpgrades="no"
                  Disallow="no"
                  IgnoreRemoveFailure="no"
                  MigrateFeatures="yes"
                  Schedule="afterInstallInitialize"
                  DowngradeErrorMessage="A later version of [ProductName] is already installed" />

    <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONROOTDIRECTORY" />
    <UIRef Id="WixUI_InstallDir" />

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFiles64Folder">
        <Directory Id="PROGRAMFILESSUBDIR" Name="Bla">
          <Directory Id="APPLICATIONROOTDIRECTORY" Name="BlaInternal" />
        </Directory>
      </Directory>
    </Directory>

    <DirectoryRef Id="APPLICATIONROOTDIRECTORY">

      <Component Id="tenlira.ini" Guid="*">
        <File Id="tenlira.ini" Source="..\ConfigurationFile\x64\tenlira.ini" KeyPath="yes" />
      </Component>

    </DirectoryRef>

    <Feature Id="MainApplication" Title="TenLira" Level="1">
      <ComponentRef Id="tenlira.ini" />
    </Feature>

  </Product>

</Wix>

All it does is simply installing a single file as an example. So far, so good. Now, all I do is add another Component and File and off course the corresponding ComponentRef in Feature. I specifically leave the Version as is: 31.00.0000. What I expected is that the new installer will not perform a Major Upgrade, but it does. Why? Also, there is now 2 entries in Add/Remove Programs.

Please help me find out what am I missing here. Thanks. Refael.

UPDATE: Posting the question got me to reread the documentation again and I discovered that the AllowSameVersionUpgrades thingy in the MajorUpgrade element should be set to yes. This time there is only one entree in the Add/Remove Programs, but it still performs Major Upgrade. Why?


Solution 1:

UPDATE: Here is a list to help debug failing major upgrades by identifying the most common problems: Common causes of failed major upgrades


Major Upgrade - "The Old, Manual Way"

I guess you are hitting an oddity that may not be handled entirely as expected by the WiX MajorUpgrade element by combining the auto-generated product GUID, the AllowSameVersionUpgrades set to yes and keeping the version number the same.

I can't see any obvious way to set the MinInclusive attribute in WiX's MajorUpgrade element - I could be mistaken, there might be a way I am unaware of. For what it is worth, I am not too keen on allowing "same version upgrades".

However, you could try to "use the old way" to author the Upgrade table using the "older elements" Upgrade and UpgradeVersion. The MajorUpgrade element is essentially a "convenience" feature to set up your major upgrades easily, and I believe it works for most users. Bob Arnson has a blog explaining the introduction of the MajorUpgrade element. This blog also shows a sample of how to do things "manually" with the "older elements" Upgrade and UpgradeVersion (do check it out).

I made a quick mock-up that might do what you want, it is just a "rough draft" - can't make any guarantees. I use preprocessor defines to set some variables that can be referenced in the WiX source file - as a C++ developer this is a piece of cake for you so I won't waste time explaining it - the source should make sense:

<?define MyProductVersion = "31.00.0000" ?>
<?define MyProductCode = "PUT-GUID-HERE" ?>
<?define MyUpgradeCode = "PUT-GUID-HERE" ?>

<!--Recommendation: set a path variable that you can redirect at will to a new release folder (new build output folder): -->

<!-- <?define MyBasePath = "C:\Projects\MyApp\Release\31.00.0000\" ?> -->

  <!-- SAMPLE: 
   <Component Win64="yes" Feature="MainApplication">
     <File Source="$(var.MyBasePath)\myapp.exe" />
   </Component> -->

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

  <Product Id="$(var.MyProductCode)" Codepage="1252" Language="1033" Manufacturer="Bla Corporation"
           Name="Bla" UpgradeCode="$(var.MyUpgradeCode)" Version="$(var.MyProductVersion)">

    <Package Comments="Contact: Refael Sheinker, [email protected]." Description="Bla"
             InstallerVersion="500"
             Compressed="yes"
             InstallScope="perMachine"
             Keywords="Installer,MSI,Database" Languages="1033" Manufacturer="Bla Corporation" Platform="x64" />

    <Media Id="1" Cabinet="my_application.cab" EmbedCab="yes" />

   <!-- Major upgrade -->
    <Upgrade Id="$(var.MyUpgradeCode)">
      <!-- Downgrade Protection -->
      <UpgradeVersion Minimum="$(var.MyProductVersion)" OnlyDetect="yes" IncludeMinimum="yes" Property="DOWNGRADE_DETECTED"  />
      <!-- Major Upgrade Configuration -->
      <UpgradeVersion IncludeMinimum="no" Maximum="$(var.MyProductVersion)" IncludeMaximum="no" MigrateFeatures="yes" Property="UPGRADE_DETECTED"   />
    </Upgrade>

    <!-- Major Upgrade: Schedule RemoveExistingProducts -->
    <InstallExecuteSequence>
      <!-- Potential scheduling (after): InstallValidate, InstallInitialize, InstallExecute, InstallExecuteAgain, InstallFinalize -->
      <RemoveExistingProducts After="InstallInitialize" /> 
    </InstallExecuteSequence>

    <!--Launch Condition: Abort setup if higher version found-->
    <Condition Message="!(loc.NewerVersionDetected)">
      NOT DOWNGRADE_DETECTED
    </Condition>

    <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONROOTDIRECTORY" />
    <UIRef Id="WixUI_InstallDir" />

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFiles64Folder">
        <Directory Id="PROGRAMFILESSUBDIR" Name="Bla">
          <Directory Id="APPLICATIONROOTDIRECTORY" Name="BlaInternal" />
        </Directory>
      </Directory>
    </Directory>

    <DirectoryRef Id="APPLICATIONROOTDIRECTORY">

      <Component Id="Test.ini" Guid="PUT-GUID-HERE" Win64="yes" Feature="MainApplication">
        <CreateFolder Directory="APPLICATIONROOTDIRECTORY" />
        <IniFile Id="SomeSetting" Action="addLine" Directory="APPLICATIONROOTDIRECTORY" Key="Setting1" Name="Test.ini" Section="MySection" Value="Some Setting" />
        <IniFile Id="OtherSetting" Action="addLine" Directory="APPLICATIONROOTDIRECTORY" Key="Setting2" Name="Test.ini" Section="MySection" Value="Other Setting" />
      </Component>
   </DirectoryRef>

    <Feature Id="MainApplication" Title="TenLira" Level="1">
      <!--<ComponentRef Id="tenlira.ini" />-->
    </Feature>

  </Product>

</Wix>

Now the !(loc.NewerVersionDetected) has to be explained. This is a localized string (for delivering your setup in different languages). To use it, right click your WiX project in Visual Studio and go: Add New Item... => Localization File => Add. As the localization file is added, your output MSI will also now go into a en-us folder under your main output location (Debug or Release).

In the localization file, add:

<?xml version="1.0" encoding="utf-8"?>
<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization">
    <String Id="NewerVersionDetected">A later version of [ProductName] is already installed.</String>
</WixLocalization>

And you should now be able to add new strings to this file and easily translate your whole setup using such language files.

Also add the WiX GUI extension. Right click "References". Add Reference... => Browse to WixUIExtension.dll => Double click this file, and press OK. Normal folder to find the file is: C:\Program Files (x86)\WiX Toolset v3.11\bin.


INI-Files

I just want to mention that INI files should ideally be installed via the IniFile table (entries are treated as atomic key-value pairs allowing advanced merging of keys and values for existing INI files), and not via the File table (the file is treated as a regular file either overwriting the whole existing file or leaving it in place - not enforcing any new values). The WiX element corresponding to the MSI IniFile table is naturally the IniFile element.

An ad-hoc sample:

<Component Id="Test.ini" Guid="PUT-GUID-HERE" Win64="yes" Feature="MainApplication">
    <CreateFolder Directory="APPLICATIONROOTDIRECTORY" />
    <IniFile Id="SomeSetting" Action="addLine" Directory="APPLICATIONROOTDIRECTORY" Key="Setting1" Name="Test.ini" Section="MySection" Value="Some Setting" />
    <IniFile Id="OtherSetting" Action="addLine" Directory="APPLICATIONROOTDIRECTORY" Key="Setting2" Name="Test.ini" Section="MySection" Value="Other Setting" />
</Component>

Links:

  • Adding entries to MSI UpgradeTable to remove related products

Solution 2:

It does a major upgrade because both MSIs have the same UpgradeCode and you have now specified AllowSameVersionUpgrades, so it does the upgrade where it didn't before.

Your build generates a new ProductCode every time, so each MSI is a new product, so you will get it installed twice if it doesn't do an upgrade and once if it does. You may have some assumption about the way upgrades work that you haven't spelled out.

Solution 3:

I had the same problem where Version is same, but the Id is different creating multiple entries in Add/Remove programs. My simple fix was to set AllowSameVersionUpgrades="yes".

<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of $(var.ServiceName) is already installed." />