0
votes

I have a WiX installer that supports major upgrades. I found that in some specific test environments, the installer on upgrade would remove existing unchanged components.

These (IIS web app pool, IIS website, etc.) components are installed in this way, under TARGETDIR:

<Directory Id="TARGETDIR" Name="SourceDir">
      <Component Id="myComponent" Guid="MY-GUID">
        <iis:WebAppPool Id="ID" Name="MyWebAppPool" Identity="networkService" ManagedPipelineMode="classic" ManagedRuntimeVersion="v4.0"/>
      </Component>
</Directory>

For the problem environments, the app pool is deleted on upgrade. Upgrades are authored in this way:

<MajorUpgrade Schedule="afterInstallExecute" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."  AllowDowngrades ="no" />

In upgrade logs, I see these entries:

MSI (s) (58:20) [11:22:58:433]: Allowing uninstallation of shared component: {MY-GUID}. Other clients exist, but installed to a different location

In environments where the problem is not seen (ie. the components are not uninstalled on upgrade), I see these log entries:

MSI12cb8.LOG:9594:MSI (s) (10:EC) [09:36:37:068]: Disallowing uninstallation of component: {MY-GUID} since another client exists

The only explanation I've been able to come up with is that for the problem environments, TARGETDIR changes between the initial install and the upgrade. According to Rob, TARGETDIR is set to the largest drive. If the largest drive (drive with most free space available?) on a system changes between initial install and upgrade, the keypath of the components will change causing the components to be uninstalled on upgrade.

Questions

  • Does this explanation sound correct?
  • How can I fix this for upgrades to existing product installations? Is there a way to set TARGETDIR on upgrade to the same value used for initial install?
2

2 Answers

2
votes

What's happening appears to be this: Your upgrade installs to some TARGETDIR that you seem to be saying you have little control over. At the end of that upgrade, RemoveExistingProducts uninstalls the old product, and that includes removing the app pool because the component is being uninstalled, presumably with a custom action (because there is no native support for app pools). Removing the component from the old location is fine because it's no longer needed there, but it looks like the custom action that removes the app pool is tied to that component removal and therefore deletes the app pool. In other words you have tied your app pool to the component sharing rules that now require you to do an in-place upgrade. Another way of looking at it is that in situations like these people add a "AND NOT UPGRADINGPRODUCTCODE" to the uninstall condition on the CA that removes the app pool so that it doesn't delete the app pool on an upgrade because it may just have been created in a new location, as in your case.

That's rather long winded, sorry, but the requirement for an in-place upgrade is usually met by having something on the system that you can search for (such a registry entry) and set the install folder to that location, disallowing any UI that can change it. You should also start getting control over your install folders (you make it sound like your install could go literally anywhere). The key word here is "default". TARGETDIR will default somewhere if you can't be botherd to set it.

0
votes

For anyone else who stumbles upon a similar problem, I worked around it using a custom action. As @PhilDW pointed out, NOT UGPRADINGPRODUCTCODE will not work for the existing install. I couldn't figure out a way to stop the app pool deletion on upgrade. Instead, I added a custom action scheduled for after removal that adds the app pool back, if it was deleted.

<CustomAction Id="CreateAppPoolCustomAction.SetProperty" Return="check" Property="CreateAppPoolCustomAction" Value="AppPoolName=$(var.AppPoolName)" />
<CustomAction Id="CreateAppPoolCustomAction" BinaryKey="MyCustomActions.CA.dll" DllEntry="CreateAppPoolCustomAction" Execute="deferred" Return="check" Impersonate="no" />

<InstallExecuteSequence>
  <Custom Action="CreateAppPoolCustomAction.SetProperty" Before="CreateAppPoolCustomAction" />
  <Custom Action="CreateAppPoolCustomAction" After="RemoveExistingProducts">WIX_UPGRADE_DETECTED AND NOT (REMOVE="ALL")</Custom>
</InstallExecuteSequence>

The C# custom action in MyCustomActions.dll usesMicrosoft.Web.Administration.ServerManager to create the application pool, if it's missing.