How to retain user-customized files during a Windows Installer major upgrade - wix

According to this, to retain user-customized files during a Windows Installer major upgrade I need:
1. Place each .config file in its own component and mark the .config file as the key path of the component.
2. Schedule the RemoveExistingProducts action in the new version of your MSI after the InstallFiles action.
<Component Id='config.json' Guid='GUID'>
<File Id='config.json' Name='config.json' DiskId='1' Source='config.json' KeyPath='yes' />
</Component>
...
<InstallExecuteSequence>
<RemoveExistingProducts Before="InstallFiles"/>
</InstallExecuteSequence>
There is following warning during build:
warning LGHT1076 : ICE63: Some action falls between InstallInitialize
and RemoveExistingProducts.
There is following error during installation:
The installer has encountered an unexpected error installing this
package. This may indicate a problem with this package. The error code
is 2613.
What is wrong?

The simplest way to handle this problem is to design your application so that user modified data is in it's own files. This way the installer can then own the files it lays down and the application can merge the two sets of data at runtime. MSI won't delete or overwrite what it doesn't know about.
Otherwise you get into complicated discussions of how to merge xml/json and other file types during an upgrade.

Related

How to remove desktop shortcut when MSI minor upgrade is get uninstalled?

My customer wants to see some "version number" in file name of the program's desktop shortcut. For example: "Foobar 10.lnk" or "Foobar 12.lnk".
Therefore, we have the following snippet in one of our .WXS files:
<DirectoryRef Id="DesktopFolder">
<Component Id="..." Guid="...">
<Shortcut Id="...." Name="Foobar_10" ... />
<RegistryValue ... />
</Component>
</DirectoryRef>
Every time we release a new version, we update that .WXS file. There's no issues in this scheme while we are producing major upgrades, because our major upgrades first uninstall existing product, and then install new version.
But now we have to release a minor upgrade. And put "Foobar 10.1.lnk" file into the "Desktop" directory. We have, therefore, one MSI file for version 10, another one for version 10.1 and MSP patch to upgrade the product from version 10 to version 10.1.
According to Changing the Product Code it's legitimate to add or remove new shortcuts using a minor upgrade:
The update can add, remove, or modify the ... shortcuts of components that are not shared by two or more features. ... If the update removes resources, it should also update the RemoveFile and RemoveRegistry tables to remove any unused files, registry keys, or shortcuts that have already been installed.
Had we install the MSP file with shortcut snippet from above, we would end up with two shortcuts in the "Desktop" directory: Foobar_10.lnk and Foobar_10.1.lnk due to transformation applied to the Shortcut table. Therefore, according to the article I already mentioned, we have to update the RemoveFile table:
<DirectoryRef Id="DesktopFolder">
<Component Id="..." Guid="...">
<Shortcut Id="...." Name="Foobar_10.1" ... />
<RemoveFile Id="..." Name="Foobar_10.lnk" On="install" />
<RegistryValue ... />
</Component>
</DirectoryRef>
Now, if one installs that MSP file onto already installed ``Foobar version 10'', he/she gets single .LNK file inside the "Desktop" directory -- Foobar_10.1.lnk.
So far so good.
But if one removes that minor upgrade using the system's "Control panel" (minor upgrade only, therefore, reverting back to ``Foobar version 10''), he/she gets two shortcuts again: Foobar_10.1.lnk "remained" from MSI+MSP, and Foobar_10.lnk created by the CreateShortcuts action from the transformed-back Shortcut table.
My main question is: how I can remove file Foobar_10.1.lnk when the MSP minor upgrade is get uninstalled? What actions/properties should I "check" to catch such un-installation?
By the way, I tried to get log from un-installation of a minor upgrade, but I failed to uninstall an upgrade using the command line. For example, this:
msiexec -uninstall {valid-patch-guid-here} -package foobar.msi
ends up with the maintenance mode for 'foobar.msi'. Both the following commands:
msiexec /I {valid-product-code-guid} MSIPATCHREMOVE={valid-patch-guid}
msiexec -package {valid-product-code-guid} -uninstall {valid-patch-guid}
give the same (these commands are from Uninstalling Patches).
I can remove a minor upgrade using "Control panel" only. Therefore, I can't get un-installation log. Without it I can't see what happens during that process. I know it's a bad idea to ask two question at once, but may be anyone knows how to remove a patch using the command line?
Thanks.
I found a solution -- ``Transitive components''. A minor upgrade: a) adds new transitive component for a shortcut (on the "Desktop" and/or in the "Start menu") with condition evaluated to 'true', and b) modifies conditions of existing "shortcut components" to be evaluated to 'false'.
Inspired by "Minor upgrade fails on removing files" topic on SO. Thanks christopher-painter#.

How to include a modifiable loose file in WiX burn?

