I am building a merge module for a Windows service, using WiX. The job of the merge module is not only to install the files, but also to do some configuration should the user chose this option during the setup (separate dialog with some form fields and checkboxes). The rest of the setup is built using InstallShield.
For this I have two components. The first basically just writes a registry entry. The other configures and starts the service, its key path is a (different) registry key.
Works like a charm, until I tested running the installer twice. The InstallShield Maintenance dialog pops up and I can select to modify the installation. My expectation was that I can switch between my two components by changing whether the components opposite conditions are met ($(var.ValidConfiguration)
is defined somewhere in the WiX files, has parentheses around the whole thing):
Component with no configuration:
<Component Id="ServiceConfigurationDisabled" Guid="(some guid)" Transitive="yes">
<Condition><![CDATA[NOT $(var.ValidConfiguration)]]></Condition>
<ServiceControl
Id="DontStartService" Name="MyService"
Stop="both" Remove="both" Wait="no" />
<!-- Registry entry without value, is key path -->
<RegistryValue
Root="HKLM" Key="SOFTWARE\Company\MyService)"
Name="AutoconfigDisabled" Value=""
Type="string" KeyPath="yes" />
</Component>
Component with configuration:
<Component Id="ServiceConfigurationEnabled" Guid="(different guid)" Transitive="yes">
<Condition><![CDATA[$(var.ValidConfiguration)]]></Condition>
<ServiceControl
Id="StartService" Name="MyService"
Start="install" Stop="both" Remove="both" Wait="no" />
<!-- Other registry entry, this time storing some information, is key path -->
<RegistryValue
Root="HKLM" Key="SOFTWARE\Company\MyService"
Name="AutoconfigEnabled" Value="some value"
Type="string" KeyPath="yes" />
<!-- Other registry values, settings, ... -->
<RegistryValue (...) />
<util:XmlFile (...) />
<firewall:FirewallException (...) />
</Component>
The first time, I let the condition evaluate to false.
The log file has these entries during InstallValidate
:
ServiceConfigurationDisabled.(guid); Installed: Absent; Request: Local; Action: Local
ServiceConfigurationEnabled.(guid); Installed: Absent; Request: Local; Action: Null
Perfect.
None of the actions of the second component are performed, only the AutoconfigDisabled
registry key is created.
Calling the setup again, this time with a condition evaluating to true:
ServiceConfigurationDisabled.(guid); Installed: Local; Request: Null; Action: Null
ServiceConfigurationEnabled.(guid); Installed: Local; Request: Null; Action: Null
Wait, what?
Even though the second component was not installed (this is what Action: Null
means?) during the first round, it now registered as Installed: Local
. None of the actions of the second component are performed, the registry is unchanged.
What am I doing wrong?
Component conditions are not generally re-evaluated during a maintenance operation, so they're not good for things the user is likely to change. You might be able to get your desired behavior by setting the transitive attribute on each component, which requests the condition be re-evaluated. But I suspect you will have to factor out your components into separate merge modules so they can be placed on separate features in the consuming project, and then handle selecting those features exclusively for both first-time installations and maintenance.