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

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#.

Related

WiX: Replace sub directory on upgrade

I'm using WiX to install some program v1.0 which brings its own JRE 8.x, located in a sub directory jre/. Now I'm upgrading my program to version 1.1, which is based on an 11.x JRE. My MajorUpgrade is
<MajorUpgrade Schedule="afterInstallExecute" DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
Update 1: I'd like to keep afterInstallExecute because there are a
couple of files I'd like to keep.
So what's the WiX way to replace that/any sub directory completely?
I tried to just replace JRE 8 with JRE 11. Turns out this leaves existing files in place, especially jre/lib/ext/. But as updating a JRE from below v9 to v9+ requires that folder not to exist, the JVM refuses to start after the upgrade
so I tried to mark all files within the jre/-sub dir with RemoveFile ... On='install' (source), hoping installing the new files would remove the old files, but that did not work
finally instead reusing jre/ I put all my JRE 8 files in sub directory jre-8u181/ and JRE 11 files in jre-11.0.2_7/, hoping WiX recognizes "oh, I do not need sub dir jre-8u181/ any longer, so I can delete it". Doesn't work either.
Update 2: The JRE folder is included via
<Directory Id='foo_jre_32' Name='jre'>
<?include jre32.wxi ?>
</Directory>
with jre32.wxi being a rather long, auto-generated list like
<Include>
<Component Id='_6387aee1c48be620fded05f509eb61ef' Guid='*'>
<File Id='_e566fc2d9d74ea7a1249736056b2a2e4' Source='foo\jre\THIRDPARTYLICENSEREADME.txt' Name='THIRDPARTYLICENSEREADME.txt' DiskId='1' />
</Component>
<Component Id='_0dcaf4ce39ea5a8ab63704e6e8e23514' Guid='*'>
<File Id='_9e93e8a3fced0b6549171fc677dad65c' Source='foo\jre\README.txt' Name='README.txt' DiskId='1' />
</Component>
<Component Id='_2fa36d02341dba40d7dd8dabc21aa506' Guid='*'>
<File Id='_3654927acd82cae7b37fb9b7c92a6615' Source='foo\jre\release' Name='release' DiskId='1' />
</Component>
<Directory Id='_9b3d18d11d414dfb14909186a8ebb330' Name='lib'>
<!-- ... -->
Change attribute to Schedule="afterInstallValidate" (or remove the attribute, because this is the default).
This causes Windows Installer to completely remove the existing version before installing the new version of the product. You can now remove the folder simply by omitting it from your new version.
This scheduling of Major Upgrade is what I recommend in general. It gives you the least headaches because the new version installation will always start "fresh", without remains of the old version (unless there are user-modified files). Sure this will be somewhat slower than Schedule="afterInstallExecute", but a correct installation is more important than an installation that is 2 seconds faster.
See also documentation of MajorUpgrade element.
I think I've found it, thanks to #tom-blodget: I include the JRE folder as a set of Directory and Component/File. The File have a Guid, which is fixed (I need them to be reproducible across our build systems to allow references to them). I didn't update them for the new JRE folder m( Now I do, plus I install the JRE folders (old and new one) to different target folders (option #3 in my initial question). Thanks again!

How do I stop removal of files during uninstallation using Wix

When uninstalling my application, I'd like to configure the Wix setup to NOT to remove few files that were added as part of the installation. It seems like the uninstaller removes all the files that were originally installed from the MSI file. How do I do that?
Here are my files which I wish to keep it forever
<Binary Id="RootCABinary" SourceFile="Assets\Certificates\RootCA.cer" />
<Binary Id="SubCABinary" SourceFile="Assets\Certificates\SubCA.cer" />
I have used WixIIsExtension.dll to install these certificates to the windows store.
Overwrite: Is it important that the file never gets overwritten? If so, add
"Never Overwrite" to your component. WiX attribute: NeverOverwrite="yes". Remember to test upgrade scenarios!
Permanent Component: As stated by Pavel, you can set a component permanent:
<Component Permanent="yes">
<File Source="License.rtf" />
</Component>
Blank GUID: Another way to do it is to set a blank component GUID. It essentially means "install and then leave alone". No repair or uninstall should be done (remember to add NeverOverwrite="yes" if the file should never be overwritten):
<Component Guid="" Feature="MainApplication">
<File Source="SubCA.cer" KeyPath="yes" />
</Component>
Read-Only Copy: I sometimes install files to a per-machine path (for example somewhere under program files) and then copy them to a per-user location (somewhere in the user-profile) on application launch, and then do operations on them that entail that the files should not be deleted. This is good in cases where you want to do something that change the files in question (they need to be writable). Then it is good to "untangle" them from deployment concerns (files will not be meddled with by installer operations at all - the installer has no knowledge of them). The original read-only copy installed to program files can be uninstalled though (no longer needed?).
Other approaches: You can also create such files using custom actions during installation (usually text files only), you can download the file in question from your web site on application launch (makes the file easy to manage and update / replace? Vulnerable to connection problems - firewalls, etc...) and the application can create the file using "internal defaults" in the main executable on launch. And there are no doubt further approaches that I can't recall.
Put your binaries in a separate WiX component and make it permanent. Have a look at this thread as well

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

How to uninstall with msiexec using product id guid without .msi file present

I'm trying to automate the uninstallation of packages created using WiX for the purposes of changing the installed software stack & configuration without reprovisioning a whole OS. Eventually I'll use powershell scripting to do this but at the moment I can't seem to get my test package to uninstall interactively with cmd.
If I run:
msiexec /x '{A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}'
msiexec /x A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8
I get:
"The installation package could not be opened. Verify that the package
exists and that you can access it, or contact the application vendor
to verify that this is a valid Windows Installer Package."
If I run:
msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
I get:
"This action is only valid for products that are currently installed"
I've looked at the windows installer guide, the WiX documentation, msiexec documentation and used orca to go over the .msi myself but I've not really found anything that gives a clear picture of how an uninstall is processed. Is the .msi file required and if not then why does windows installer seem to think it is when given a GUID?
The WiX code for the .msi installer is:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='htp://schemas.microsoft.com/wix/2006/wi' >
<!--DO NOT COPY / PASTE THE PRODUCT ID GUID BELOW TO YOUR OWN WIX SOURCE -->
<Product Id='A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8' Language='2057'
Manufacturer='COMPANYNAME IT-Operations'
Name='COMPANYNAMEServerListener' Version='1.0.0'
UpgradeCode='PUT-GUID-HERE'>
<Package Id='*' Manufacturer='COMPANYNAME IT-Operations' Compressed='yes' />
<Media Id='1' Cabinet='COMPANYNAMEServerListener.cab' EmbedCab='yes' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='COMPANYNAME' Name='COMPANYNAME'>
<Directory Id='INSTALLDIR' Name='COMPANYNAMEServerListener'>
<Component Id='MainExecutable' Guid='*' >
<File Id='COMPANYNAMEServerListener.exe'
Source='COMPANYNAMEServerListener.exe' Vital='yes'
KeyPath='yes' />
<ServiceInstall
Id='COMPANYNAMEServerListenerInstall'
DisplayName='COMPANYNAMEServerListener'
Description='Accepts and discards TCP connections on port 28028 to indicate that this server is alive and ready to be controlled'
Name='COMPANYNAMEServerListener'
Account='NT AUTHORITY\LocalService'
ErrorControl='normal'
Start='auto'
Type='ownProcess'
Vital='yes'
>
<ServiceDependency Id='tcpip'/>
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="COMPANYNAMEServerListener" Wait="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id='Complete' Level='1' >
<ComponentRef Id='MainExecutable' />
</Feature>
<CustomTable Id ="COMPANYNAMEMetadata">
<Column Id="Property" Type="string" Category="Identifier" PrimaryKey="yes"/>
<Column Id="Value" Type="string"/>
<Row>
<Data Column="Property">InstallString</Data>
<Data Column="Value">/qn</Data>
</Row>
</CustomTable>
</Product>
</Wix>
"Reference-Style" Answer: This is an alternative answer to the one below with several different options shown. Uninstalling an MSI file from the command line without using msiexec.
The command you specify is correct: msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
If you get "This action is only valid for products that are currently installed" you have used an unrecognized product or package code, and you must find the right one. Often this can be caused by using an erroneous package code instead of a product code to uninstall - a package code changes with every rebuild of an MSI file, and is the only guid you see when you view an msi file's property page. It should work for uninstall, provided you use the right one. No room for error. If you want to find the product code instead, you need to open the MSI. The product code is found in the Property table.
UPDATE, Jan 2018:
With all the registry redirects going on, I am not sure the below registry-based approach is a viable option anymore. I haven't checked properly because I now rely on the following approach using PowerShell: How can I find the product GUID of an installed MSI setup?
Also check this reference-style answer describing different ways to uninstall an MSI package and ways to determine what product version you have installed:
Uninstalling an MSI file from the command line without using msiexec
Legacy, registry option:
You can also find the product code by perusing the registry from this base key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall . Press F3 and search for your product name. (If it's a 32-bit installer on a 64-bit machine, it might be under HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall instead).
Legacy, PowerShell option: (largely similar to the new, linked answer above)
Finally, you can find the product code by using PowerShell:
get-wmiobject Win32_Product | Format-Table IdentifyingNumber, Name
Similar post: WiX - Doing a major upgrade on a multi instance install (screenshot of how to find the product code in the MSI).
msiexec.exe /x "{588A9A11-1E20-4B91-8817-2D36ACBBBF9F}" /q
The good thing is, this one is really easily and deterministically to analyze:
Either, the msi package is really not installed on the system or you're doing something wrong.
Of course the correct call is:
msiexec /x {A4BFF20C-A21E-4720-88E5-79D5A5AEB2E8}
(Admin rights needed of course- With curly braces without any quotes here- quotes are only needed, if paths or values with blank are specified in the commandline.)
If the message is: "This action is only valid for products that are currently installed", then this is true. Either the package with this ProductCode is not installed or there is a typo.
To verify where the fault is:
First try to right click on the (probably) installed .msi file itself. You will see (besides "Install" and "Repair") an Uninstall entry. Click on that.
a) If that uninstall works, your msi has another ProductCode than you expect (maybe you have the wrong WiX source or your build has dynamic logging where the ProductCode changes).
b) If that uninstall gives the same "...only valid for products already installed" the package is not installed (which is obviously a precondition to be able to uninstall it).
If 1.a) was the case, you can look for the correct ProductCode of your package, if you open your msi file with Orca, Insted or another editor/tool. Just google for them. Look there in the table with the name "Property" and search for the string "ProductCode" in the first column. In the second column there is the correct value.
There are no other possibilities.
Just a suggestion for the used commandline: I would add at least the "/qb" for a simple progress bar or "/qn" parameter (the latter for complete silent uninstall, but makes only sense if you are sure it works).
There's no reason for the {} command not to work. The semi-obvious questions are:
You are sure that the product is actually installed! There's something in ARP/Programs&Features.
The original install is in fact visible in the current context. It looks as if it might have been a per-user install, and if you are logged in as somebody else now then it won't know about it - you'd need to log in under the same account as the original install.
If the \windows\installer directory was damaged the cached file would be missing, and that's used to do the uninstall.
Thanks all for the help - turns out it was a WiX issue.
When the Product ID GUID was left explicit & hardcoded as in the question, the resulting .msi had no ProductCode property but a Product ID property instead when inspected with orca.
Once I changed the GUID to '*' to auto-generate, the ProductCode showed up and all works fine with syntax confirmed by the other answers.
Try this command
msiexec /x {product-id} /qr
you need /q at the end
MsiExec.exe /x {2F808931-D235-4FC7-90CD-F8A890C97B2F} /q

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>