0

I'm using WiX toolset to create my msi file from a C++ codebase with cmake and cpack. This setup works great for the past 6 months, but now i'm getting sporadic bad behaviour.

My Setup uninstall older products with the same upgrade-code to guarantee that there is only one version of my product installed (always major upgrade). I rolled out the newest version to my users (via SCCM) two days ago and in 10% of all installations I have the following problem.

The uninstall of the old installation seems to be sometimes wrong. The uninstall process remove the files, but left the root directory and the bin directory but all files are removed. When I query for the installation of the old product via WMI it say that it is installed, which is strange. The other 90% of the instllations everything works great. I also can not reproduce that locally.

I have a log file, but i'm not sure if it is from an misbehaving installation or not.

The logfile is very long, so I post only snippets which makes me sceptical.

MSI (s) (0C:9C) [15:21:35:083]: Component: CM_CP_runtime.bin.main2.exe; 
Installed: Absent;   Request: Null;   Action: Null
...
    MSI (s) (0C:78) [15:21:43:591]: Executing op: FileCopy(SourceName=-dbqfin2.dll|FreeImage.dll,SourceCabKey=CM_FP_runtime.bin.FreeImage.dll,DestName=FreeImage.dll,Attributes=512,FileSize=6201856,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=3.17.0.0,Language=1033,InstallMode=58982400,,,,,,,)
    MSI (s) (0C:78) [15:21:43:591]: File: C:\Program Files\LDS Studio\bin\FreeImage.dll;    To be installed;    Won't patch;    No existing file

The Action: Null means that there is no operation (but it should be local), but I'm not sure why. When I start the installation a second time everything works fine (because there is no old version). I had that behaviour a year ago (but only for two single files) when we are porting from Visual Studio to cmake and forgot to add a version to our dll (previous dll had a version, new one had no version, so msi think that's not a new version and skip it in REINSTALLMODE default mode 'omus'). Since then we have changed nothing and everything works fine until 2 days ago.

Here is my wix_main.wxs file

<Product Id="$(var.CPACK_WIX_PRODUCT_GUID)"
    Name="$(var.CPACK_PACKAGE_NAME)"
    Language="1033"
    Version="$(var.CPACK_PACKAGE_VERSION)"
    Manufacturer="$(var.CPACK_PACKAGE_VENDOR)"
    UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)">

    <Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine"/>

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

    <Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
    <Upgrade Id="$(var.CPACK_WIX_UPGRADE_GUID)">  
        <UpgradeVersion
            Minimum="1.0.0.0" Maximum="99.0.0.0"
            Property="PREVIOUSVERSIONSINSTALLED"
            IncludeMinimum="yes" IncludeMaximum="no"
        />
    </Upgrade> 

    <WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/>
    <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/>

    <?ifdef CPACK_WIX_PRODUCT_ICON?>
    <Property Id="ARPPRODUCTICON">ProductIcon.ico</Property>
    <Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/>
    <?endif?>

    <?ifdef CPACK_WIX_UI_BANNER?>
    <WixVariable Id="WixUIBannerBmp" Value="$(var.CPACK_WIX_UI_BANNER)"/>
    <?endif?>

    <?ifdef CPACK_WIX_UI_DIALOG?>
    <WixVariable Id="WixUIDialogBmp" Value="$(var.CPACK_WIX_UI_DIALOG)"/>
    <?endif?>

    <FeatureRef Id="ProductFeature"/>

    <UIRef Id="$(var.CPACK_WIX_UI_REF)" />

    <?include "properties.wxi"?>
    <?include "product_fragment.wxi"?>

    <CustomAction Id="RegisterExtensions"
                  FileKey="CM_FP_runtime.bin.main.exe"
                  ExeCommand="-regext"
                  Execute="deferred"
                  Return="check"
                  HideTarget="no"
                  Impersonate="no"
                  />
    <CustomAction Id="UnregisterExtensions"
                  FileKey="CM_FP_runtime.bin.main.exe"
                  ExeCommand="-unregext"
                  Execute="deferred"
                  Return="ignore"
                  HideTarget="no"
                  Impersonate="no"
                  />

    <InstallExecuteSequence>
        <RemoveExistingProducts Before='InstallInitialize'>
            NOT REMOVE
        </RemoveExistingProducts>
        <Custom Action="RegisterExtensions" After="InstallFiles">
            NOT REMOVE
        </Custom>
        <Custom Action="UnregisterExtensions" After="InstallInitialize">
            Installed AND (REMOVE = "ALL")
        </Custom> 
    </InstallExecuteSequence>
