Edit: Quoting myself because I summarized the issue much better in one of the comments below…
I have a condition that is true when the package is installed, but
not true when it is removed. I expected MSI to remember that it had
installed the conditional component and remove it with the uninstall,
but this is not the case. I am trying to find out A) the proper way to
clean up this orphaned component, and B) the best way to protect
against this problem in the future.
I guess my question boils down to, is it safe to just delete an orphaned feature/component after a product is uninstalled? And is there any way to check what, if anything, is still referencing a component that I believe to be an orphan? And how do I fix my installer to keep this from happening in the future?
We have a wix project to install a library, Foo. This installer puts copies of Foo.dll into the GAC, and a folder, Program Files\Reference Assemblies\Foo\<version> by default. The installer also adds two registry keys, one is a custom key which stores the path of the Foo folder for reuse in future installs, the other tells Visual Studio to include the full <version> folder path in its search for installed libraries so that Foo shows up in the “Add References” dialog. Multiple versions of the Foo library can be installed on the machine at a time, each will be located in the appropriate <version> folder under Foo.
Foo 2.0.0 had a bug that slipped through testing, Foo 2.0.1 contained the bug fix, no other changes. It was decided that since the bug fix was the only change, we would add a policy file to the GAC which would redirected references for Foo 2.0.0 to Foo 2.0.1. This policy file was added to the installer as a new component inside of a new feature. An upgrade tag was added to detect and remove Foo 2.0.0 when Foo 2.0.1 was installed. The installation of the policy feature was made conditional on Foo 2.0.0 being detected. Everything seemed to be working and Foo 2.0.1 was pushed out.
Now, a year later, we discover that we again missed noticing a bug, this time in the installer setup rather than the library code. It turns out that when Foo 2.0.1 replaces 2.0.0, and is then uninstalled, the policy file is orphaned and remains in the GAC while all other files and keys are removed. I have tested this on a clean install of windows (virtual machines can be so useful) and confirmed that the problem can be replicated, i.e. no additional references to the component have snuck in to cause it to stay behind.
All of this was originally done in WiX 3.0 but we have recently moved up to using WiX 3.5. Our WiX code looks like this:
<Product Id="Guid 1" Name="Foo v2.0.1" Language="1033" Version="2.0.1" Manufacturer="My Team" UpgradeCode="Guid 2">
<Package InstallerVersion="300" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Upgrade Id="Guid 2">
<UpgradeVersion Minimum="2.0.0" Maximum="2.0.0" IncludeMaximum="yes" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADE2X0X0"></UpgradeVersion>
</Upgrade>
<Property Id="FOODIR">
<RegistrySearch Id="FooPath" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="RefAssemb" Name="Reference Assemblies">
<Directory Id="FOODIR" Name="Foo">
<Component Id="FooLibPath" Guid="Guid 3">
<RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
<RegistryValue Name="InstallPath" Type="string" Value="[FOODIR]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOVERSION" Name="v2.0.1">
<Component Id="Foo_VSFile" Guid="Guid 4">
<File Id="Foo_DLL" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes"></File>
</Component>
<Component Id="Foo_VSRegKey" Guid="Guid 5">
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v3.5\AssemblyFoldersEx\Foo v2.0.1" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="[FOOVERSION]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOGAC" Name="GAC">
<Component Id="Foo_GAC" Guid="Guid 6">
<File Id="Foo" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes" Assembly=".net"></File>
</Component>
<Component Id="Foo_Policy_2x0x1" Guid="Guid 7">
<File Id="Foo_PolicyDLL" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.dll" KeyPath="yes" Assembly=".net"></File>
<File Id="Foo_PolicyConfig" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.config" CompanionFile="Foo_PolicyDLL"></File>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFoo" Level="1">
<ComponentRef Id="Foo_GAC"/>
<Feature Id="Foo_VSSupport" Level="1">
<ComponentRef Id="FooLibPath"/>
<ComponentRef Id="Foo_VSFile"/>
<ComponentRef Id="Foo_VSRegKey"/>
</Feature>
<Feature Id="Foo_Policy_v2x0x1" Level="0">
<ComponentRef Id="Foo_Policy_2x0x1"/>
<Condition Level="1">UPGRADE2X0X0</Condition>
</Feature>
</Feature>
</Product>
is it safe to just delete an orphaned feature/component after a
product is uninstalled?
No, it's not. If you just delete it, its component registration information is still left on the machine.
And is there any way to check what, if anything, is still referencing
a component that I believe to be an orphan?
Not really. But if there is something referencing one of your components, it's most likely another product developed by you or an older version of your current product which wasn't uninstalled correctly.
It's very unlikely that a random product would reference your component or assembly.
And how do I fix my installer to keep this from happening in the
future?
Use major upgrades which uninstall the old component and install the new one. No special policy files, no conditional installations or removals.
Multiple versions of the Foo library can be installed on the machine
at a time, each will be located in the appropriate folder
under Foo.
Why? If you have a single product, you can use major upgrades. This way the user will have only one version installed with only one version of your assembly.
Versioned assemblies installed side by side make sense only for different products.
It was decided that since the bug fix was the only change, we would
add a policy file to the GAC which would redirected references for Foo
2.0.0 to Foo 2.0.1. This policy file was added to the installer as a
new component inside of a new feature.
This is a hack and most likely this is what is causing the problem. Your new installed should have uninstalled the old version along with Foo 2.0.0.
Major upgrades should always be standalone.
Related
I have written and am maintaining a number of Wix installation source files which I use to build MSI files for distribution of my application.
I have not explicitly programmed for any kind of upgrading, updating, reinstallation or anything of the kind -- there is a single feature that consists of a number of components with stable GUIDs and I have observed that at least a clean installation does what I expect it to.
However, I (and anyone in possession of the MSI files I distribute) may seemingly install distinct versions of my application side-by-side using their respective (distinct) MSI files. Which isn't a problem in itself, except that I obviously use the same folder as installation target -- "%ProgramFiles(x86)%\Foobar" -- to install the application (version regardless) in. Meaning that in effect there is always ever one version that ends up being installed.
I would argue that Windows Installer behaves correctly in so far that it updates files from whichever MSI package it installed last. One interesting side-effect of this is that if the last MSI was of earlier version, the files in the application folders would be overwritten with the copies from that earlier version.
But none of that seems to be the actual problem to me. I want to fix the disparity between what is actually installed (a single application version) and what Windows tracks as installed -- in my case two records of two distinct application versions.
Since I install the application in a folder that doesn't depend on the version being installed, tracking multiple application versions by Windows is a mistake.
So I guess my question is, how do I fix this so that only one version is shown (reflecting reality) or what's the idiomatic approach in these kind of cases? I deliberately did not overspecify my Wix source code, hoping in return that Windows Installer would use some built-in intelligence to figure everything out on its own. But I may need to add some explicit upgrade or uninstall-previous-version-first instructions, I suppose.
My minified Wix source code (file "foobar.wxs") would look like this:
<?xml version="1.0" encoding="utf-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Name="Foobar" Manufacturer="ACME Inc." Id="*" UpgradeCode="ae9a7d6d-6c2d-446a-97d9-9dbe829d2ea8" Language="1033" Codepage="1252" Version="!(wix.PRODUCT_VERSION)">
<Package Id="*" Languages="1033" SummaryCodepage="1252" Compressed="yes" InstallerVersion="200" />
<Icon Id="foobar" SourceFile="!(wix.APPPATH)/foobar.ico" />
<Property Id="ARPPRODUCTICON" Value="foobar" />
<Property Id="ARPCOMMENTS" Value="Gives you full foobar powers" />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" />
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="Foobar" FileSource="!(wix.APPPATH)">
<Component>
<File Id="foobar.exe" Name="foobar.exe" />
</Component>
<!-- There are other components like above (assets) -->
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="foobar_menu" Name="Foobar">
<Component Id="foobar_shortcut" Guid="e80a6b95-a145-453a-b327-65a977e741fe">
<Shortcut Icon="foobar" Id="foobar_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<Shortcut Directory="DesktopFolder" Icon="foobar" Id="foobar_desktop_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<RegistryValue KeyPath="yes" Root="HKMU" Key="Software\[Manufacturer]\[ProductName]" Type="string" Value="" />
<RemoveFolder Id="remove_foobar_menu" On="uninstall" />
</Component>
</Directory>
</Directory>
<Directory Id="CommonAppDataFolder">
<Directory Id="app_data_foobar" Name="foobar">
<Component Guid="" Id="app_data_config_folder">
<CreateFolder />
</Component>
<Component Guid="" Id="app_data_config_folder_log_file">
<File Name="foobar.log" Source="foobar.log.template">
<!-- Add write access permission to the log file to members of "Users" group. -->
<!-- PermissionEx Sddl="D:AR(A;;GWGR;;;BU)" / -->
<!-- Bug with Windows Installer, can't use PermissionEx/MsiLockPermissionsEx table. See https://stackoverflow.com/questions/55145282/how-to-include-inherited-permissions-when-specifying-permissions-for-a-file-inst -->
<util:PermissionEx Append="yes" GenericWrite="yes" User="Users" />
</File>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="foobar">
<ComponentGroupRef Id="foobar" />
<ComponentRef Id="foobar_shortcut" />
<ComponentRef Id="app_data_config_folder" />
<ComponentRef Id="app_data_config_folder_log_file" />
</Feature>
</Product>
</Wix>
I am compiling the object file with the following Windows Command Prompt line:
candle.exe -ext WixUtilExtension -out %TEMP% foobar.wxs
And then generating the MSI file with:
light.exe -ext WixUtilExtension -spdb "-dAPPPATH=%apppath%" "-dPRODUCT_VERSION=%version%" -out %TEMP%\foobar-%version%.msi %TEMP%\foobar.wixobj
(using Wix 3.11.1.2318)
Upgrade Code: As long as you have set an upgrade code (which identifies a bunch of related products) you can use a major upgrade element to indicate products that are to be uninstalled as part of a new MSI's installation.
MajorUpgrade Element: Just inject a MajorUpgrade element for default treatment of major upgrades into your existing WiX source. It is a sort of "magic element" doing a lot for you making a number of (usually good) assumptions. There are older and more flexible ways to do it - if you need more detailed control (for legacy purposes usually - auto-magic does not cover all bases):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
The above is the standard use for all WiX files created in Visual Studio.
Note: I will try to tune up this answer shortly with more links, but give that a go first?
First link: Using Visual Studio to make WiX files. The Hello WiX and Visual Studio-type of scenario.
Major Upgrade Suggested Reading: A few things to know about major upgrades. All WiX markup essentially revolves around the compiled MSI's Upgrade table. It is there that major upgrade logic is configured. Custom actions could also affect things, and a few other things such as launch conditions perhaps.
WiX Documentation: How To: Implement a Major Upgrade In Your Installer
Major Upgrade - Common Problems: WIX does not uninstall older version
Major Upgrade - Manual Configuration: Adding entries to MSI UpgradeTable to remove related products (using old-style Upgrade elements)
Further:
Major Upgrades - How-To & Concept: Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs
I've inherited project with MSI created in WiX and now I'm trying to solve some of the issues that unfortunately exist.
There's a remember property pattern which is used to found specific directory saved in registry entry:
<Property Id="AUTO_FOUND_DIR" Secure="yes" Admin="yes">
<RegistrySearch Id="regsrch_AUTO_FOUND_DIR"
Root="HKCU"
Key="$(var.RegPath)"
Name="$(var.SpecificKey)"
Type="raw"
/>
</Property>
The SpecificKey value is then saved in AUTO_FOUND_DIR property.
Then the black magic appears. A separate component is holding (among other stuff) a shortcut located in ProgramMenuFolder (non-advertised) to the main executable.
I've been told that usage of util:RemoveFolderEx is a workaround for an old issue where this shortcut was orphaned and hasn't been removed during uninstall:
<Feature>
<DirectoryRef Id="ProgramMenuDir">
<Component Id="cmp_ProgramMenuDir" Guid="{0E8BD13A-GUID-IS-HERE-6E5092ECA9EF}">
<CreateFolder />
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryKey Id='reg_SpecificKeyID' Root='HKCU' Key='$(var.RegPath)' ForceCreateOnInstall="yes">
<RegistryValue Type='string' Name='$(var.SpecificKey)' Value='[ProgramMenuDir]'/>
</RegistryKey>
<!-- other content: shortcut to ProgramMenuFolder and other stuff -->
<util:RemoveFolderEx Id="rm_dirID" On="install" Property="AUTO_FOUND_DIR"/>
</Component>
</DirectoryRef>
</Feature>
The problem is: I don't need this workaround (and usage of AUTO_FOUND_DIR property as well. I've removed that code but during upgrade (major, Product and Package GUIDs set to "*", UpgradeCode has the same value as previous version) I can see in verbose log from MSI that this AUTO_FOUND_DIR exists, the RegistrySearch reads the key value with specific directory and as a result the util:RemoveFolderEx removes that directory and all components that are there located.
My question is: how can I detect why this old property is being used during upgrade and how to get rid of it?
Additional information: the install scope is PerMachine, ALLUSERS is set to 1. The MSI with upgraded version has this property removed.
Without a close look at your complete verbose log to see what's going on, remember that an upgrade does an uninstall of the older installed product. This means that a lot of the logic in the older installed product will happen during your upgrade. So you will definitely see the RegistrySearch running as the older product uninstalls, setting AUTO_FOUND_DIR, and you will see the RemoveFolder that runs during the uninstall.
So it's not clear if you actually have an issue if all you're seeing is the uninstall activity of that older product being uninstalled. That activity is embedded in the installed product.
My application installs the DesKey Dk2 dongle drivers if they are not installed or are lower than a perticular version. One of the user is complaining that whenever he runs the application from start menu shortcut, DK2 installation starts and then application is launched. Nobody else is facing this issue.
I am indtalling Dk2 as custom action in Wix script as below. lets say DK2 is represented as ABC:
<DirectoryRef Id="TARGETDIR">
<Directory Id="ABCRedistDirectory" Name="ABCDrivers">
<Component Id="ABCRedist" Guid="*">
<File Id="ABC_EXE" Source="$(var.TargetDir)ABC.exe" KeyPath="yes" Checksum="yes"/>
</Component>
</Directory>
</DirectoryRef>
<Property Id="DK2_VERSION">
<RegistrySearch Id="Dk2_Version"
Root="HKLM"
Key="SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\DESkey DK2 Uninstall"
Name="DisplayVersion"
Type="raw" />
</Property>
<Feature Id="ABCRedist" Title="ABC drivers" AllowAdvertise="no" Display="hidden" Level="1">
<ComponentRef Id="ABCRedist"/>
</Feature>
<CustomAction Id="InstallDK2Drivers" FileKey="ABC_EXE" ExeCommand="" Execute="deferred" Impersonate="no" Return="check"/>
<InstallExecuteSequence>
<Custom Action="InstallDK2Drivers" Before="InstallFinalize">
<![CDATA[NOT DK2_VERSION OR DK2_VERSION < "7.34.0.57"]]>
</Custom>
</InstallExecuteSequence>
The warning in Windows Event viewer points to main executable, which is below:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="FolderName">
<Directory Id ="MyFolder" Name="MyApp">
<Component Id ="MyApp.exe" Guid="*">
<File Id="MyApp.exe" Source ="$(var.TargetDir)MyApp.exe" KeyPath="yes" Checksum ="yes" />
<Shortcut Id="MyAppStartMenuShortcut" Name="My App" Directory="ProgramMenuDir" Icon="MyAppIcon.exe" WorkingDirectory="MyFolder" Advertise="yes"></Shortcut>
<Shortcut Id="MyAppDesktopShortcut" Name="My App" Directory="DesktopFolder" Icon="MyAppIcon.exe" WorkingDirectory="MyFolder" Advertise="yes"></Shortcut>
</Component>
...
Now it is not happening on other machines so I am not able to diagnose. Can anybody point out any obvious mistake? What can I do to diagnose this on customer's machine i.e. how to get logs when installing .exe, .dll, some third party installers like VC100 CRT and VC100 MFC and .Net 4.0 bootstrapper?
Thanks in advance.
I can't tell from your post if the 3rd party ABC product that you're using is the same one that is repairing, or maybe the client doesn't know and you're assuming that it is your ABC thing. There's no indication in that WiX fragment exactly how you're installing the ABC thing, all you show is that it's copied to disk, there's no clue how you are running it to get it installed, and no shortcuts either.
What's happening in general seems to be that the other product is going into repair mode. There should be MsiInstaller entries in the Application Event Log that say something about whatever is wrong, referencing component ids, products, and maybe file names or registry entries.
Your setup may have a conflict with that other install. It's unlikely to be anything to do with your shortcut except that your shortcut is advertised, so it goes off into a component feature check, and is apparently finding that you are sharing something with that other app, and now it needs repairing. If the 3rd party app that repairs is not your ABC thing then you won't be able to reproduce the issue unless you also install that 3rd party thing and find out what you're sharing with it, perhaps in the wrong way.
Is this some sort of bit-torrent component? I see an abc project on sourceforge.net with Python dll's, some visual C++ runtimes included as well as other stuff. This is by no means a properly constructed package, and it could trigger the self-repair problems seen on that particular computer.
To debug this would require to see what entries exist in the Windows Event log - it will specify what MSI component triggered the repair. The error could still be in other packages since a COM call from the embedded components in this abc package, could trigger a repair in any MSI package installed on the system. As could taking over a file association associated with torrent files.
Right click My Computer
Select Manage
Event Log -> Windows Logs -> Programs
In this log you will find entries starting with "Description: Detection of product..." with id 1001 or 1004. Here is a sample:
Event ID: 1001
Description: Detection of product "{4ED0C75A-8BC5-4520-B9C7-76968FD5677F}", feature "Test" failed during request for component "{A7B09747-E527-4E1B-AE51-323CD636210F}"
This information is enough to determine what package triggered the self-repair. Please provide this information and we can take it from there.
I extracted the above sample information from Stefan Krüger's MSI FAG at installsite.org.
You are using advertised shortcuts. In your Shortcut elements, set Advertise="no" or remove the Advertise attribute completely.
For more information, see this SO answer and msdn.
I have a similar problem like forki23 by bring Wix to not overwrite a configuration file during upgrade. I have a config file that should not be overwritten during upgrade, but it should be removed during uninstall. However every solution I find, breaks something else.
If I set NoOverwrite=yes and move the RemoveExistingProducts to InstallFinalize the config file is handled as I wished. However, in this case the shortcut is removed during upgrade for some reason. If I leave RemoveExistingProducts at InstallInitialize, the config file is actually removed during upgrade, however the shortcuts are present.
Why is this happening and is there are way to fix it?
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
<!-- InstallInitialize causes config-file to disappear during upgrade -->
<!-- InstallFinalize causes shortcuts to disappear during upgrade -->
...
<Property Id="DISABLEADVTSHORTCUTS" Value="1" />
...
<Directory Id="INSTALLLOCATION" Name="MyApp">
<Component Id="MYAPP.EXE" DiskId="1" Guid="...">
<File Id="MYAPP.EXE" Name="MyApp.exe" Source="..." Vital="yes" KeyPath="yes">
<Shortcut Id="startmenuShortcut"
Directory="ProgramMenuDir"
Name="!(loc.ProductName)"
WorkingDirectory='INSTALLLOCATION'
Icon="Icon.ico"
IconIndex="0"
Advertise="yes" />
</File>
<RegistryValue Root="HKLM"
Name="InstallLocation"
Key="$(var.InstallLocationRegistryKey)"
Type="string"
Value="[INSTALLLOCATION]">
</RegistryValue>
</Component>
<Component Id="MYAPP.EXE.CONFIG" DiskId="1" Guid="..." NeverOverwrite="yes">
<File Id="MYAPP.EXE.CONFIG"
Name="MyApp.exe.config"
Source="..."
KeyPath="yes" />
</Component>
...
</Directory>
...
<Directory Id="ProgramMenuFolder">
<Directory Id="ProgramMenuDir" Name="!(loc.ProductPrefix)">
<Component Id="ProgramMenuDir" Guid="...">
<RegistryValue Root="HKCU" Key="SOFTWARE\MyApp"
Type="string" Value="[INSTALLLOCATION]" KeyPath="yes" />
<RemoveFolder Id="ProgramMenuDir" On="uninstall"/>
</Component>
</Directory>
</Directory>
Note A: The config-file is a machine-wide configuration and should apply for all users.
Note B: I'm using WiX 3.7 and the target plattform is Windows 7 and 8.
Theoretically "NoOverwrite=yes and move the RemoveExistingProducts to InstallFinalize" should work, but it s clear we are losing something from the big picture. The best method to see why Windows Installer removes the shortcuts is to create a verbose log when launching the upgrade setup. You can do that in a cmd.exe with this command: msiexec /i [msi path] /L*V debug.log
The post the a download link for the log and the GUIDs of the components hosting the shortcuts so we can see if the log helps us understand what happens.
Windows installer works very exact in those things, and if anything gets removed in the After="InstallFinalize" case, this means, that a component has been removed, MSI has thought is not needed, because not contained in your new version of the msi file. Be very sure the GUID of the component containing MYAPP.exe and the shortcut has not changed in your new version. (Compare with a tool like Orce or Insted). It seems it has!
MSI removes only full components, not only shortcuts. Really! Maybe you have an update problem of shortcuts in Windows. Sometimes such things happen. Try to reboot to be sure, that this happens, what you think. Maybe there is an error in your test procedure (or it's the above mentioned GUID problem). There are not many other possibilities, if you have not custom actions which remove shortcuts or you try to add shortcuts as files or such dangerous stuff.
Putting a shortcut in the same component as the .exe is common, but not optimal in my eyes! I recommend to separate resources as much as possible, so put it in an own component. This has advantages, if you later want to rename the shortcut. Then you can just change the GUID of this component and the important .exe file is not touched.
There is a small disadvantage of that, loosing the direct connection to the file version of MYAPP.exe in overinstall scenarios, so if MYAPP.exe is a shared file between several different setups, this is not recommended. Perfect solutions for this are possible, but are not in the focus here.
Workaround: If you are still able to change the old (first) msi setup, just mark the component MYAPP.EXE.CONFIG as permanent. Then it will not be uninstalled during Major Upgrade (but not uninstalled at all, what has advantages and disadvantages, in other words, it is mostly acceptable for .config files).
If version 1 of your setup is already shipped, then you could do the same with some tricks too.
The following steps had been performed:
Setup1.msi had been built in VS2005 + WiX 3.0 (.NET Framework 2.0).
Version 1.0.0.0 had been installed by Setup1.msi.
For the purpose of minor upgrade a Setup2.msi had been built
(Setup2.msi differs from Setup1.msi ONLY in ProductVersion="1.0.1.0")
The following Patch.wxs had been prepared:
<Patch
AllowRemoval="no"
Classification="Update"
Comments="..."
Description="..."
DisplayName="..."
Manufacturer="..."
TargetProductName="...">
<Media Id="1000" Cabinet="MyPatch.cab">
<PatchBaseline Id="MyPatch" />
</Media>
<PatchFamily
Id="MyPatchFamily"
Version="1.0.1.0"
ProductCode="...THE SAME AS IN Setup1.msi..."
Supersede="yes">
<ComponentRef Id="CMP_Program_EXE" />
<ComponentRef Id="CMP_Desktop_Shortcut" />
<ComponentRef Id="CMP_ProgramMenu_Shortcut" />
</PatchFamily>
</Patch>
Patch.msp had been created with help of candle, light, torch and pyro.exe.
The following command had been invoked:
msiexec /p Patch.msp REINSTALL=ALL REINSTALLMODE=vomus
As a result, Program.exe was updated and new shortcuts "v. 1.0.1"
were created.
However, old Shortcut "v. 1.0.0" remained both on "DesktopFolder" and on
"ProgramMenuFolder".
How can I make the Patch remove old Shortcut?
Thanks in advance.
The simplest way is not to add version to shortcut name. See Windows UX Guidelines:
Avoid putting a version number in a program name unless that is how users normally refer to your program.
Otherwise your minor upgrade has to remove the shortcut to the old version and create a new shortcut that points to the new version.
During minor upgrade, the old version does not get uninstalled, that's why the shortcut is not updated.
You can use the RemoveFile element within the Component one owning the Shortcut:
<DirectoryRef Id="DesktopFolder">
<Component Id="..." Guid="...">
<Shortcut Id="..." Name="foobar_1.0.1" ... />
<RemoveFile Id="..." Name="foobar_1.0.0.lnk" On="install" />
...
</Component>
</DirectoryRef>
But. There's one problem remained -- when you uninstall you MSP patch, new shortcut (foobar_1.0.1.lnk) is not removed (because the Shortcut table is transformed back, I believe). Therefore, user ends up with two shortcuts. I do not know how to fix that, therefore I asked here.