I have a setup package including a non-compressed file.
<DirectoryRef Id="INSTALLLOCATION">
<Component Id="LocationConfig">
<File Id="LocationConfigFile"
Source="LooseFile.Config"
DiskId="2"
Vital="no"
Compressed="no" />
</Component>
</DirectoryRef>
The purpose of this file being loose is so it can be edited before installation. This works as desired.
I have a burn chain specifying that the loose file is included as a loose file alongside the bootstrapper. Note also the use of SuppressLooseFilePayloadGeneration to allow the manual specification of the file as a loose payload.
<Chain>
<MsiPackage SourceFile="MyInstaller.msi"
Visible="yes"
Vital="no"
SuppressLooseFilePayloadGeneration="yes">
<Payload Compressed="no" SourceFile="LooseFile.Config" />
</MsiPackage>
</Chain>
The burn log looks like this:
[3860:38D8][2013-04-26T16:42:48]e000: Error 0x80091007: Hash mismatch for path: C:\ProgramData\Package Cache\.unverified\payAC32431CF002C09E2F0B537A32ACA259
[3860:38D8][2013-04-26T16:42:48]e000: Error 0x80091007: Failed to verify hash of payload: payAC32431CF002C09E2F0B537A32ACA259
[3860:38D8][2013-04-26T16:42:48]e310: Failed to verify payload: payAC32431CF002C09E2F0B537A32ACA259 at path: C:\ProgramData\Package Cache\.unverified\payAC32431CF002C09E2F0B537A32ACA259, error: 0x80091007. Deleting file.
[3860:38D8][2013-04-26T16:42:48]e000: Error 0x80091007: Failed to cache payload: payAC32431CF002C09E2F0B537A32ACA259
[33FC:3A54][2013-04-26T16:42:48]e314: Failed to cache payload: payAC32431CF002C09E2F0B537A32ACA259 from working path: C:\Users\Snixtor\AppData\Local\Temp\{c887e0cf-5038-4e15-95b1-8510d8c96b88}\payAC32431CF002C09E2F0B537A32ACA259, error: 0x80091007.
OK, the hash is failing because the file has changed. But... I want to allow the user to change the file. I can readily enough do this with a standard setup package, so what hoops do I have to jump through to get it to behave with a bootstrapper?
I found this discussion in the WiX users mailing list. Robs response of "It should just work" sounds promising, but then the discussion seems to move on to suggest it could be a bug? If the author ever raised a bug report, I can't find it.
An alternative I considered was to exclude the file altogether from the bootstrap payload, and then manually copy it over to the MSI cache path during install so the MSI can find it, though burn will never try to validate it. But the two troubles I see there are:
I can't find out how to discover the cache path from within my bootstrapper.
Even if I knew the path, I'd need to elevate the bootstrapper to copy the file. That may not be a showstopper, but I have a sneaking suspicion it might prove difficult.
This isn't supported today. Burn validates everything before placing it in the cache for security reasons. What you could do is read the external file in a custom Bootstrapper Application and store the results as persisted Variables. This will be more work but Burn just won't trust files that don't match the security hashes/signatures inserted at build time.
I worked around this same issue by hanging off the current directory property that the burn bootstrapper sets and using it with a copyfile element like so:
<Component Id="SettingsFile" Guid="...">
<CopyFile Id="Copy" Delete="no" DestinationDirectory="INSTALLFOLDER" SourceName="LooseSettings.xml" SourceProperty="CURRENTDIRECTORY" />
<RemoveFile Id="Remove" On="uninstall" Directory="INSTALLFOLDER" Name="LooseSettings.xml" />
</Component>
There are quite possibly some issues with it but I'm writing an installer to someone else's specs and it seems to work

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.

Force installer to upgrade a file in the GAC regardless of the version number

I have a WiX installer which needs to install new bits for Interop.FOOBARLib.DLL to the GAC. The problem is that the version number is the same as the old version and the new bits do not get written the GAC on an upgrade. We execute the RemoveExistingProducts action after the InstallFinalize action.
We cannot move the RemoveExistingProducts action to earlier in the install.
The foobar.dll component is not my component so I cannot increment the type library version (which would cause the version of the interop to increment and all these problems go away).
Is there a way to FORCE the file to be upgraded in the GAC even if the version is the same? I want behavior similar to “gacutil.exe /f”.
The component looks like:
<Component Id="Interop.FOOBARLib.dll" Guid="{4E0C173E-34DF-4249-A3A6-5530047FA65B}" >
<File Id="Interop. FOOBARLib.dll" Name="Interop.FOOBARLib.dll" KeyPath="yes" Assembly=".net"/>
</Component>
What you're trying to do is called an In-Place update of an Assembly in the GAC. For this to work correctly both Interop.FOOBARLib.dll libraries must have the same assembly version, but the newer dll must have a higher file version. The file version attribute must be included in the new MSI's MsiAssemblyName table. Wix does not include this attribute by default, so you have to add the following parameter to your .wixproj file:
<SetMsiAssemblyNameFileVersion>True</SetMsiAssemblyNameFileVersion>
See also:
In-place updating using Wix
You can regenerate the interop assembly yourself and force it to get a higher version like this:
tlbimp /asmversion:1.2.3 /out:Interop.FOOBARLib.DLL foobar.dll
You could try to execute a custom action to delete the file, right before the components are installed. It's not recommended to use vbscript for custom actions but the example below should still illustrate the idea.
<CustomAction Id="ForceRemove" Script="vbscript" Execute="deferred">
<![CDATA[
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
fso.DeleteFile("c:\somefile.dll")
]]>
</CustomAction>
<InstallExecuteSequence>
<Custom Action='ForceRemove' Before='InstallFiles'/>
</InstallExecuteSequence>

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>