Why do I need to specify a new guid for a version upgrade in wix - installation

I've just updated our wix install scripts as per this previous question/answer How to implement WiX installer upgrade?.
The idea was to prevent an older version 'downgrading' a newer version. So I have parts of the wix file that look like:
<Product Id="A_GUID"
<Upgrade Id="18626be5-521c-4b58-ab8a-54baddf66679">
<UpgradeVersion
Property="NEWERVERSIONDETECTED"
Minimum="$(var.Version)"
IncludeMinimum="no"
OnlyDetect="yes"
ExcludeLanguages="yes"
/>
</Upgrade>
<CustomAction Id="NewerVersionFound" Error="Can't downgrade." />
<InstallExecuteSequence>
<Custom Action="NewerVersionFound"
After="FindRelatedProducts">NEWERVERSIONDETECTED</Custom>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
I have two versions of this, say 2.1 and 2.2. The current practice is to keep the Product Id Guid (shown as 'A_GUID' above) the same for minor versions (like 2.x) and only to change for major - so moving from 1.x to 2.x we change the Guid.
But, the above doesn't work if the Product Guid is kept the same for 2.1 and 2.2, despite the '$(var.Version)' changing. If I change the Guid, it does work (and prevents the downgrade 2.2 -> 2.1).
I was wondering why this is the case (assuming I'm doing it correctly) - why do we need two bits of information (guid and version) for this to work?
Edit1a: There is an UpgradeCode Guid in the wix, which stays the same for each version.
Edit1b: If it's relevant, this is done with an old version of wix (2.x).

Its not related to the product guid.
It is always related to the UpgradeCode you are specify as attribute at the product node.
<Product Id="*" Name="name" Version="$(var.Version)" UpgradeCode="12345678-55F7-4731-A318-772EF75D2830">
Within the upgrade node you are looking to the upgradecode (and not to product guid).
You can specify multiply Upgradecodes to find different versions of your software. but upgradecode should normally stay the same in a product. see best practices on MS homepage, please.
<Upgrade Id="12345678-55F7-4731-A318-772EF75D2830">
<UpgradeVersion ExcludeLanguages="no" Property="OLDVERSIONFOUND"
IgnoreRemoveFailure="yes" MigrateFeatures="no" IncludeMinimum="no"
Minimum="0.0.0.0" Maximum="$(var.Version)" IncludeMaximum="no"
/>
<UpgradeVersion OnlyDetect="yes" Property="NEWAPPFOUND" IncludeMinimum="yes" Minimum="$(var.Version)" Maximum="99.99.99.99" />
</Upgrade>
With an custom action (you already have) you react on that.
<CustomAction Id="OldAppFound" Error="Newer app of [ProductName] is installed" />
Ofcurse you need to schedule the tests for that in the sequences (you also did)
<InstallExecuteSequence>
<Custom Action="OldAppFound" After="FindRelatedProducts">NEWAPPFOUND</Custom>
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="OldAppFound" After="FindRelatedProducts">NEWAPPFOUND</Custom>
</InstallUISequence>
And if necessary remove the old one (you also have in your code)
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>

Related

Unable to remove previous versions of MSI. Multiple entry's show up in control panel

I am not sure if i am missing something or i just plain dumb, the issue i am having related to uninstalling a MSI. Basically i am building a app, and using WIX to create the MSI installer. What is happening is that i am able to install a previous version of the app but not upgrade to a newer version MSI. E.g, when i click/install the newer version MSI then the older MSI, both MSI will be installed and visible in control panel(MyApp 1.5.0, MyApp 1.6.0).
I am pretty sure i have the "MINORUPGRADE/MAJORUPGRADE" along with the RemoveExistingProducts Action configured properly but this issue still happens.
I tried modifying the MINORUPGRADE/MAJORUPGRADE values and the RemoveExistingProducts property but still get the same behavior.
My WIX config looks like this (without the Directory/paths...)
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Language="1033" Manufacturer="MyApp" Name="MyApp $(env.APP_BASE_VERSION)" UpgradeCode="$(env.UPGRADE_CODE)"
Version="$(env.APP_BASE_VERSION)">
<Package Compressed="yes" InstallerVersion="200" InstallScope="perMachine" InstallPrivileges="elevated" Description="MyApp $(env.APP_VERSION) Installer"
Comments="" Manufacturer="MyApp" />
<Property Id="MsiLogging" Value="v!" />
<MediaTemplate EmbedCab="yes" CompressionLevel="$(env.COMPRESSION_LEVEL)"/>
<Icon Id="icon.ico" SourceFile="$(env.STATIC_RESOURCE_PATH)\icon.ico" />
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<Upgrade Id="$(env.UPGRADE_CODE)">
<UpgradeVersion
Property="MAJORUPGRADE"
Minimum="0.0.0.0"
IncludeMinimum="yes"
Maximum="$(env.APP_BASE_VERSION)"
IncludeMaximum="no"
IgnoreRemoveFailure="no"
MigrateFeatures="yes" />
<UpgradeVersion
Property="MINORUPGRADE"
Maximum="$(env.APP_BASE_VERSION)"
Minimum="$(env.APP_BASE_VERSION)"
IncludeMinimum="yes"
IncludeMaximum="yes"
/>
</Upgrade>
...
<CustomAction Id="Remove_Roaming_MyApp" Directory="TARGETDIR" ExeCommand="cmd.exe /C "rmdir /s /q "[AppDataFolder]\MyApp""" Execute="deferred" Return="ignore" HideTarget="yes" Impersonate="no" />
<Property Id="WixShellExecTarget" Value="[#MyAppEXE]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<util:CloseApplication Id="CloseApp" Target="MyApp.exe" RebootPrompt="no" CloseMessage="yes"/>
<CustomAction Id="Kill_MyApp_instances"
Directory="TARGETDIR"
ExeCommand="taskkill.exe /IM MyApp.exe /F"
Execute="deferred"
Return="ignore" HideTarget="yes" Impersonate="no" />
<!-- This will create a log in the Temp folder of the user profile by default. NOTE: this is not the full log! The MSI will need to be ran with `/l*v <destination file>` -->
<CustomAction Id="CopyLog_partial"
ExeCommand="cmd /c copy "[MsiLogFileLocation]" "%SystemDrive%\Windows\Temp\MyApp-msi.log""
Directory="TARGETDIR"
Impersonate="no"
Execute="commit"
Return="ignore" />
<CustomAction Id="CopyLog_full"
ExeCommand="cmd /c copy "[MsiLogFileLocation]" "[LocalAppDataFolder]\Temp\MyApp-msi.log""
Directory="TARGETDIR"
Impersonate="no"
Execute="immediate"
Return="ignore" />
<InstallExecuteSequence>
<RemoveExistingProducts Before="InstallValidate" />
<Custom Action="WixCloseApplications" Before="InstallValidate" />
<Custom Action="Kill_MyApp_instances" After='InstallInitialize'></Custom>
<Custom Action="LaunchApplication" After='InstallFinalize'>NOT Installed</Custom>
<Custom Action="CopyLog_partial" After="PublishProduct" />
<Custom Action="CopyLog_full" OnExit="success" />
</InstallExecuteSequence>
<Feature Id="ProductFeature" Title="SetupProject1" Level="1">
<ComponentGroupRef Id="ELECTRON_FRAGMENTS"/>
<ComponentRef Id="Remove_MyApp_Roaming_comp" />
<ComponentRef Id="Remove_uninstallCache_folder" />
<ComponentRef Id="Remove_MyApp_programFiles_comp" />
<ComponentRef Id="Shortcut_startMenu" />
</Feature>
</Product>
</Wix>
The intended behavior is to remove the previous MSI or better yet all versions installed during the installation process of the MSI. I know this can be done with a CustomAction but i was wondering if theres a more elegant way to do this?
Summary: When you get two entries in Add / Remove Programs the major upgrade has failed. I suggest you comment out all major upgrade
constructs and try to get a heartbeat with the simplest constructs
available - explained below.
Major Upgrade: You have used old constructs to implement your major upgrade. There is a newer and simple way, like this:
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
This is sort of like a "template" implementing the major upgrade with the least amount of fuzz by just using "normal options" or simplified options if you like. There is no need to add your own RemoveExistingProducts entry when you use this template, in fact that will probably yield a "duplicate entry / symbol" on compile:
Add the MajorUpgrade element above.
Comment out the entire Upgrade element.
Comment out the RemoveExistingProducts element.
Older Constructs: The MajorUpgrade element will be enough to implement a working major upgrade, but you can still use the old WiX constructs (Upgrade and UpgradeVersion elements) to obtain more fine-grained control of the authoring of the Upgrade table. It is this table which defines how existing installations are to be handled. The upgrade table identifies related products by matching upgrade codes and then performs whatever action is defined for that scenario in the Upgrade table itself.
Previous Answers: There are several older answers on this topic, please see the following:
Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs
Adding entries to MSI UpgradeTable to remove related products (this shows how to use both the newer template construct AND the older WiX element for "fine-grained" control of Upgrade table).
A Few More Links:
Upgrading a WiX generated package with major version zero
How to prevent Wix from installing an older version?
Wix UpgradeVersion
Checking for Oldies
UPDATE:
Unable to remove previous versions of MSI. Multiple entry's show up in control panel
WIX does not uninstall older version (potential causes of failed major upgrade)

Problem with Wix uninstall using CustomAction

I've created a very simple MSI which copies some files to the ProgramFiles directory and while installing calling to custom actions found in a binary written in C#.
While installing, I can easily call any custom action I want. For example I've created an installation step where the user should enter a license, and after confirming the license it is checked against a server using logic written inside C# custom action.
But, when uninstalling, every time I add a custom action (even if it does nothing but returning Success), I get error that the installation failed.
This is how I use the uninstalling step:
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='MsiUnpublishAssemblies'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>
where TestUninstallation is defined as following:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="TestCustomAction" DllEntry="Uninstall" />
The property DllEntry equals Uninstall which is a C# method which only returns Success.
After installation is completed, I'm trying to uninstall and I'm getting the UserExit dialog defined inside the AdminUISequence with the property OnExit.
Any idea what am I missing?
Debugging: Managed code is relatively easy to debug (native code is actually even easier). Here are some pointers:
Debug C# Custom Actions (Advanced Installer)
Different debugging methods / aspects
Suggestions: I think you just have a broken reference to the dll export function - in other words an erroneous dll function name / reference:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no"
BinaryKey="CustomActions" DllEntry="__ERRONEOUS FUNCTION REFERENCE__" />
Just check what the dll actually exports and match like this:
<CustomAction Id="CustomAction1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
As always the real McCoy is the check of the dll itself to see if you have the right function name (the below screen shot from this prior answer, recommended read).
This is a native code C++ dll:
This is a DTF-packaged managed code dll:
Notice that this is a native dll with the managed code stuff embedded. It yields a very different functions list, but you still have to find the function name in there that you refer to.
This is a straight-up managed code dll (no native wrapping):
And finally: this is the straight-up managed code DLL without being wrapped in a native dll shell.
Un-Uninstallable Setup: When a custom action crashes or fails during uninstallation, you will have problems getting rid of the installation (it just rolls-back and you are stuck with it installed). There are several fixes or workarounds.
The overall fix - in my view - is to not fail custom actions on uninstall, or at least condition them so you can force an uninstall by setting a property via the command line:
Set in MSI property table: SUPPRESSERROR = 0. Then - when needed - on the command line set:
msiexec.exe /x {PRODUCT-GUID} SUPPRESSERROR="1"
Inside the MSI you condition the uninstall custom action with:
REMOVE="ALL" AND SUPPRESSERROR="0"
Now the custom action will not run if SUPPRESSERROR is anything but 0.
There is an older answer with several further options: I screwed up, how can I uninstall my program? (courtesy of Wim Coenen, with me messing up his answer with more suggestions).
Boilerplate: For quick use, let me just dump a boilerplate ad-hoc custom action test project here. This assumes a C# managed code custom action project called "CustomAction1" in the same Visual Studio solution and a reference added to it in your WiX source - like you already have obviously (this is for later when we have all forgotten what the problem was and need to test again):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WiXCustomActionsTesting" Language="1033" Version="1.0.0.0"
Manufacturer="test" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<UIRef Id="WixUI_Mondo" />
<Property Id="SUPPRESSERROR" Value="0" Secure="yes" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="WiXCustomActionsTesting" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!--BEGIN CUSTOM ACTION SECTION-->
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="CustomActions" DllEntry="CustomAction1" />
<InstallUISequence></InstallUISequence>
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='InstallInitialize'></Custom>
</InstallExecuteSequence>
<!--END CUSTOM ACTION SECTION-->
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiXCustomActionsTesting" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component>
<File Source="C:\Projects\MySetup\MyApp.exe">
</File>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Create WiX project
Copy paste the code, set a new Upgrade GUID
Create CustomAction project, default name
Add reference to custom action project from wix project
Add reference to WiXUIExtension.dll
Adjust path to file in component
Compile

Wix to remove previous version that was installed with Bootstrapper from control panel

I've got "ABC 10.1" program version installed on computer allready and I would like to remove it on each newer ABC.msi installation (for example when ABC 10.2 is going to be installed).
So far I've used a BOOTSTRAPPER to install new versions and it worked like a charm (the previous version was uninstalled and removed from the control panel and newest was appearing in the control panel).
Right now I would like to use ABC.msi for installation and to not use the BOOTSTRAPPER, but when I use ABC.msi installer the previous one is not being removed from the control panel and I have two "ABCs" 10.1 and 10.2 versions in the control panel.
How can I remove ABC previous version that was installed by BOOTSTRAPPER during newest ABC.msi installation (and avoid duplicating ABC in Control Panel)?
Bundle.wxs
<Bundle
Name="$(var.ProductName)"
Version="$(var.ShowVersion)"
Manufacturer="ABC"
UpgradeCode="33312c8d-64f1-4a04-a98e-4aa692de6aaf"
IconSourceFile="$(var.BaseDir)\Setup\UI\ABC.ico"
Condition="((VersionNT = v6.1 AND ServicePackLevel >= 1) OR (VersionNT > v6.1)) AND (WixBundleInstalled = 0)">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
<bal:WixStandardBootstrapperApplication
LicenseFile="$(var.BaseDir)\Setup\UI\License.rtf"
LogoFile="$(var.BaseDir)\Setup\UI\OticonAppIcon.png"
SuppressOptionsUI="yes"
ShowVersion="yes"/>
</BootstrapperApplicationRef>
<Chain>
<!-- Install .Net 4.6.1 -->
<PackageGroupRef Id='NetFx461Redist'/>
<!-- my packages to install -->
<PackageGroupRef Id="InstallerPackages" />
</Chain>
</Bundle>
<Fragment>
<PackageGroup Id ="InstallerPackages" >
<MsiPackage SourceFile="$(var.OutDir)\ABC.MSI" />
<MsiPackage SourceFile="$(var.OutDir)\DatabaseInstallerABC.msi" />
</PackageGroup>
</Fragment>
productABC.wxs (the file that is creating ABC.msi) is as following:
<?xml version="1.0" encoding="UTF-8"?>
<?include .\defines.wxi ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension" xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="$(var.ProductCode)"
Codepage="1252" Language="1033"
Manufacturer="ABC" Name="$(var.ProductName)"
UpgradeCode="{3338CE6B-D733-41AF-830B-DCAFE2E0CD58}"
Version="$(var.ProductVersion)">
<Package Id="*"
Compressed="yes"
InstallerVersion="300"
Languages="1033"
Manufacturer="ABC"
Platform="x86" />
<Media Id="1"
Cabinet="media1.cab"
EmbedCab="yes" />
----some directories and components----
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."
AllowSameVersionUpgrades="yes"/>
<InstallExecuteSequence>
<LaunchConditions After="AppSearch" />
<Custom Action="SetTargetDir" Before="CostFinalize"></Custom>
<Custom Action="SaveTargetDir" After="InstallValidate"></Custom>
</InstallExecuteSequence>
<WixVariable Id="WixUILicenseRtf" Value="$(var.BaseDir)\Setup\UI\License.rtf" />
<WixVariable Id="WixUIBannerBmp" Value="$(var.BaseDir)\Setup\UI\bannrbmp.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="$(var.BaseDir)\Setup\UI\ABCbackground.bmp" />
</Product>
</Wix>
Generic answer: The wix bootstrapper is what writes the entry in the Control Panel for your "ABC 10.1" release. In order to remove that, your 10.2 release will need to have a custom action tell the previous bundle to uninstall itself ("previous-cached-bundle.exe /silent /uninstall" or something similar). Now, whether you tell the old bundle to remove itself first, or you allow your MSI to upgrade first is up to your own requirements.

WiX localization - Desktop/Program menu short cut not replaced in Upgrade

We have recently included localization support for simplified Chiense (zh-cn) in Windows Installer using WIX 3.5. The current version of our product is 1.3.0 and in old version (1.2.0) we do not have any localization support.
We have also changed the product code for 1.3.0 in order to perform major upgrade. And used MSI transform (using Wisubstg.vbs and Wilangid.vbs ) to integrate the localization support into single MSI without using Bootstrapper.
When we test this in Simplified Chinese build in upgrade mode the desktop and prpogram menu short cuts were not replaced but duplicated with Chinese text.
WiX Short cut code is here.
<Shortcut Id="StartServiceShortcut" Icon="StartServiceIcon"
Name="!(loc.STR_StartServiceName)"
Description="!(loc.STR_StartServiceDesc)"
Target="[INSTALLLOCATION]bin\myapp.exe" Arguments="-s"
WorkingDirectory="INSTALLLOCATION"/>
product.Wxl
<String Id="STR_StartServiceName">Start Service</String>
<String Id="STR_StartServiceDesc">Start Services</String>
Installed 1.2 build with Windows 7 English version
Changed the Regional settings to Simplified Chinese and then run the 1.3 Installer.
Seen the Windows Installer Wizard showiing the contents in simplified Chinese.
Installation was successful. But Desktop/Program menus are duplicated with new Chinese text.
Both shorcuts are working with out any issues. But uninstall did not remove both shourt cuts.
Uninstall product does not remove the short cuts. I have also seen that two entries in the Programs/Features list. Also seen there are two different registry entries. The product name and Publisher values are localized all other properties are same in these to Registry keys.
Where am I missing?
Thanks in Advance for any help!
Update:
After changing the upgrade language to '0', I am still getting the same issue. It seems like this new version 1.3 has been installed as totally a new product. Any help!
After changing the upgrade language to '0' I am still getting the same issue. It seems like this new version 1.3 has been installed as totally a new product. Any Help!
<Package InstallerVersion="300" Compressed="yes" InstallPrivileges="elevated" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)"
IncludeMinimum="no"
OnlyDetect="yes"
Language="0"
Property="NEWPRODUCTFOUND" />
<UpgradeVersion Minimum="$(var.RTMProductVersion)"
IncludeMinimum="yes"
Maximum="$(var.ProductVersion)"
IncludeMaximum="no"
Language="0"
Property="UPGRADEFOUND" />
</Upgrade>
<Product Id="$(var.ProductID)" Name="!(loc.STR_ProductName)" Language="!loc.STR_Language)" Version="$(var.ProductVersion)" Manufacturer="!(loc.STR_Manufacturer)" UpgradeCode="$(var.UpgradeCode)" >
Finally I got it. Thanks to Alex Shevchuk's Blog From MSI to WiX, Part 8 - Major Upgrade
.
The FindRelatedProducts action runs through each record of the Upgrade table in sequence and compares the upgrade code, product version, and language in each row to products installed on the system.
So, After changing my code to something like below, upgrade was successful. Could be useful to others.
Any comments are welcome!
<Product Id="$(var.ProductID)"
Name="!(loc.STR_ProductName)"
Language="0"
Version="$(var.ProductVersion)"
Manufacturer="!(loc.STR_Manufacturer)"
UpgradeCode="$(var.UpgradeCode)">
<Package InstallerVersion="300" Compressed="yes" InstallPrivileges="elevated" Languages="0" Manufacturer="!(loc.STR_Manufacturer)" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)"
IncludeMinimum="no"
OnlyDetect="yes"
Language="0"
Property="NEWPRODUCTFOUND" />
<!-- For UPGRADEFOUND Property, the Language attribute should be set to 1033 as previous version supports only English -->
<UpgradeVersion Minimum="$(var.RTMProductVersion)"
IncludeMinimum="yes"
Maximum="$(var.ProductVersion)"
IncludeMaximum="no"
Language="1033"
Property="UPGRADEFOUND" />
</Upgrade>

