MSIEXEC using command line REINSTALL not using original INSTALLDIR - wix

I am trying to configure Wix to build my msi to only perform build versions (1.0.x) of my product in conjunction with the REINSTALL property, my problem is that when I run the command line: MSIEXEC.exe /i my.msi /l*vx build-inst.log REINSTALL=ALL REINSTALLMODE=vamus it fails to do anything.
I have checked the msi log and found that it is looking for the existing product in the default folder (.\program files (x86)...\myproduct) yet when I installed it the first time I actually used a custom path (c:\myproduct). It was my impression that using REINSTALL the installer would use the installed path of the original product.
Is this actually the case? Should I be specifying the INSTALLDIR on my command line? I would rather not as this is meant for use by clients and I cannot guarantee I will know where the product was installed.
This method of performing "build" upgrades has been suggested in a couple of places but I can not find anything explaining any need to specify the INSTALLDIR
Is there any way to configure this in Wix?
Thanks
Kieran

The easiest solution would be to store the installation directory in the registry and look it up upon reinstalling.
To look up your registry value, you'd use something of the sort:
<Property Id="INSTALLDIR">
<RegistrySearch Id="InstallLocation" Root="HKCU"
Key="SOFTWARE\Company\Product" Name="Location" Type="raw" />
</Property>
If the registry value isn't found, the INSTALLDIR property will be set to your directory structure.
Rob has a complete solution on his blog for when you specify such a property from the command line.

Normally the original entries in the directory table are stored for reinstall without that you store them yourself.
So there is something "special" in your MSI, if this doesn't work. If you have a custom action which sets directory properties like INSTALLDIR, you should not use it. E.g. give them a condition "Not Installed".

I found out that the problem was due to using a wildcard for the product id, so every time a new msi was built it created a new product id.
By fixing this it seemed to resolve the problem, though I have also implemented the registry key option as it will help for upgrades where I do want to change the product id.
Thanks

Related

WiX 3.8 keeping config file state during Major Upgrade

I'm using WiX 3.8 (the latest stable release, I think), and I can't seem to get a config file to not get uninstalled-and-reinstalled during a major upgrade.
There are lots of questions about this on SO -- a lot of answers point to this site as a good answer. However, the suggestion given doesn't work (for me).
What the site says is to place each config file in its own component and mark the file as the key path of the component. Something like this:
<Component Id="config.xml"
Guid="*"
Directory="folder_where_config_file_lives">
<File Id="config_file"
Source="$(var.Project.ProjectDir)bin\Release\configFile.xml"
KeyPath="yes"/>
</Component>
Great. Next it says to schedule RemoveExistingProduct after the InstallFiles action, like so:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFiles"/>
</InstallExecuteSequence>
Problem is, when I compile, I get this error:
The InstallExecuteSequence table contains an action
'RemoveExistingProducts' that is declared in two different locations.
Please remove one of the actions or set the Overridable='yes'
attribute on one of their elements.
This person also had that problem, but he seems to have solved it. What fixed it for him was adding a scheduling attribute to the , which effectively got rid of the "two different locations" declaration problem (I guess):
<MajorUpgrade Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
So when I substitute the schedule change attribute (which contains a attribute itself, I guess), not only does it not work -- the config file gets removed and replaced during the upgrade -- it causes even more weirdness. My project has a bootstrapper with a lot of MSIs, and although I get log files for the installation of all of the MSIs that are after the MSI that contains the config file, they aren't installed.
Let me repeat that: the logs say that the MSIs are installed, but they aren't. There's probably a rollback somewhere that I can't find in the log files, but reading the MSI log files it looks like the installation went swimminly.
Does anyone know a way for a config file to not be removed-and-reinstalled during a Major Upgrade in Wix 3.8? What I've mentioned above is the best info from the interwebs that I could find, but I've tried pretty much everything on SO to no avail.
The MajorUpgrade element has everything you need, including where the RemoveExistingProducts action is scheduled. Don't add a RemoveExistingProducts into a sequence as well.
RemoveExistingProducts shouldn't be after InstallFiles. It's not clear where that comes from, but the documentation doesn't say that's a choice:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa371197(v=vs.85).aspx
When RemoveExistingProducts is sequenced early (such as after InstallInitialize or InstallValidate) it means that you are effectively uninstalling the old product followed by an install of the new product upgrade, and that means uninstalling the config file and installing the one in the upgrade. The way to retain the config file is to schedule REP afterInstallExecute. This results in an upgrade that is basically a version-rules install of the new product over the older installed one. The version rules mean that if you want updated binaries you must update their file versions. The good news about data files (your config file) is that updated data files won't be replaced:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
The older product then gets uninstalled, retaining the resulting set of files.
So sequencing of REP afterInstallExecute in the MajorUpgrade seems to be what you want. A caveat is that you need to follow component rules, which should happen automatically if you have auto-generated * guids in WiX.
IMO, Windows Installer was invented before XML caught on and the component rules don't handle it well. What I prefer to do is to not fight this behavior. Write your application so that one config file is owned by the installer and can always be safely overwritten and another config file that holds your user configuration data and that MSI doesn't know about. This second file should take override the first file.