</Product>

I've found some blogpost where people deal with the OnlyDetect mode in but I'm not sure if it can help me or not because I'm not able to reproduce it on my machines.

Switching to REINSTALLMODE amus (https://msdn.microsoft.com/en-us/library/windows/desktop/aa371182(v=vs.85).aspx) maybe is my last resort, but that would be my last resort.

I've found this blogpost https://jpassing.com/2007/06/16/where-to-place-removeexistingproducts-in-a-major-msi-upgrade/ => could it be that i have to change from

<RemoveExistingProducts Before='InstallInitialize'>
                NOT REMOVE
            </RemoveExistingProducts>

to

<RemoveExistingProducts After='InstallInitialize'>
                NOT REMOVE
            </RemoveExistingProducts>

???

Maybe somebody can help me with the problem

Greetings
Tonka

Michael Aigner
  • 4,820
  • 4
  • 21
  • 33
  • After an upgrade but the old product is still there it means the upgrade has failed, rolled back, and attempted to re-install. This is not about REINSTALLMODE, RemoveExistingProducts (which needs no conditions) or Action=Null. You didn't say if the new product is installed, as well as the old one (which can happen if REP is outside the transaction). You don't have rollback custom actions, further complicating things. You really need a full log of the failure, and the exact order of REP, InstallInitialize, UnRegisterExtensions is not clear with respect to each other and InstallValidate. – PhilDW Nov 15 '17 at 19:01
  • I had some weird behaviour when an uninstall failed or something weird happened causing it to end. This caused an upgrade to happen and it migrated the feature states of the old install into the upgrade and for some reason core and required features were marked as not installed so the product installed with the features and their components not installed. Adding `MigrateFeatures='no'` to your `` tag might fix the issue. It's what I've done in the case where I don't really use the features except as a way to package and organize the components of the product. – Brian Sutherland Nov 15 '17 at 20:22
  • (Which is probably hiding another subtle design issue which causes the problem in the first place) – Brian Sutherland Nov 15 '17 at 20:26
  • @PhilDW Do you mean that the uninstall works, the install of the new product fail and then it try to reinstall the old msi? As far as i understand RemoveExistingProducts before InstallInitialize does not try to reinstall the old one when upgrade fails, only when it's after InstallInitialize. The new product is not install, WMI query says that the old version is installed, but there are only 2 empty folder in my target directory. What do you mean that i have no custom rollback action, are my two custom action not enough? – Michael Aigner Nov 18 '17 at 07:56
  • What also is strange that the msi install seems to return 0 to SCCM, because SCCM says the msi install was a success – Michael Aigner Nov 18 '17 at 08:09

1 Answers1

0

The most likely explanation for what you see is that RemoveExistingProducts (an uninstall of the older product) is failing. That will result in a rollback that restores that older product. The old product is then installed, and then the new product continues with its install. At some point in time the install of your new upgrade fails and rolls back, and this will potentially damage the older installed product. That's the general issue with doing an upgrade RemoveExistingProducts outside of the upgrade's transaction. If your REP was after InstallInitize it would fail and the upgrade would not proceed.

(As an aside, asimilar issue can happen if you sequence REP after InstallFinalize and outside the upgrade's transaction, because you install the upgrade product, the transaction finishes, the uninstall of the older product is attempted and fails, it rolls back and restores the older product so now you have both old and new products installed. )

You need to create a verbose MSI log of the upgrade to see why the REP is failing and leaving the older product installed, and why the new upgrade is failing (because otherwise it would show in Program&Features). Also it is safer to keep RemoveExistingProducts inside the upgrade's transaction.

PhilDW
  • 20,260
  • 1
  • 18
  • 28