Are install-time changes authored with XmlFile reversed during uninstallation? - wix

Question
On the XmlFile element, does Permanent="yes" means it will not attempt to remove the changes applied by XmlFile during install? If this is not right, how can I copy an XML file to install folder during installation, apply changes to it, and then successfully remove that file during uninstall?
Background
I am creating an MSI file with WiX. I am using the XmlFile element to change an XML file after I install it. I am confused about whether changes made with XmlFile get backed out during uninstall, and how that relates to the XmlFile element's Permanent attribute.
I've seen conflicting answers to the question of whether changes made during install with XmlFile get backed out on uninstall. First, there is this SO answer by Stein Åsmul, which purports to quote WiX developer Bob Arnson (emphasis mine):
"You can do everything (and more) that XmlFile supports with XmlConfig but it requires extra authoring that's not necessary with XmlFile. XmlFile is best at modifying XML files that you're installing (e.g., to add an attribute reflecting the installed path of a file); it can't remove the modifications at uninstall time but if your setup installed the file, it will be uninstalled anyway. XmlConfig is best at modifying shared XML files because it supports uninstalling the modifications."
Unfortunately the source for that quote is a dead link, so I can't check to see if there are caveats, or what version of WiX Toolset it applies to.
This XML tutorial on FireGiant appears to contradict that quote (again, emphasis mine):
So, to carry out the modifications we planned, we need the following entries (note that we have to provide our own sequencing, this is important to ensure that the changes will be removed in the proper reverse order upon uninstallation):
The FireGiant tutorial goes on to show XML elements modified like this:
<Component Id='Settings' Guid='YOURGUID-574D-4A9A-A266-5B5EC2C022A4'>
<File Id='XmlSettings' Name='settings.xml' DiskId='1' Source='settings.xml' Vital='yes' />
<util:XmlFile Id='XmlSettings1' File='[INSTALLDIR]settings.xml'
Action='createElement' Name='add' ElementPath='//settings' Sequence='1' />
<util:XmlFile Id='XmlSettings2' File='[INSTALLDIR]settings.xml'
Action='setValue' Name='key' Value='a_key' ElementPath='//settings/add' Sequence='2' />
<util:XmlFile Id='XmlSettings3' File='[INSTALLDIR]settings.xml'
Action='setValue' Name='value' Value='a_value' ElementPath='//settings/add' Sequence='3' />
<util:XmlFile Id='XmlSettings4' File='[INSTALLDIR]settings.xml'
Action='setValue' Value='key_item' ElementPath='//settings/add' Sequence='4' />
<util:XmlFile Id='XmlSettings5' File='[INSTALLDIR]settings.xml'
Action='createElement' Name='inside' ElementPath='//settings/add' Sequence='5' />
<util:XmlFile Id='XmlSettings6' File='[INSTALLDIR]settings.xml'
Action='setValue' Value='inside_item' ElementPath='//settings/add/inside' Sequence='6' />
</Component>
I am using WiX Toolset version 3.11.2. I suspect the FireGiant tutorial is newer and thus more likely to be correct. The documentation for the XmlFile element doesn't discuss this one way or another. It does, however, seem to hint that an attribute named Permanent might come into play. Valid values for Permanent are yes or no, but the description of the attribute is not specific as to what the effect will be for each value. This is the complete description of the attribute Permanent:
Specifies whether or not the modification should be removed on uninstall. This has no effect on uninstall if the action was deleteValue.
I'm left guessing whether I am interpreting the meaning of the Permanent attribute correctly.
So here's my question: Does Permanent="yes" means it will not attempt to remove the changes applied during install? If this is not right, how can I copy an XML file to install folder during installation, apply changes to it, and then successfully remove that file during uninstall?
Notes
I am using WiX Toolset version 3.11.2.

The only change that XmlFile can "undo" is Action="createElement" -- because that just turns into deleting the specified element. The tutorial uses XmlFile elements that can be undone -- creating elements and setting values in them -- so it's accurate.
XmlConfig was created to allow arbitrary XML modifications to be made and unmade at uninstall.

