{"id":2373,"date":"2006-03-24T19:55:00","date_gmt":"2006-03-24T19:55:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/heaths\/2006\/03\/24\/identifying-administrative-installation-patches\/"},"modified":"2006-03-24T19:55:00","modified_gmt":"2006-03-24T19:55:00","slug":"identifying-administrative-installation-patches","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/setup\/identifying-administrative-installation-patches\/","title":{"rendered":"Identifying Administrative Installation Patches"},"content":{"rendered":"<p>Windows Installer has the ability to create\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/administrative_installation.asp\">administrative installations<\/a>,\nwhich is a copy of the <i>.msi<\/i> database and files that have been expanded into a source\ndirectory structure. These administrative installations can easily be deployed\nusing Active Directory group policy deployment among other things, such as\ncreating patches since <i>\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/patchwiz_dll.asp\">\nPatchWiz.dll<\/a><\/i> requires that patch sources are not compressed. You can\ncreate administrative installations using the \/a\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/command_line_options.asp\">\ncommand-line switch<\/a>, which corresponds to <code>ACTION=ADMIN<\/code> if specified in the\ncommand-line arguments.<\/p>\n<p>You can also patch an administrative installation using both the \/a and \/p\ncommand-line switches. This results in the patch transforms persisting changes\nto the database; however, the non-administrative transform that begins with a #\nsymbol is not persisted and is used only to resolve the patched files to the\ncorrect\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/media_table.asp\">\nMedia table<\/a> entry, which describes in what cabinet the patch files can be\nfound.<\/p>\n<p>Because Microsoft Developer Division uses an\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/monitoring_an_installation_using_msisetexternalui.asp\">\nexternal UI handler<\/a>, it writes it&#8217;s own registry keys using the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/registry_table.asp\">\nRegistry table<\/a> to display entries in the Add\/Remove Programs (ARP) control\npanel. This means that <code>ARPSYSTEMCOMPONENT=1<\/code> is set,\nwhich I&#8217;ve\n<a href=\"\/heaths\/articles\/arpsystemcomponent.aspx\">discussed\nbefore<\/a> as causing several problems. This means, too, that patches must write\ntheir own registry keys for ARP.<\/p>\n<p>Since patches applied to administrative installations loose their identity as\npatches since the non-administrative transform isn&#8217;t persisted, these patches\ncannot be removed. This means when installing a patched administrative\ninstallation the <code>DWORD<\/code> registry value NoRemove must be added to the appropriate\nregistry key for each patch applied to the administrative installation. This\nprevents the <b>Remove<\/b> button from displaying in ARP for that patch, which\nwould &#8211; if clicked &#8211; display the message &#8220;The patch is not applied to this\nproduct.&#8221; Frankly, that&#8217;s misleading.<\/p>\n<p>There are several ways of solving this problem and hiding that button that\nall lead to writing the <code>DWORD<\/code> registry value NoRemove. We need a component that\nwill install only when installing or patching an installation for patches\napplied to the administrative installation.<\/p>\n<p>Since non-transitive component conditions are evaluated only when the\ncomponents are first installed, one could use the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/isadminpackage.asp\"> <code>IsAdminPackage<\/code> property<\/a>\nto determine if the component &#8211; which contains the registry value to be written\n&#8211; should be installed. Windows Installer sets this property automatically when\ninstalling or reinstalling an administrative installation onto a client machine.\nBecause of problems introduced by setting <code>ARPSYSTEMCOMPONENT=1<\/code>, I\nhad to implement a custom supersedence strategy using transitive components. That\nmeans the conditions are evaluated every time the components are reinstalled,\nwhich is when their containing feature or features are reinstalled. Clearly the\n<code>IsAdminPackage<\/code> property won&#8217;t work in this case.<\/p>\n<p>Another way might be to use the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/adminproperties.asp\">\n<code>AdminProperties<\/code> property<\/a>, which lists property values to save when the\nadministrative installation is created into a sub-stream named <i>AdminProperties<\/i>\nthat &#8211; if the <code>AdminProperties<\/code> property exists in the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/property_table.asp\">Property table<\/a> &#8211; is read\nfor property values to override property values stored in the Property table\nwhen the administrative installation is installed. The problem here is that the\n<i>AdminProperties<\/i> sub-stream &#8211; which is written during the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/installadminpackage_action.asp\">InstallAdminPackage\nstandard action<\/a> &#8211; is only written when the administrative installation is\ncreated, not when it is patched. So, patches can&#8217;t use this feature to persist\nan override to a property to indicate that the patch was applied to an\nadministrative installation.<\/p>\n<p>Recall that the non-administrative transform is only applied when the patch\nis installed on a client machine to patch an installed product. Another solution\nis to write a property into the non-administrative transform that we could\ncheck. In your <i>.pcp<\/i> file as input to <i>\nPatchWiz.dll<\/i>, there must exist an\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/upgradedimages_table_patchwiz_dll_.asp\">\nUpgradedImages table<\/a>. This table lists the upgrade <i>.msi<\/i> files that <i>\nPatchWiz.dll<\/i> uses to create patches. The PatchMsiPath column allows you to\nspecify the path of a copy of the upgrade <i>.msi<\/i> that contains additional\nentries in tables. These entries are added to the non-administrative transform\ninstead of the regular patch transform that contains the differences between\nboth the target and upgrade <i>.msi<\/i> files.<\/p>\n<p>So, create a new property with a patch-specific name and a property value,\nsuch as <code>KB123456.ClientPatch=1<\/code>, in a copy of the upgrade <i>.msi<\/i> and add the\nsource path to that copy in the PatchMsiPath column for your\nrelevant upgrade <i>.msi<\/i> entry in the UpgradedImages table. When you create the patch, that\nnon-administrative transform will contain that property in the Property table.<\/p>\n<p>Next, add the condition <code>NOT KB123456.ClientPatch<\/code>, using the example property\nname, to the Condition column in the\n<a href=\"http:\/\/msdn.microsoft.com\/library\/en-us\/msi\/setup\/component_table.asp\">\nComponent table<\/a> for the relevant component entry that\nwrites the <code>DWORD<\/code> registry value NoRemove, or whatever component objects you want\nto install only for administrative installation patches. An example WIX fragment\nfor such a component would look like the following:<\/p>\n<p><font face=\"monospace\">&lt;Component Id=&#8221;KB123456.ARPNoRemove&#8221;&gt;<br \/>\n&nbsp;&nbsp;&nbsp; &lt;Condition&gt;NOT KB123456.ClientPatch&lt;\/Condition&gt;<br \/>\n&nbsp;&nbsp;&nbsp; &lt;Registry Id=&#8221;ARP.KB123456.NoRemove&#8221; Action=&#8221;write&#8221; KeyPath=&#8221;yes&#8221;\nRoot=&#8221;HKLM&#8221; Key=&#8221;SOFTWAREMicrosoftWindowsCurrentVersionUninstallKB123456&#8243;\nName=&#8221;NoRemove&#8221; Type=&#8221;integer&#8221; Value=&#8221;1&#8243;\/&gt;<br \/>\n&lt;\/Component&gt;<\/font><\/p>\n<p>When the administrative installation is patched, the non-administrative\ntransform isn&#8217;t persisted so the <code>KB123456.ClientPatch<\/code> property won&#8217;t exist when\nthe administrative installation is actually installed on a client machine. This\nmeans that the component condition that checks that the property doesn&#8217;t exist\nwill evaluate to true and the component gets installed, which writes the\nproperty value and hides the <b>Remove<\/b> button from ARP.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":389,"featured_media":3843,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[20],"class_list":["post-2373","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-installation"],"acf":[],"blog_post_summary":"<p>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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts\/2373","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/users\/389"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/comments?post=2373"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/posts\/2373\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/media\/3843"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/media?parent=2373"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/categories?post=2373"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/setup\/wp-json\/wp\/v2\/tags?post=2373"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}