How can I conditionally overwrite file during WIX install?

I have two work modes in my installer:
use config files left from previous installation
delete all existing configs and put default configs instead
The mode is determined by the checkbox in the WPF UI of the installer. If second mode is selected, then CustomAction is run, which manually deletes the configs folder from disk:
<InstallExecuteSequence>
<Custom Action="RemoveConfigsFolder" After="RemoveFolders" Overridable="yes">NOT Installed AND DELETESETTINGS=1</Custom>
</InstallExecuteSequence>
I'm using NeverOverwrite attribute:
<ComponentGroup Id="Configs" Directory="INSTALLDIR" >
<Component Id="Configs" Permanent="yes" NeverOverwrite="yes">
<File Id="main.config" Name="main.config" Source=".\Configs\main.config" KeyPath="yes" />
</Component>
</ComponentGroup>
The first mode works fine in this case, but when I try to use second mode it fails and all configs are just deleted and never created again during the installation.
During my research of the issue, I think I've found the reason why this happens: https://community.flexerasoftware.com/showthread.php?96157-The-truth-the-whole-truth-about-quot-Never-overwrite-quot-and-quot-Permanent-quot-files&p=156826#post156826
Actually this is a Windows Installer issue. If you log the uninstall
you will notice that very early in the installation the Installer
decides that the component containing this file will not be installed
because it is marked "Never Overwrite" and a copy of this file already
exists on the target machine. The uninstall happens after that which
removes the existing file. This is because the Installer decides this
when the "CostFinalize" action is launched. This action HAS to be run
before the "RemoveFiles" action.
But how do I fix it?
The problem with settings such as Never Overwrite or Permanent is they look like build settings, but they are not really - they stick to the system attached to the component id. So resetting in the project won't help because it's associated with that id. It's also not clear why setting Never Overwrite might have been a solution to some problem, because by definition patches and overwrite upgrades won't overwrite it, but overwriting it is a requirement of your setup.
Even if you had not set Never Overwrite the Windows Installer rules would not overwrite the file if it was modified after install. So if you had installed it, then it was altered, and then you did an upgrade, the file would not be overwritten (which is another reason why Never Overwrite does not seem needed).
Another issue is that your custom action RemoveConfigsFolder is not marked with an Execute enumeration value, therefore it is immediate, therefore it does not run elevated, therefore it might simply be failing, so without seeing the code it's impossible to say if reports an issue if it can't do the remove. It's also not possible to determine if it explicitly specifies the full path to the folder correctly. So the most likely quick fix to this issue is to mark the custom action as execute deferred, and the DELETESETTINGS value will need to be passed in via CustomActionData.
My initial thought is to remove the 'Never Overwrite' property. Then create a component condition that checks if the file exists. My thought is that your custom action has the condition to correctly remove the config files. If the files do not exist then the components will be selected for install.