Related

WiX NeverOverwrite flag deletes file

I have a data file that is installed with my application. When it is installed fresh, the data file gets installed properly. When it is upgrading, it was always overwriting the existing instance of the file, which was not wanted. I added the NeverOverwrite="yes" flag, and now for a new install is still the same, but for an existing install, it is deleting the existing file, and not installing the new file either. Have I used the flag incorrectly? This is the snippet in question:
<Component Id="cmp8553E6DDC92DBCDC568FB76CA13E7AF2" Guid="{C94263EB-56AF-4B19-942F-C92998252932}" NeverOverwrite="yes">
<File Id="fil7C9456E83E3FB4618684509DEEF67EBD" KeyPath="yes" Source="$(var.CDWrapper.ProjectDir)..\foo\bar" />
</Component>
Turns out I didn't have a full understanding of the NeverOverwrite flag and our usage of it. I will have to find a different method to preserve the existing file instead of always installing a new copy, but that's a whole different issue that should have reasonable answers elsewhere.

How to correctly add new files in WiX installation to create an uninstallable patch?

Good time,
I'm trying to create the uninstallable patch (msp file) which should contain the newly added files. The file is added in following maner:
<Directory Id="Some Dir">
<Directory Id="MYDIR" Name="Some Name">
<Component Id="Some Component" Guid="{GUID}" KeyPath="yes">
<File Id="README.txt" Name="README.txt" Source="to_install\README.txt" KeyPath="no"/>
<File Id="default.cfg" Name="default.cfg" Source="to_install\default.cfg" KeyPath="no"/>
... // some more files
</Component>
...
...
I've also tried using the DirectoryRef but got the same results.
The "MYDIR" directory is used in other places also and several other files are already installed there, but the patch is adding new files, and the installation log shows that the CreateDirectory table becomes modified and the uninstallation for patch becomes disabled.
What is a correct way to do it using WiX?
KeyPath :
This value points to a file or folder belonging to the component that the installer uses to detect the component.
It is creating entry in CreateFolder of MSI because of KeyPath is provide at component level that is taking installation folder as its key so your patch become not uninstallable.
Provide KeyPath at one of the file of component that is the most important for that component. So it does not make entry into CreatFolder table of MSI and your patch become uninstallable.
Whether a patch is uninstallable isn't really related to a piece of WiX source like that. One thing you definitely need for an uninstallable patch is a PatchMetadata table that says that the patch is uninstallable, as here:
http://wixtoolset.org/documentation/manual/v3/patching/patch_building.html
or here, see PatchMetadata table if you use a PCP file, AllowRemoval:
https://msdn.microsoft.com/en-us/library/aa370344(v=vs.85).aspx
So there are some things you need to do during patch creation to make the patch uninstallable that are nothing to do with your WiX source. It's not clear from your question how you are creating the patch correctly.
Even if you get the patch generation working, there are some rules that must be followed, described here:
https://msdn.microsoft.com/en-us/library/aa367850(v=vs.85).aspx
where it says (for example) that you can't change the component ids of any existing installed items. Create a verbose log when installing the patch and look for messages like SELMGR and entries saying removl of components is not supported, and if that has happened it means the patch will not apply correctly.

Patch (minor upgrade) creation issues with MSM (merge modules)

