Assemblies may be missing from the GAC or WinSxS cache after an MSI major upgrade
I recently read about an issue that affects Windows Installer products that use major upgrades and also install assemblies to the GAC or the WinSxS component store using the MsiAssembly and MsiAssemblyName tables. This issue is documented in the Microsoft Knowledge Base article at https://support.microsoft.com/kb/905238.
To summarize the issue, if you build an MSI that installs assemblies and includes major upgrade functionality, and then build a new version of the MSI that invokes a major upgrade, you may see the assemblies removed from the GAC or the WinSxS store after the major upgrade completes. This happens if you do not change the assembly version for your assemblies and have not scheduled the RemoveExistingProducts action to occur after the InstallFinalize action.
If you are running into this issue with your MSI, you can workaround this issue in one of the following 2 ways:
- Increment the assembly version number for each of your assemblies every time you deliver a new MSI that will be installed via a major upgrade
- Schedule the RemoveExistingProducts action to occur after the InstallFinalize action in your MSI
If you change the scheduling, you do not necessarily need to increment the assembly version number for each of your assemblies.
One important note here - the sample WiX files for the Q and Z sample applications that ship in the Windows Media Center SDK for Windows Vista are susceptible to this problem. I have updated the downloadable WiX v3.0 projects for the Q application (that can be downloaded from this location and that is described here) and the Z application (that can be downloaded from this location and that is described here) to address this. The specific change I made in q.wxs and z.wxs implements the 2nd option listed above by changing this line in each WXS file:
<RemoveExistingProducts After="InstallInitialize" />
To this:
<RemoveExistingProducts After="InstallFinalize" />
If you have the Media Center SDK installed and want to make this fix for the WiX v2.0 files that are installed as part of the SDK, you can make equivalent changes to the versions of q.wxs and z.wxs included there.
Comments
Anonymous
February 14, 2007
Having RemoveExistingProducts after InstallFinalize comes with its own set of issues. It's not in the scripted part of the install, so if that REP uninstall fails and rolls back then you're left with both old and new products on the system. When REP is in the scripted part a roll back in the upgraded install and in the remove of the older one restore the system to its original state. The after InstallFinalize behavior is documented as "If the removal of the old application fails, then the installer only rolls back the uninstallation of the old application." but perhaps the consequences of that (leaving both old and new on the system) aren't quite so obvious. Since a repair of the product will install the missing assemblies anyway, my vote for the least-dangerous course would be to keep REP in the safest place - just after InstallInitialize.Anonymous
February 14, 2007
Hi PhilDWilson - I'm not sure I agree that the least dangerous course is to leave RemoveExistingProducts before InstallInitialize. This can cause your product assemblies to be not installed correctly in major upgrade scenarios, and then your users have to figure out that they will need to run a repair to fix it. I understand the possible risk of the RemoveExistingProducts uninstall failing and leaving an extra copy of the product on the system, but that seems less invasive than leaving an install with missing assemblies. Overall, the cleanest solution is to update assembly versions for each major upgrade, but I know that is not always feasible for every software project for various reasons.Anonymous
February 15, 2007
It's a judgement call, yes, but I often ask the question "what's the worst that that can happen". In this case, imagine a help desk trying to solve the problem of having both old and new products on the system, conflicting with each other, services all mixed up, COM registration all mixed up etc because one product was supposed to replace the other, not co-exist with it. Now imagine a help desk call where the answer is "right-click the MSI file and choose repair", and that call may not have even been necessary in the first place if a shortcut invoked the repair. So we'll agree to disagree on this one ;=)Anonymous
February 15, 2007
Hi PhilDWilson - Your help desk call analogy is a useful question to ask in scenarios such as this, thank you for providing this example. I do want to point out, however, that leaving RemoveExistingProducts before InstallFinalize and doing a major upgrade of an MSI that includes assemblies with the same assembly version is guaranteed to result in missing assemblies. The other case (putting RemoveExistingProducts after InstallFinalize and having the old product uninstall fail) might fail, but is not guaranteed to cause problems like the other scenario. The help desk people may have to dig a bit deeper in this latter case, but I think the frequency of calls may be less with the recommendation in this blog post. That is a trade-off that each setup developer needs to weigh for their own product though, and my blog post should be taken only as a recommendation, not as the only option to approach this issue.Anonymous
May 31, 2007
I ran into an interesting scenario related to Windows Installer major upgrades recently and I wantedAnonymous
October 31, 2007
It's unclear from reading the KB article and your post if this issue would still apply if the File Version of the assembly was incremented and the FileVersion was set in the MsiAssemblyName table, or if it only applies when the assemblies are exactly the same and thus would not otherwise need be copied by the new package. Do you have any idea?Anonymous
October 31, 2007
Hi Jlevine - The FileVersion value is used when doing in-place upgrades of assemblies (for example, when applying a patch to an installed product) as described at http://msdn2.microsoft.com/en-us/library/Aa372360.aspx. However, my understanding is that the issue described in this blog and the KB article at http://support.microsoft.com/kb/905238 happens if the assembly identity is the same, which happens if the AssemblyVersion attribute is the same in both the old and the new version of your MSI. My understanding could be wrong though, so it may be worth trying a test major upgrade scenario where you only update the assembly's FileVersion to see how it behaves.Anonymous
December 16, 2008
When I create an MSI-based installer, one of the things I typically include in the setup authoring isAnonymous
July 11, 2012
The comment has been removedAnonymous
July 16, 2012
The comment has been removedAnonymous
July 17, 2012
Thanks Aaron. What about REINSTALLMODE= emus? Other than being inefficient, do you think there are other drawbacks here? Thanks, Kiran HegdeAnonymous
July 17, 2012
Hi Kiran Hegde - It is fine to set REINSTALLMODE = emus in your MSIs. The REINSTALLMODE flag that I recommend avoiding is "a" because it will cause an MSI to revert a file to an older version if a newer one exists on the computer, which is almost always a bad idea. I'm not sure if using REINSTALLMODE = emus will fix the issue you're seeing where assemblies are not in the GAC after a major upgrade though. You'll have to give it a try and see what happens.Anonymous
July 18, 2012
Thanks AaronAnonymous
August 30, 2012
Hello Aaron, I am reigniting this thread again w.r.t to the GAC issue which i had mentioned earlier, as i had 2 additional questions: i)Would sequencing RemoveExistingProducts before CostInitialize be a good solution? I have tried it and am observing that i am not seeing MigrateFeatureStates being invoked. RemoveExistingProducts gets invoked and runs fine. I see the following message: Skipping Migratefeaturestates action: not run in maintenance mode. Our installer has a single feature at the moment. So it is not an issue. This seems to have been recommended earlier on the web. Do you see any issues with this? ii)I have also figured out that if you can populate the FileVersion attribute for assemblies which go into the GAC in the MsiAssemblyName table, this problem would be resolved. Is there something i need to do to get the FileVersion attribute populated? Thanks in advance, Kiran HegdeAnonymous
August 31, 2012
Hi Kiran Hegde - According to the documentation at msdn.microsoft.com/.../aa371197(v=vs.85).aspx, it isn't permitted to schedule RemoveExistingProducts before InstallValidate, and CostInitialize is scheduled before InstallValidate, so your sequencing suggestion won't work. When RemoveExistingProducts runs, it will do an uninstall of the old product, and that will cause the entire uninstall log to be included within the verbose MSI log for your installation process. The item you saw about MigrateFeatureStates not running in maintenance mode might be from the uninstall process invoked by RemoveExistingProducts as opposed to from the install of the new version of your product. I'd suggest looking for other instances of the string MigrateFeatureStates in your log file to see if it appears elsewhere. If you're using WiX to build your MSI, you can use the suggestions listed at blogs.msdn.com/.../8847259.aspx to make the WiX build process populate the FileVersion attribute in your MsiAssemblyName table.