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