2

EDIT Please see the small repro at the bottom.

I have the same issue as here: Windows installer deletes versioned file during product upgrade, instead of downgrading it

File Table |File                                            | Component_                                   | FileName    | FileSize | Version|
-----------|--------------------------------------------------------------------------------------------------------------------------------------------
old MSI    |fileEcMWtDjRdBXxvVHY.WvW_XXJI4GZcq5iAszC_F3KIwk | Cj9pc73bMjDSVVGUqS81_nPSltSFuUEweshtzct2AHi4 | bftlang.dll | 118784   | 2004.553.4453.1067
new MSI    |fileYXlC3cFPRwh6qrJ5u..Ll052XUiMylAmA6a4BwMlz_o | CZJsalkL4nX.r6JS5xXH3pjmr9mY1AO4CITmUEHjP82I | bftlang.dll | 118784   | 2004.553.4453.1064

Component Table |Component                                    | ComponentId                            | KeyPath
----------------|---------------------------------------------------------------------------------------------------------------------------------------
old MSI         |Cj9pc73bMjDSVVGUqS81_nPSltSFuUEweshtzct2AHi4 | {C45097D5-E359-48B5-9F85-AB5EC81D62BF} | filepcu3NI3UMnsXucCthGSqTSHMvUoyVuyQHRbEXnUVii0
new MSI         |CZJsalkL4nX.r6JS5xXH3pjmr9mY1AO4CITmUEHjP82I | {8B97BC16-7D4D-45CD-A3E3-903C60868202} | fileYXlC3cFPRwh6qrJ5u..Ll052XUiMylAmA6a4BwMlz_o


MSI (s) (88:A0) [20:12:50:115]: Disallowing installation of component: {8B97BC16-7D4D-45CD-A3E3-903C60868202} since the same component with higher versioned keyfile exists

