Wix: moving <Upgrade> into <Fragment> - wix

I'm working on a suite of related applications that all have their own WiX installer. As you can imagine, there's a lot of duplication between these installers, and I'm trying to do something about that. To this end, I've created a WiX Library Project with some .wxs files, and I've been trying to shove as much code as possible into these .wxs files. Which has worked mostly fine, so far. However, there's one thing that I can't seem to move to a .wxs: the Upgrade element.
This is my upgrade code:
<Upgrade Id="MY-UPGRADE-CODE-GUID">
<!-- If an older version is found replace it -->
<UpgradeVersion OnlyDetect="no" Property="OLDERFOUND"
Maximum="$(var.ProductVersion)" IncludeMaximum="no"/>
<!-- If the same version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="SELFFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="yes"
Maximum="$(var.ProductVersion)" IncludeMaximum="yes"/>
<!-- If a newer version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="no"/>
</Upgrade>
<CustomAction Id="AlreadyUpdated" Error="This version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall it manually before this installer can continue."/>
<CustomAction Id="NoDowngrade" Error="A newer version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall that version manually before this installer can continue."/>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
<!-- If the same version is found execute AlreadyUpdated -->
<Custom Action="AlreadyUpdated" After="FindRelatedProducts">SELFFOUND</Custom>
<!-- If a newer version is found execute NoDowngrade -->
<Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
</InstallExecuteSequence>
For Upgrade/#Id, I use the same value as for Product/#UpgradeCode. (I'm not sure whether that's necessary, or even good practice.) I'd like to put that in a WixVariable at some point but for now I'm trying to make it work with literal GUIDs.
I have already moved the CustomActions and the InstallExecuteSequence to a Fragment in my .wxs file. I have defined a WixVariable to contain the ProductName, so I can have each app's installer display its own name. This works exactly the way I want and expect.
However, when I move the Upgrade element into a Fragment (it doesn't matter if it's in the app's Product.wxs file or in one of the library .wxs files, and it also doesn't matter if the Fragment is the same one that contains the InstallExecuteSequence), the following problems occur:
If I first run the old version of the installer, and then the new one with Upgrade moved into a separate Fragment, but with identical Product/#Version, Product#/UpgradeCode, and Upgrade/#Id, I end up with 2 installs of the same product, which is obviously not what I want.
If I install the new installer twice, it won't install the second time (which is what I want), but it won't show my error message either.
I would replace the whole thing with a MajorUpgrade, were it not for the facts that 1) it doesn't seem to want to give an error message when the same version is installed again, and, more importantly, 2) it's not allowed inside a Fragment for some reason, so I'm still stuck with duplication (albeit a lot less than previously).
So ... how can I move my Upgrade element into a .wxs library without losing the current functionality? What am I doing wrong?

As described in the wix Linker documentation, the linker will follow references between fragments while building a windows installer database. But this means that it will ignore any fragments that aren't referenced (directly or indirectly via other fragments) by the product wxs.
This behavior can be quite useful in your scenario, because it allows you to model the dependencies between all your reusable components, and the linker will make sure to include only the ones that are needed for a specific product.
To make sure that your upgrade logic is included, you have two alternatives:
Add a dummy Property to your fragment, and reference it with a PropertyRef in your product wxs.
Or put your upgrade logic in an Include element, and then instruct the wix preprocessor to include it in your product wxs. See the Wix Preprocessor documentation for an example.

Related

WIX Installer - is there any automated way of managing Product ID / Code?