Getting an install to upgrade using WiX - old installer created not using WiX

I'm building my first WiX installer after becoming sick of the sight of Wise For Windows Installer!
I've built the installer and that works fine, but now I need to get it to perform an upgrade from the previous version of my app. While I can find plenty of post about setting up WiX do perform upgrades, I can't find anything telling me how to do it when you have a previous installer made using another tool.
Do I do it the same way? Do I need to get upgrade codes, etc, from the old installer? Many thanks in advance!
UPDATE:
Following fletcher's instructions I got the UpgradeCode from the old installer using dark.exe and added it to the Product tag's UpgradeCode. The start of my WiX file now looks thus...
<Product Id="fcdc6617-e960-46db-8faa-1dc627f250c8" Name="MyProduct"
Language="1033" Version="1.2.0.5165" Manufacturer="MyCompany"
UpgradeCode="{E97A233B-AB49-4B66-B92A-68972F6D72B9}">
<Package InstallerVersion="200" Compressed="yes" />
<!-- Upgrade from previous version(s) -->
<Property Id="PREVIOUSVERSIONINSTALLED" Secure="yes" />
<Upgrade Id="{E97A233B-AB49-4B66-B92A-68972F6D72B9}">
<UpgradeVersion Minimum="1.1.0.4605" Maximum="1.2.0.5165"
Property="PREVIOUSVERSIONINSTALLED"
IncludeMinimum="yes" IncludeMaximum="no" />
</Upgrade>
...but now when I run this installer I end up with two instances of MyProduct on the target machine. Where am I going wrong?
In WiX v3.5 there is also the new MajorUpgrade element that makes all this authoring much easier.
It sounds like you want to do a Windows Installer Major Upgrade. Adding the Upgrade table will find the existing product and set your PREVIOUSVERSIONINSTALLED property to the product code.
To remove the old product during your install you need to add the RemoveExistingProducts action to your Execute sequence. There are some choices you have to make on where to sequence this in. The most basic way is to uninstall the old application early in the execute sequence by adding:
<RemoveExistingProducts Before="InstallInitialize" />
You can do the remove later in the execute sequence but you have to be more careful of component rules.
Finally found a solution this morning, thanks everyone who pointed me in the right direction (including DAVID GARDINER's blog). Making sure the Upgrade code is the same as the previous installer, and that both the Product code has changed and the version number has been incremented, here is the full solution:
<Product Id="fcdc6617-e960-46db-8faa-1dc627f250c8" Name="$(var.ProductName)"
Language="1033" Version="$(var.Version)" Manufacturer="$(var.Manufacturer)"
UpgradeCode="$(var.UpgradeCode)">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<!-- without this next line the upgrade doesn't work! (not sure why?) -->
<Property Id="ALLUSERS" Value="1" />
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Property='PREVIOUSVERSIONSINSTALLED'
OnlyDetect="no" IncludeMinimum='yes'
Minimum='1.1.0.4605' IncludeMaximum='no'
Maximum='$(var.Version)' />
<UpgradeVersion Minimum="$(var.Version)"
IncludeMinimum="no" OnlyDetect="yes"
Language="1033"
Property="NEWERPRODUCTFOUND" />
</Upgrade>
...
<InstallUISequence>
<Custom Action="UIandAdvertised" Sequence="3">
ProductState=1
</Custom>
<LaunchConditions After="AppSearch" />
</InstallUISequence>
<CustomAction Id="PreventDowngrading" Error="Newer version of this product is already installed." />
<CustomAction Id="UIandAdvertised" Error="Something about the UI."/>
<!-- Remove exist products before install -->
<InstallExecuteSequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">
NEWERPRODUCTFOUND AND NOT Installed
</Custom>
<LaunchConditions After="AppSearch" />
<RemoveExistingProducts Before="InstallInitialize" />
</InstallExecuteSequence>
</Product>
I had a similar problem and finally figured it out by running my installer with verbose logging.
The existing installation of my application was getting ignored because it was installed with InstallScope="per-machine" while the default is per-user.
"FindRelatedProducts: current install is per-user. Related install for product '{GUID}' is per-machine. Skipping..."
To solve, I added InstallScope to my package element:
<Package Id='*' ... InstallScope="perMachine"/>
I hope this helps!

Resources