Avoid uninstall when WiX AllowSameVersionUpgrades is Yes?

I have the WiX's Product Id="*" and AllowSameVersionUpgrades="yes", so I can have 1 installer with different features (different sample images, but same .exe).
And I want to be able to install the multiple versions in the same machine so only the new features are added and the old features remain in the folder. But the old features are always being deleted. Is there a way to handle the correct way, maybe using Custom Actions?
For anyone with a similar problem the solution I've found was create a property:
<Property Id="DELFILES" Secure="yes"/>
Secure="yes" is very important otherwise the property's value won't be read when running the installer.
And then add this condition for the custom action:
<![CDATA[(REMOVE = "ALL") AND (DELFILES = "TRUE")]]>
Finally to install I just call the .msi and pass the parameter:
MsiExec.exe /x MyInstaller.msi DELFILES="TRUE"

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").

Install a file regardless of version number with WiX

MyApp version 1.0 contained the file foo.dll. The version of this file was version 10.5.567. Now MyApp is version 2.0 and the version of foo.dll is 2.0.123. The version is LOWER than the old version. The vendor who wrote this dll decided to go backwards with the file version number. This is not a decision I can change.
How do I get WiX to always install this file?
The RemoveExistingProducts action is scheduled after the InstallFinalize action and also cannot be changed.
InstallShield had an 'always overwrite' flag but I don't think I can mimic this without changing the behavior of all files via a compiler switch. I do not want this. I just want to update THIS ONE file.
I have also tried
<RemoveFile Id="foo.dll" On="install" Name="foo.dll" />
But this did not work either. I end up getting 'Error 2753 The file foo.dll is not marked for installation' later.
It is really crazy this post is 10 years old and we also have this problem. The other approaches did not work for us.
How to solve it:
All files which should be copied regardless of their version, must be in their own component
Component element must have a unique Guid, not a *
Set KeyPath to no in the inner File element
Example:
<Component Id="cmpExample" Guid="{5ABE7229-C247-4CBA-B8DE-2C4968CD103E}" >
<File Id="fileExample" KeyPath="no" Source="$(var.TargetDir)\SomeExample.dll" />
</Component>
I would recommend to aggregate all those component elements in a component group. And furthermore you can use XML Transformation to get rid of those files if you use heat.exe to harvest all your files.
With this solution you can copy the file, even if the installed file version is higher. And it still works as expected when a higher file version will be installed with your upgrade.
This isn't easy because it is against the standard behaviour of MSI-packages. When compiling, you have to set supress-file-options with -sf in light.exe. Then there are no file-informations from your files read. You can set DefaultVersion this version will be used.
I have also used RemoveFile in a solution, but i can say that it works. I have add it in same componente where i add the file.
<Component>
<File DiskId="1" Id="fooDLL" Name="foo.dll" Vital="yes"
DefaultVersion="$(var.MAJORVERSION).$(var.MINORVERSION).$(var.BUILDVERSION)"
DefaultLanguage="0"></File>
<RemoveFile Id='RemoveOldDLL' On='install' Name='foo.dll'/>
</Componente>
This is not a clean way, but to remove an old version it works for me.
I answered a similar question a while back:
Forcing an upgrade of a file that is modified during its initial installation
I think I would use a companion file to solve this problem, but you might get validation errors since the file in question is a dll and hence considered an executable. I believe it would still work, and reliably so.
Annotation
Force always overwrite in Light.exe output with the "InstallShield trick":
XPath="/wixOutput/table[#name='File']/row/field[5]" InnerText=„65535.0.0.0“
One thing that could be worth trying is to set RemoveExistingProducts real early in the sequence. The MSI should then remove the old program first, then install the upgrade. This will solve your issue. e.g.
<InstallExecuteSequence>
<RemoveExistingProducts After="CostFinalize" />
</InstallExecuteSequence>