Identifying Administrative Installation Patches

Heath Stewart

Windows Installer has the ability to create administrative installations, which is a copy of the .msi database and files that have been expanded into a source directory structure. These administrative installations can easily be deployed using Active Directory group policy deployment among other things, such as creating patches since PatchWiz.dll requires that patch sources are not compressed. You can create administrative installations using the /a command-line switch, which corresponds to ACTION=ADMIN if specified in the command-line arguments.

You can also patch an administrative installation using both the /a and /p command-line switches. This results in the patch transforms persisting changes to the database; however, the non-administrative transform that begins with a # symbol is not persisted and is used only to resolve the patched files to the correct Media table entry, which describes in what cabinet the patch files can be found.

Because Microsoft Developer Division uses an external UI handler, it writes it’s own registry keys using the Registry table to display entries in the Add/Remove Programs (ARP) control panel. This means that ARPSYSTEMCOMPONENT=1 is set, which I’ve discussed before as causing several problems. This means, too, that patches must write their own registry keys for ARP.

Since patches applied to administrative installations loose their identity as patches since the non-administrative transform isn’t persisted, these patches cannot be removed. This means when installing a patched administrative installation the DWORD registry value NoRemove must be added to the appropriate registry key for each patch applied to the administrative installation. This prevents the Remove button from displaying in ARP for that patch, which would – if clicked – display the message “The patch is not applied to this product.” Frankly, that’s misleading.

There are several ways of solving this problem and hiding that button that all lead to writing the DWORD registry value NoRemove. We need a component that will install only when installing or patching an installation for patches applied to the administrative installation.

Since non-transitive component conditions are evaluated only when the components are first installed, one could use the IsAdminPackage property to determine if the component – which contains the registry value to be written – should be installed. Windows Installer sets this property automatically when installing or reinstalling an administrative installation onto a client machine. Because of problems introduced by setting ARPSYSTEMCOMPONENT=1, I had to implement a custom supersedence strategy using transitive components. That means the conditions are evaluated every time the components are reinstalled, which is when their containing feature or features are reinstalled. Clearly the IsAdminPackage property won’t work in this case.

Another way might be to use the AdminProperties property, which lists property values to save when the administrative installation is created into a sub-stream named AdminProperties that – if the AdminProperties property exists in the Property table – is read for property values to override property values stored in the Property table when the administrative installation is installed. The problem here is that the AdminProperties sub-stream – which is written during the InstallAdminPackage standard action – is only written when the administrative installation is created, not when it is patched. So, patches can’t use this feature to persist an override to a property to indicate that the patch was applied to an administrative installation.

Recall that the non-administrative transform is only applied when the patch is installed on a client machine to patch an installed product. Another solution is to write a property into the non-administrative transform that we could check. In your .pcp file as input to PatchWiz.dll, there must exist an UpgradedImages table. This table lists the upgrade .msi files that PatchWiz.dll uses to create patches. The PatchMsiPath column allows you to specify the path of a copy of the upgrade .msi that contains additional entries in tables. These entries are added to the non-administrative transform instead of the regular patch transform that contains the differences between both the target and upgrade .msi files.

So, create a new property with a patch-specific name and a property value, such as KB123456.ClientPatch=1, in a copy of the upgrade .msi and add the source path to that copy in the PatchMsiPath column for your relevant upgrade .msi entry in the UpgradedImages table. When you create the patch, that non-administrative transform will contain that property in the Property table.

Next, add the condition NOT KB123456.ClientPatch, using the example property name, to the Condition column in the Component table for the relevant component entry that writes the DWORD registry value NoRemove, or whatever component objects you want to install only for administrative installation patches. An example WIX fragment for such a component would look like the following:

<Component Id=”KB123456.ARPNoRemove”>
    <Condition>NOT KB123456.ClientPatch</Condition>
    <Registry Id=”ARP.KB123456.NoRemove” Action=”write” KeyPath=”yes” Root=”HKLM” Key=”SOFTWAREMicrosoftWindowsCurrentVersionUninstallKB123456″ Name=”NoRemove” Type=”integer” Value=”1″/>
</Component>

When the administrative installation is patched, the non-administrative transform isn’t persisted so the KB123456.ClientPatch property won’t exist when the administrative installation is actually installed on a client machine. This means that the component condition that checks that the property doesn’t exist will evaluate to true and the component gets installed, which writes the property value and hides the Remove button from ARP.

0 comments

Discussion is closed.

Feedback usabilla icon