Downgrade File in MajorUpgrade

2019-05-23 13:54发布

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

1条回答
女痞
2楼-- · 2019-05-23 14:37

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.

查看更多
登录 后发表回答