I am facing issues with patches (minor upgrade) installation (updates) with MSM (merge modules).
I am creating MSI (test.msi) with texst.wxs. And inside text.wxs referring to app.msm file (there is a folder app, which contains so many folders and files. And harvesting this folder and making app.msm file)
Below are steps for making app.msm file.
heat dir "app" -gg -sfrag -template:module -srd -ke -var var.source -out app.wxs
candle -dsource=app app.wxs
light app.wixobj
Below is snippet of test.wxs file
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
....
....
<Directory Id='Config' Name='Config'>
<Component Id='APP_CLIENT' Guid='*'>
<Component Id='Manual' Guid='*'>
<File Id='Manual' Name='Manual.pdf' DiskId='1' Source='Resources/Manual.pdf'
KeyPath='yes'>
<Shortcut Id="startmenuManual" Directory="ProgramMenuDir"
Name="Instruction Manual" Advertise="yes" />
</File>
</Component>
</Directory>
<Directory Id='exmp_REPO' Name='!(loc.Merge_FolderTitle)'>
<Merge Id="LocalRepository" Language="1033" SourceFile="app.msm" DiskId="1"/>
<Component Id='exmp_REPOSITORY' Guid='*'>
<CreateFolder/>
<RemoveFolder Id='exmp_REPO' On='uninstall' />
</Component>
</Directory>
....
<Feature Id='Complete' Display='expand' Level='1' ConfigurableDirectory='MYAPPPATH'>
<ComponentRef Id='Manual'/>
<ComponentRef Id='App_CLIENT'/>
<ComponentRef Id='exmp_REPOSITORY'/>
...
...
I am able to make major upgrade with my test.wxs by using app.msm (merge module). But not able to make patch with successful installation. Patch install (update) is reflecting in version change in the "Programs and Features" and showing in "View Installed updates". The manual changes also are reflecting with patch update. But whatever the changes in "app" (which are created app.msm and referred in test.wxs) folder are not reflecting.
I have used 2 approaches for making patch, which are mentioend in below urls
1) http://wixtoolset.org/documentation/manual/v3/patching/patch_building.html
2) http://wixtoolset.org/documentation/manual/v3/patching/wix_patching.html
Please help in this regards.
First, I would advise to find out, whether the built patch contains the correct files or not. If not, you have a build problem, that the msm is not updated. If yes, you have most likely a problem with the content of the msm which may be not consistent with it's predecessor (especially GUIDs, table primary keys, etc.).
You can find out and see the content of the patch without installing with tools like Orca and Insted which you can search and download.
Second, using merge modules is highly complicating things, especially for patches, and of limited usefulness, if they are your own and you use them only once. Msms are primarily made for situations, where you need the .msm at least for 2 different MSI packages. I have seen a handfull of problems using merge modules in patches with other tools, BTW. I have no special experience here with WiX+patches+MSMs, but, it's as I said.
Last, but not least, you will have to choose, if you really want to keep this complexity in the future. As I remember, there are other possibilities in WiX to modularize / encapsulate parts of your software.
You can check the versions of files in merge modules and MSI files by opening them with Orca and looking in the File table. Or open the MSI file with Orca and then Transform=>View patch to see changes.
It may be obvious, but a binary versioned file will be replaced by a file with a higher file version. I mention because there is a belief out there that somehow "new" files replace "old" files, and that's wrong. Versions matter.
Generally you need to install a patch with an msiexec command that specifies REINSTALL=ALL REINSTALLMODE=omus. Double-clicking an MSP file will not necessarily just work unless you've arranged for it to do this internally with a custom action that sets them when PATCH is set.
The patch will not work if you break component rules. A common mistake is to remove a component during a patch, and that will result in an "advertised" update that doesn't actually update anything. In a verbose log look for SELMGR entries and text about removal of components is not supported. Setting the MSIENFORCEUPGRADECOMPONENTRULES property to 1 will fail the patch if you've done this.
If a file has no version whether it's overwritten depends on the replacement rules here, and there's some difference if the file is hashed or not:
https://msdn.microsoft.com/en-us/library/aa370531(v=vs.85).aspx
Also: how do you know the patch isn't working? If you have no file versions then you can't know if a file has been replaced unless you look very carefully. You cannot trust the dates because Windows changes timestamps when a file is installed. You really need to build binary files with file versions because everything like patches, hotfixes, service packs etc will use them to replace binary files. Otherwise for data files use file hashing.
I see several potential problems here, maybe even in addition to the other answers:
when you heat up files for your merge modules -gg autogenerates new component guids. This will not work with patches, since it'll basically add a bunch of new components (new guids each time!!). Also, you'll remove components this way, which is something you shouldn't do and which cannot be done easily, unless you still include the original merge module. And then you'll end up with path problems.
The tutorial for wix patching uses wixpdb files for diffing the original and the updated installer. With wixpdb files, merge modules will not be patched, irrespective of whether files changed in them or not. You will need to do administrative installations and then diff on the msi itself. You'll still have problem #1.
your wxs snippet is bad. At least the xml element is never correctly closed. Your Feature also has a MergeRef somewhere?
Some tips:
you can view what your patches do with a program called Orca. Open the original msi, then just drag your msp patch file on top of it.
rather don't use merge modules, they complicate things. You can also use heat to generate a fragment which you then simply include into your wix project.
use the wix patching approach (Patch element, not the PatchCreation element). It's easier, but you have the same control
don't autogenerate guids if you plan to update those autogenerated components with a patch. It won't be a problem with major upgrades :)
Just one important issue of a number of potential reasons while files are not updated correctly: You write, in your .msm is a high number of unversioned files, like .xml, etc.
Important rule:
EVERY unversioned file which is changed on the PC AFTER the first MSI install not by the MSI engine itself (e.g. you edited a config.xml file or so), will be normally NEVER updated by MSI again, not by patch and not by Major Upgrade. (With normally I mean, you have to take special actions, like uninstalling or especially specifying those files as socalled companion files).