I'm developing a Windows Service installer using Wix. From what I understand, I should generate a unique Product ID for every Version of the service. This can be easily done by setting "*" in the Product Id attribute. However, when I do this and I rebuild the installer, it generated a different guid for every build. When I already have any version of service installed on my machine, this causes several problems: i.e. 'exe' file remaining after uninstall process being finished, multiple entires in Programs in Control Panel.
After some investigation, I found that the solution to this would be to update the Product Id manually every time when version number of the service would change. Or maybe I am missing something?
Anyway, the question is: is there any way to make this process automated, so that the guid would be same every time I build the same version of the service but change only when the version is updated? Any external tool for Wix?
Build Automation: You can use build automation in a number of ways to automate the creation of new product GUIDs and similar. You would pass in values to the WiX project file and such things, but perhaps that is not what you want to maintain. Let me add another option below.
Major Upgrade Element: I assume you have a major upgrade element in there? It takes care of uninstalling the previous version if configured correctly. Typically you see multiple versions in Add / Remove Programs when you re-build the same version and install on top of the last installation.
Suggestion: You can manually configure the Upgrade table to uninstall "same version installations". You take out the "auto-magic" of the "MajorUpgrade" element and do things yourself with Upgrade elements (an older way to configure the Upgrade table that yields more detailed control):
Remove this (or just comment it out):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Add this:
<!-- The top of your new source - compiler variables, can be overridden at compile time via candle.exe and light.exe switches -->
<?xml version="1.0" encoding="UTF-8"?>
<?define MyProductVersion = "2.0.0.0" ?>
<!-- Sample GUID only, do not use in your package ->
<?define MyUpgradeCode = "88888888-7777-7777-7777-888888888888" ?>
<..>
<Upgrade Id="$(var.MyUpgradeCode)">
<UpgradeVersion Minimum="$(var.MyProductVersion)" OnlyDetect="yes" IncludeMinimum="yes" Property="DOWNGRADE_DETECTED" />
<UpgradeVersion Property="PRODUCTLINE1" IncludeMinimum="yes" IncludeMaximum="yes" Maximum="$(var.MyProductVersion)" Minimum="0.0.0" />
</Upgrade>
<..>
<!-- You have to add RemoveExistingProducts to the InstallExecuteSequence table -->
<InstallExecuteSequence>
<!-- Potential scheduling (after): InstallValidate, InstallInitialize, InstallExecute, InstallExecuteAgain, InstallFinalize -->
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
Summary: The above should let you rebuild your setup and install any number of
times using the same version number and the installation process will
remove prior installation "auto-magically". Please test. No guarantees. Murphy's law. Etc... :-).
Some Links:
Adding entries to MSI UpgradeTable to remove related products
Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs
WIX does not uninstall older version (list of common major upgrade problems)

Wix Installer - create installer that always re-installs over tself

