Instance of Troubleshooting Patch Applicability

Context:

While troubleshooting Windows Installer installs can be complex, troubleshooting patch installs adds a couple more layers of complexity (patch transforms and applicability rules). Given the lack of patch troubleshooting documentation (at least that I've seen) I figured I'd take a case where Carolyn has provided detailed analysis for a customer inquiry from Product Support.

Desired Behavior:

Patch 1 targets the Base package and patch 2 targets both the Base and Patch 1

Actual Behavior:

Applying a patch 2 after patch 1 results in the error “The upgrade patch cannot be installed by the Windows Installer service because the program to be upgraded may be missing, or the upgrade patch may update a different version of the program. Verify that the program to be upgraded exists on your computer and that you have the correct upgrade patch.”

Analysis

You can easily analyze a MSP and the usual place to start looking when a patch isn't applicable is the applicability criteria for the MSP. For a patch, there's two layers.

Layer 1

PID_TEMPLATE summary property of the MSP itself. This is the list of target ProductCodes for the patch. You can bypass this layer using PATCH={path to patch} on the command line and invoking a /i type installation operation. MsiApplyPatch and/or "/p patch.msp" involve this layer.

Layer 2

PID_REVNUMBER and PID_CHARCOUNT summary properties of the MSTs embedded within the MSP. The revision number gives the ProductCode-ProductVersion/ProductCode-ProductVersion/UpgradeCode set between the target and update images. The charcount identifies the validation flags.

You can use dfview to look at the substorages within a MSP to find out how many targets it has. That, or just use msiinfo on the MSP since that gives you the list of embedded transforms. Each case here has a single set of embedded transforms. 

 C:\scratch\rfc>msiinfo patch02.msp
ClassId = {000C1086-0000-0000-C000-000000000046}
[ 5][/k] Keywords = PatchSourceList
[ 7][/p] Template(MSI CPU,LangIDs) = {F7877CD1-F33B-4DCD-8B11-A72BB64F22E6}
[ 8][/l] SavedBy =  :QPPrevVersionToQPNewVersion;:#QPPrevVersionToQPNewVersion
[ 9][/v] Revision = {68C7ED9B-4938-492B-B88B-B463E5000817}
[15][/w] Words(MSI Source Type) = 3
 
C:\scratch\rfc>msiinfo patch01.msp
ClassId = {000C1086-0000-0000-C000-000000000046}
[ 5][/k] Keywords = PatchSourceList
[ 7][/p] Template(MSI CPU,LangIDs) = {F7877CD1-F33B-4DCD-8B11-A72BB64F22E6}
[ 8][/l] SavedBy =  :QPPrevVersionToQPNewVersion;:#QPPrevVersionToQPNewVersion
[ 9][/v] Revision = {5E64DBA1-44E9-4A04-AF2C-5CCCD109C95D}
[15][/w] Words(MSI Source Type) = 3

So, patch02 is certainly not a multi-target patch.

Now to get to the 2nd layer, you have to go back to the transforms in the temp folder of the machine that generated the patch (assuming the DontRemoveTempFolderWhenFinished property was set in the Property Table of the PCP file). Oh, and one other thing to add. None of these patches have the MsiPatchSequence table so they follow 2.0 patch application rules which means chronological patch application (order in which patches are applied on the machine). Here's the requisite data from the transforms:

 
C:\scratch\rfc>msiinfo patch01.mst
ClassId = {000C1082-0000-0000-C000-000000000046}
[ 1][/c] Codepage = 1252
[ 2][/t] Title = Customers Product
[ 3][/j] Subject = Customers Product
[ 4][/a] Author = Customers Company
[ 5][/k] Keywords = Installer,MSI,Database
[ 6][/o] Comments = Contact:  Your local administrator
[ 7][/p] Template(MSI CPU,LangIDs) = Intel;1033
[ 8][/l] SavedBy = Intel;1033
[ 9][/v] Revision = {F7877CD1-F33B-4DCD-8B11-A72BB64F22E6}10.4.27.01;{F7877CD1-F
33B-4DCD-8B11-A72BB64F22E6}10.5.28.03;{D9929871-61DC-4461-AD49-29212301A721}
[12][/r] Created = 2005/03/11 09:46:38
[14][/g] Pages(MSI Version Used) = 200
[16][/h] Characters(MSI Transform) = 144834583
[18][/n] Application = InstallShield« Developer 8.0
[19][/u] Security = 1
 
C:\scratch\rfc>msiinfo patch02.mst
ClassId = {000C1082-0000-0000-C000-000000000046}
[ 1][/c] Codepage = 1252
[ 2][/t] Title = Customers Product
[ 3][/j] Subject = Customers Product
[ 4][/a] Author = Customers Company
[ 5][/k] Keywords = Installer,MSI,Database
[ 6][/o] Comments = Contact:  Your local administrator
[ 7][/p] Template(MSI CPU,LangIDs) = Intel;1033
[ 8][/l] SavedBy = Intel;1033
[ 9][/v] Revision = {F7877CD1-F33B-4DCD-8B11-A72BB64F22E6}10.4.27.01;{F7877CD1-F
33B-4DCD-8B11-A72BB64F22E6}10.5.28.04;{D9929871-61DC-4461-AD49-29212301A721}
[12][/r] Created = 2005/03/11 09:46:38
[14][/g] Pages(MSI Version Used) = 200
[16][/h] Characters(MSI Transform) = 144834583
[18][/n] Application = InstallShield« Developer 8.0
[19][/u] Security = 1

The char count is a combination of validation and error suppression. As the Character Count MSDN Topic says the upper word is validation and the lower word is error condition flags. For both the above transforms, Char Count is 144834583. If you convert 144834583 to hex, you get 0x008A20017 thus the upper word is 8A2 and the lower word is 17. This further means 8A2 is the validation condition. 017 is the error condition.

Now let's break these down using the MSDN Topic MsiCreateTransformSummaryInfo.

  • Validation Translation:

    Note that 0x8A2 is not the default patchwiz value. Our recommendation is 0x922 per the MSDN Topic TargetImages Table (PatchWiz.DLL).

    Value Flags
    0x8A2 MSITRANSFORM_VALIDATE_UPGRADECODE (0x00000800) +MSITRANSFORM_VALIDATE_NEWLESSEQUALBASEVERSION (0x00000080) + MSITRANSFORM_VALIDATE_UPDATEVERSION (0x00000020) + MSITRANSFORM_VALIDATE_PRODUCT (0x00000002)
  • Error Condition Translation:

    Value Flags
    0x17 MSITRANSFORM_ERROR_UPDATEMISSINGROW (0x00000010) +MSITRANSFORM_ERROR_ADDEXISTINGTABLE (0x00000004) + MSITRANSFORM_ERROR_DELMISSINGROW (0x00000002) + MSITRANSFORM_ERROR_ADDEXISTINGROW (0x00000001)

Breaking down 0x8A2 into behavior

Flag Behavior
MSITRANSFORM_VALIDATE_UPGRADECODE (0x00000800) Validate UpgradeCode for equality.
MSITRANSFORM_VALIDATE_NEWLESSEQUALBASEVERSION (0x00000080) See Below. Note: This is an unusual choice.
MSITRANSFORM_VALIDATE_UPDATEVERSION (0x00000020) major.minor.upgrade fields of Product Version should be validated
MSITRANSFORM_VALIDATE_PRODUCT (0x00000002) Validate ProductCode for equality

So what does newlessequalbase mean?

InstalledVersion <= BaseVersion.

Installed: ProductVersion (ProductVersion of Installed product from in-memory view of Property table)
Base: TransformVersion

The base version that is used is the "Old" Product's version in the Revision property.

So for Patch02, here's what we have:

Installed = 10.5.28.03 (this is the product + Patch01 view of the Property table)
Base = 10.4.27.01

The comparison is then: 

10.5.28.03 <= 10.4.27.01

This always evaluates to false. Now if you apply Patch02 on top of just the product (no Patch01), you have the comparison as: 

10.4.27.01 <= 10.4.27.01

That's true.

Conclusion

In the end, these patches are poorly authored for their validation conditions. They should use the recommended value: 0x922 and additionally the targeting of Patch02 is all wrong. Since it doesn't obsolete Patch01, it would have to target Patch01 which from analyzing the patch, it does not. It only targets RTM and therefore it's impossible to have a valid sequence of patches from RTM -> Patch01 -> Patch02.

Content credit also belongs to

  • Carolyn, MSI Team Dev Lead. You can get other Carolyn insights about developing for Windows Installer from the Windows Installer Chat Archives
  • Nitin, Phil, Alden, and Erik, Microsoft Support Team

[Author: Robert Flaming]

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.