(off topic: does Stackoverflow markdown support tables? They aren't mentioned in the help.)

For some reason, our incremental build system creates bogus version numbers which sometimes decrease. While I would agree if you tell me to fix that first, it is not under my control and the same issue would manifest itself if someone ever wanted to e.g. downgrade a nuget package.

The workaround mentioned in the linked question (scheduling it before Costing) solves the original problem, but it seems to cause issues with Burn and upgrading.

Another workaround mentioned was changing REINSTALLMODE, but I am using Burn, which afaik doesn't allow me to change it. (We have no shared components, so if it were possible to change the REINSTALLMODE, that would probably be the best solution.)

After scheduling it before Costing, I have a few times observed the issue that the bootstrapper was registered in ARP, but the MSI wasn't installed. (I think this was caused by a cancellation of the upgrade which caused a rollback of the new installation but no reinstallation of the old version - but I am not sure about that).

We generate component GUIDs by calling Guid.NewGuid(), so the component rules are violated, so I can't schedule it after InstallFinalize, but a comment mentions that MSI then keeps the old (higher versioned) version, which is definitely not what I want.

Basically, I have a directory layout in my MSI, and want to copy that 1:1 into the selected directory, ignoring and overwriting any existing files regardless of their version. To solve similar problems with non-versioned files, I use this pattern for each file:

<Component Directory="APPLICATIONFOLDER" Permanent="no" Guid="##Guid.NewGuid()##" Id="##some random id##">
    <File Id="##some different random id##" Source="#source#" />
    <RemoveFile Id='##some other random id##' On='install' Name='#name#'/>
</Component>

If possible, I would like to schedule RemoveExistingProducts after InstallInitialize, that way the uninstallation is inside the transaction.

Is there a clean way to downgrade a file during a major upgrade? Even though the workaround of scheduling it before costing mostly works, I still had to suppress ICE27, which complained about it.

Edit:
I searched a bit more and found this question which mentions modifying the file table after compilation. I guess that might be a viable option for me, but can it really be that hard to work around an msi bug? (I consider a major upgrade removing a file and not replacing it a bug.)

EDIT2:

I have created a small repro, it contains 6 MSI files:

  • in with_different_guids, the DLL is downgraded, but both versions have different IDs and different GUIDs.
  • in with_same_guids, the DLL is also downgraded, but both versions have the same (autogenerated) ID and GUID.

Both show the same behaviour:

If I install 1\SetupProject.msi, the directory contains three files, 1 .txt, 1 .dll and 1.exe.
If I then run 2\SetupProject.msi, only the .txt and .exe get reinstalled (the version of the .exe is unchanged).

In rep_before_costinitialize, the REP is scheduled before CostInitialize, and the MajorUpgrade works, all three files are on disk.

Edit3:
I was also able to reproduce my Burn problem:
If I schedule REP before CostInitialize, the MajorUpgrade works as expected: the DLL is downgraded.
BUT the transactional logic of the Burn Upgrade no longer works as expected.
If I install setupExe\1\BootstrapperProject.exe, then start setupExe\2\BootstrapperProject.exe, but cancel it in the middle, neither MSI is installed, but the Entry for 1 is still shown in ARP.

Repro: https://onedrive.live.com/redir?resid=5062efe7e0c8eccc!124918&authkey=!ANYtHc5SkzFXn5U&ithint=file%2czip

Community
  • 1
  • 1
Lukas Rieger
  • 676
  • 10
  • 31

1 Answers1

1

FWIW, I've talked to MS support in the past about having REP before costing to make upgrades work successfully, and at that time they said it was ok, while pointing out that it's also before MigrateExistingFeatures so if you migrate features during upgrades there'll be an issue.

I wouldn't alter the File table. This guarantees that the version on disk does not match the version in the File table, and makes the file a candidate for repair. If the version on disk is 2.0 and the version in the File table is 3.0 then a repair will see that the file is broken. Some major upgrades or patches will notice the difference and demand that you supply the source MSI to restore the file (because of the version mismatch) before deciding if the file needs to be patched or upgraded. Windows cannot know whether a file needs updating by an incoming update if the version on disk doesn't match the File table.

In any case, for an individual file it's much safer and easier to just open the darn file with Visual Studio and change the real version!

There are also occasional bugs like this https://support.microsoft.com/en-us/kb/905238 which cloud the issue.

Do you generate that File table key with your code? I'm suspicious of the fact that your File table key is different in the different MSIs. It is probably not the issue, but changing File table keys has been known to cause issues in other update scenarios. If the upgrade attempts to find the previous version of the file by using the primary key to the file table (which relational databases do) and fails to find it in the older MSI then the results might be unpredictable.

PhilDW
  • 20,260
  • 1
  • 18
  • 28
  • I use wix to generate the msi and (currently) don't do any post-processing. The MSI alone works after scheduling it beforeCosting, but it looks like it causes issues with my bootstrapper, burn. The IDs are autogenerated each time. I originally thought that wouldn't be an issue because a major upgrade with REP after installinitialize is supposed to completely uninstall the old version and then reinstall the new one. – Lukas Rieger May 21 '15 at 18:20
  • True, but the "disallowing installation" stuff is done in CostFinalize, so it decides it's not going to install it early on. It's not clear to me why you even get that message without seeing both MSIs. If all the Component guids in the old MSI are different to the guids in the new MSI, then how come Windows Installer says you already have that guid installled? That makes no sense to me. I think I've seen this question before, and my response was probably the same - you shouldn't even be getting the same guid in both MSI files! – PhilDW May 22 '15 at 17:31
  • afaik, it is not about the component guid but about the component KEY FILE. The MSIs are a few hundred MB each, I will see if I can maybe create smaller ones to repro the issue – Lukas Rieger May 22 '15 at 22:51
  • No, it's about the guids. If the same guid is in both old and new MSI files then that's the problem,and potentially the NewGuid() code is not actually creating completely uniques guids if there are duplicates in both old and new files. – PhilDW May 23 '15 at 18:24
  • I added a small repro, would you mind taking a look? You seem to know a bit about msi... I have the same behaviour, regardless of whether the Component GUIDs are the same or different. Thank you. – Lukas Rieger Jun 08 '15 at 14:21
  • I'd need two MSIs, one the base and the other the upgrade that shows the issue. I can't tell from those project folders which those two MSIs would be. – PhilDW Jun 08 '15 at 19:17
  • The two MSIs are in __msi\with_different_guids\1__ and __msi\with_different_guids\2__. – Lukas Rieger Jun 09 '15 at 13:14