I am trying to create an installer that doesn't "carry" any files but does set a registry key and create a small folder structure (if not present)
The script does use CopyFile to copy files from a network location to a folder on the c-drive.
The idea is to create an installer that can be re-run whenever the network files are updated so that they are brought local for the user.
The CopyFile code has a RemoveFile line just ahead of it (in same component) so the file should always be copied "fresh"
The folder structure and registry key don't really need to be repeated but won't matter if they are.
I am also not worried about an uninstall (nor what it does as this install is always needed) but I can't create install after install in the system so I do need to be upgrading somehow.
I have (currently got) these bits of script in my wxs file
http://schemas.microsoft.com/wix/2006/wi'>
<Product
Id='*'
Name='Eclipse Template Installer'
UpgradeCode='$(var.ProductUpgradeCode)'
Version='$(var.ProductVersion)'
Manufacturer='Article 10'
Language='1033'>
<Package InstallerVersion='200' Compressed='yes' Comments='Windows Installer Package' />
<Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
<Upgrade Id="$(var.ProductUpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" IncludeMaximum="yes"
Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
</InstallExecuteSequence>
When I run the Installer for the first time, it works. Files are copied and all good.
I then tweak one of my copied files to force a new date/time on it and re-run the installer. It pops up indicating it is gathering info but doesn't then replace the file with the older version so I think the installer has concluded it needn't do anything as the current "latest version" is already installed?
If I choose Repair in Programs and Features the file does get updated.
I tried setting the IncludeMaximum to a yes but that doesn't seem to help. Same result.
I have also tried the MajorUpgrade command instead of the InstallExecuteSequence but I get the same end result. Maybe for different reasons but not really sure. Maybe not using it right.
Can anyone tell me how to force my installer to redo everything it originally did even if the version matches?
My client can't make new MSI files so updating the version and rebuilding isn't an option. They just want to update the network files (templates) and rerun the MSI to get the files local.
The first run of the MSI also configures their machine for the templates so it is all very simple, which is what they wanted.
Thanks
Simon
Might be a bad idea (I'm not sure what else this product/tool is supposed to do), but you could condition the MSI standard action "PublishProduct" to '0' so that it never runs. This way, your MSI will install and never register itself on the machine. You can continue to run your installer over and over, and it will never detect that an older version is installed.
This is, of course, not the 'correct' way to go about things, but it works.

MSI Major Upgrade overwriting rules

I think I read it somewhere, but couldn't find the source now and can't confirm it: when install(Major upgrade) a newer version from MSI, if the file has been modified (either by installer or user), the default rule is that old file wouldn't be replaced by the same file from a new version?
I think also I observed that behavior in the installer I wrote before, but now after a few changes, seems that it will always replace the old modified config files!
Product definition:
<Product Id="*" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="Advanced Software Solution" UpgradeCode="$(var.UpgradeCode)">
<Package Id="*" InstallerVersion="200" Description="The web service installer" Compressed="yes"
InstallScope="perMachine"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Component Definition:
<Component Id='WebConfigComp' Guid='GUID'>
<File Id='WebConfigFile' Name='Web.config' Source='$(var.TheWebService.WCF.TargetBinPath)\Web.Distribution.config'
KeyPath='yes'>
</File>
</Component>
InstallExecutesequence
FindRelatedProducts 25
AppSearch 50
LaunchConditions 100
ValidateProductID 700
myScripts_CA 799
CostInitialize 800
FileCost 900
CostFinalize 1000
MigrateFeatureStates 1200
InstallValidate 1400
RemoveExistingProducts 1401
InstallInitialize 1500
BackupCA Installed 1501
ProcessComponents 1600
UnpublishFeatures 1800
SchedSecureObjectsRollback_x64 VersionNT > 400 1801
RemoveFiles 3500
RemoveFolders 3600
CreateFolders 3700
InstallFiles 4000
InstallServices VersionNT 5800
SchedSecureObjects_x64 NOT REMOVE~="ALL" AND VersionNT > 400 5801
ConfigureIIs NOT SKIPCONFIGUREIIS AND VersionNT > 400 5999
RegisterUser 6000
RegisterProduct 6100
PublishFeatures 6300
PublishProduct 6400
InstallFinalize 6600
LunchWCFReadme NOT Installed 6601
Update: I just created a new project for testing, the same behavior observed (the modified file is replaced by the newer version of installer) without change the default InstallExecSequence. Which probably means even though the File Versioning should applies, but it not actually kicked in to affect the result would expected as Remove of old version happened too early be default as Glytzhkof and PhilDW pointed out.
I am using Wix 3.8, the current stable, did I missed something?
Update2:
So far, I can confirm that moving RemoveExistingProducts after InstallFiles will keep the modified unversioned files. But the problem is that seems MajorUpgrade conflict with
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>
I am adding, and the error message is
Error 1 Duplicate symbol
'WixAction:InstallExecuteSequence/RemoveExistingProducts' found. This
typically means that an Id is duplicated. Check to make sure all your
identifiers of a given type (File, Component, Feature) are
unique. C:\TestDev\MySetupTest\MySetupTest\Product.wxs 5 1 MySetupTest
which is not very helpful either.
Final Update:
After digging the web thing for a while, find out what the issue is:
By default, MajorUpgrade schedules RemoveExistingProducts after
InstallValidate. You can change the scheduling using the Schedule
attribute. For example, If you choose to schedule it after
InstallInitialize, it will look like the following:
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit.">
Source: Wix Toolset website
So including of MajorUpgrade will indeed change RemoveExistingProducts sequence for you, which is a useful feature to have, but was unexpected for me. Thanks for all the help, now things starting make sense to me. An happy ending after all!
When a major upgrade uninstalls an existing installation before the new version gets installed (RemoveExistingProducts before InstallInitialize) it will normally remove all files that were originally installed - this includes files that may have been modified. Then the new version is installed with a fresh bundle of files.
If you schedule RemoveExistingProducts after InstallFinalize the install of the new version's files precedes the removal of obsolete files. In this scenario files are only replaced if they are versioned and newer than installed files, and for unversioned files like txt, pdf, etc... the file replacement rules basically states that the file will only be overwritten if it has not been changed on disk.
It follows that moving RemoveExistingProducts after InstallFinalize may solve your file "replacement problem" which is really a case of the modified files being deleted during uninstalland reinstalled by your current upgrade strategy.
As pointed out, your REP is early in the sequence, so it's essentially an uninstall of all the older product followed by an install of the new one. This is one type of major upgrade.
The other type of major upgrade is when REP is at the "end". In this case the new product is installed on top of the older product, and that will follow file replacement rules, that's the part that's relevant to your question.
This might be useful:
http://msdn.microsoft.com/en-us/library/aa370531(v=vs.85).aspx
and there's different rule if the unversioned files have hash checking. Yes, unversioned means "without a file version in the file's resources".
Behaviour what you are saying is unversioned file replacement logic.
Placing config file in its own component and marking the config file as the key path of the component will ensure that Windows Installer will use unversioned file replacement logic when deciding whether or not to replace this file in the install of your new version.
This post can help you to achieve the same.
I can't comment because of lack of reputation, but if you want to add
<InstallExecuteSequence> <RemoveExistingProducts After="InstallExecute" /> </InstallExecuteSequence>
and if you got duplication error, you should try this instead;
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecute" />
My guess is that the change in behavior is because you have moved RemoveExistingProducts early in the InstallExecuteSequence.
A major upgrade is essentially running an uninstall of the original product, and then a reinstall of the new one. If RemoveExistingProducts is late in the InstallExecuteSequence - and component referencing is done correctly - the existing product is not uninstall first, but its "leftover" components are removed after installing the new product. It works like a "diff" if you like. This will preserve installed files on disk that are not removed in the new version. I hope that was comprehensible - a lot at once, and rushed.
Here is a thread that deals with ensuring unversioned files get updated during an update: Forcing an upgrade of a file that is modified during its initial installation

How to modify xml on wix repair

I have an installer that is created with WIX and modifies a config via XmlFile, however I believe that the Wix Util Extension does not perform these actions on repair. This is causing problems when trying to perform a self-healing installer. Is there any way to accomplish what I am looking for
By piecing together a bunch of sources I came up with the following:
<Property Id="REINSTALLMODE" Value="amus"/>
<SetProperty Id="REINSTALL" Value="ALL" After="AppSearch">
<![CDATA[Installed AND REMOVE<>"ALL"]]>
</SetProperty>
Which forces a REINSTALL = ALL if it is not a remove or install
I have a similar scenario. Properties can be edited by the user through the UI, which are stored/loaded via the Registry and written to configuration files. Beyond Justin's answer, Secure="yes" must be set on each property, or MSI will ignore it (the log will show "Ignoring disallowed property").

Conditional uninstall of previous version in Wix

I'm doing an installer for an software used for debugging embedded ARM cores (OpenOCD) and this installer automatically removes the previous versions - that's simple. However, sometimes that would be desirable to have more than just one version installed (each version has it's own folder, so there's no conflict here) due to various (in-)compatibility issues etc.
I'm trying to create an installer which would have an option in the Feature tree (or anywhere else) to uninstall (or not) the previous version.
Basically there is this install sequence:
<InstallExecuteSequence>
<Custom Action="NewerVersionDetected" After="FindRelatedProducts">DOWNGRADE</Custom>
<RemoveExistingProducts After="InstallFinalize"/>
</InstallExecuteSequence>
I know that RemoveExistingProducts can be made conditional by putting a condition between RemoveExistingProducts tags but... what should the condition be? There's a lot of info about making features conditional or about conditions like OS version and some registry entries, but I haven't found any useful info about "user-defined conditions"...
Let's say that in the feature tree there is this element:
<Feature Id="UninstallOlderVersionFeature" Title="Uninstal previous versions" Level="1" Description="..."/>
How to make uninstallation previous version conditional on this feature (or any other method the user could select during the installation - a question box or a separate window or whatever it takes)?
Any help appreciated (by me and the users of the installer), as I'm not very good in Wix and XML (I'm an embedded person (; )
If any more details on the whole Wix file are required - tell me and I'll post relevant bits.
Try this:
<RemoveExistingProducts After="InstallFinalize">
<![CDATA[&UninstallOlderVersionFeature=3]]>
</RemoveExistingProducts>
It is the state wether the feature is selected. "3" says that the Feature is selected for installation.