WiX: Make component upgradable, but not uninstallable

I am creating an installer using WiX (and I must say I really don't enjoy it, but that is less WiX's fault and more MSI's. And I absolutely applaud Rob Mensching's helpfulness here and elsewhere).
Is it possible to create a component such that it:
can be updated (via patches and major upgrades)
Files that have changed since install will not be uninstalled (it doesn't matter whether they're also not uninstalled if unchanged)
Ordinary file versioning rules apply when updating (that is, unversioned files that changed since install aren't updated, others are)
I am beginning to think this isn't possible. Marking a component as permanent means the component won't ever be uninstalled, which means it isn't updatable (at least not via major upgrade; how about patches?).
Marking a component NeverOverwrite won't prevent it from being uninstalled, even if the user has changed the unversioned keyFile (thus the user's work is deleted).
I appreaciate all definitive answers, even if they are negative.
I did not try it, but you probably can use a custom action to do this. See From MSI to WiX, Part 5 for an example. The Custom Action Type 38 example includes this:
<Directory Id="INSTALLDIR" Name="Minimal" LongName="MinimalInstallation">
<Component Id="Component1"
Guid="{A77C5B06-132D-4884-8E17-EA10A83C812D}">
<File Id="ReadMe" DiskId="1" Name="Readme.txt" Source="Readme.txt" Vital="yes" KeyPath="yes" />
<!--RemoveFile Id="RemoveCopy" Name="ReadMe2.txt" On="uninstall" /-->
</Component>
</Directory>
....
<CustomAction Id="CopyReadMe"
Script="vbscript">
<![CDATA[
Set fso = CreateObject("Scripting.FileSystemObject")
path = Session.Property("INSTALLDIR")
fso.CopyFile path & "ReadMe.txt", path & "ReadMe2.txt"
]]>
</CustomAction>
<InstallExecuteSequence>
<Custom Action="CopyReadMe" After="InstallFinalize">Not Installed</Custom>
</InstallExecuteSequence>
Here Readme.txt is installed, and using a vbscript custom action copied to ReadMe2.txt. If you do some version and/or modification date checking in vbscript, you can control yourself whether the ReadMe2.txt is overwritten or not. And without the RemoveFile, wix will not uninstall ReadMe2.txt, because it does not know the file exists.
For an upgrade it might be possibe to use a different custom action to copy ReadMe2.txt back to Readme.txt before installing. That way you can let wix handle the Ordinary file versioning rules to determine if Readme.txt can be updated. Then you don't have to add version handling to the vbscript.

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>