Wix database script execute depending on chosen features on FeatureTree - sql

I my installer I want to run some scripts depending on features chosen in FeatureTree. But is seems like installer execute all script which I added.
<Feature Id="FeatureProduct" Title="App Setup" Level="1" ConfigurableDirectory="INSTALLFOLDER">
<ComponentGroupRef Id="ComponentsProduct" />
<ComponentRef Id="ComponentMenuStart" />
<ComponentRef Id="ComponentDesktopFolder"/>
<ComponentRef Id='SqlComponent.DatabaseSchemeCreator' />
<Feature Id='NE051' Title='Module1' Description='Module 1' Level='1'>
<ComponentRef Id='ComponentModules.Module1' />
<ComponentGroupRef Id='ComponentGroupModul1' />
<ComponentRef Id='SqlComponent.DatabaseModule1' />
</Feature>
<Feature Id='NE041' Title='module2' Description='Module 2' Level='1'>
<ComponentRef Id='ComponentModules.Module2' />
<ComponentGroupRef Id='ComponentGroupModul2' />
</Feature>
</Feature>
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="SqlComponent.DatabaseSchemeCreator" Guid="2E7CB14E-7190-4C35-A83F-9B1A5C7A2923">
<Condition><![CDATA[AUTO_CONFIGURE_DB = 1]]></Condition>
<sql:SqlDatabase Id='SqlComponent.DatabaseSchemeCreator' Database='[PROP_SQL_DATABASE_NAME]' Server='[PROP_SQL_SERVER_NAME]'
CreateOnInstall='yes' ContinueOnError='no'>
<sql:SqlScript Id='SqlComponent.DatabaseSchemeCreator' BinaryKey='CreateTablesScript' ExecuteOnInstall='yes' />
</sql:SqlDatabase>
</Component>
</DirectoryRef>
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="SqlComponent.DatabaseModule1" Guid="B7E392C5-3A1E-4F73-9B86-54394E93330B">
<Condition><![CDATA[AUTO_CONFIGURE_DB = 1]]></Condition>
<sql:SqlDatabase Id='SqlComponent.DatabaseModule1' Database='[PROP_SQL_DATABASE_NAME]' Server='[PROP_SQL_SERVER_NAME]'
CreateOnInstall='yes' ContinueOnError='no'>
<sql:SqlScript Id='SqlComponent.DatabaseModule1' BinaryKey='Module1Script' ExecuteOnInstall='yes' />
</sql:SqlDatabase>
</Component>
</DirectoryRef>
Every time I don't mark module 1 in FeatureTree to install Wix execute a script for module 1. Condition in components work well. Do you have any ideas how to prevent this?

As far as I can see you didn't use any feature state for your components (or where does the AUTO_CONFIGURE_DB-property come from?).
If I understood you correctly you want those scripts to be executed only if the related Feature has been set to be installed. According to Conditional Statement Syntax you can use the &feature-action in your conditions:
For example, the conditional expression "&MyFeature=3" evaluates to True only if MyFeature is changing from its current state to the state of being installed on the local computer, INSTALLSTATE_LOCAL.
In your case this would probably translate to the following, e.g. execute the script only if the feature NE051 has been selected to be installed locally:
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="SqlComponent.DatabaseSchemeCreator" Guid="2E7CB14E-7190-4C35-A83F-9B1A5C7A2923">
<Condition><![CDATA[&NE051=3]]></Condition>
<sql:SqlDatabase Id='SqlComponent.DatabaseSchemeCreator' Database='[PROP_SQL_DATABASE_NAME]' Server='[PROP_SQL_SERVER_NAME]' CreateOnInstall='yes' ContinueOnError='no'>
<sql:SqlScript Id='SqlComponent.DatabaseSchemeCreator' BinaryKey='CreateTablesScript' ExecuteOnInstall='yes' />
</sql:SqlDatabase>
</Component>
</DirectoryRef>
</Fragment>

Related

Issue with Wix not creating IIS pool before running SQL setup scripts

I'm writing a Wix msi that needs to create a database, configure IIS and then run a few SQL setup scripts. The issue I'm running into is that the SQL scripts seem to be running before IIS has completed configuration with the appropriate pool / website. One of the SQL scripts relies on the IIS being configured and causes the installer to fail:
Here's what I have (Relevant parts pulled out of various *.wxs files):
Main.wxs:
<Feature Id="ProductFeature" Title="SamplePortalApi.Setup" Level="1">
<ComponentGroupRef Id="SamplePortalApiComponents" />
<ComponentGroupRef Id="MyIISConfiguration" />
<ComponentRef Id="SqlComponent" /> <!-- Database Setup / Scripts Run Here -->
</Feature>
ConfigureIIS.wxs:
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="SamplePortalAppPool" Guid="" KeyPath="yes">
<iis:WebAppPool Id="SamplePortalAppPool"
Name="SamplePortalApi"
Identity="applicationPoolIdentity"
ManagedPipelineMode="Integrated"
ManagedRuntimeVersion="v4.0" />
</Component>
<Component Id="InstallWebsite" Guid="" KeyPath="yes">
<iis:WebSite Id="SamplePortalApi" Description='SamplePortalApi' Directory='INSTALLFOLDER' AutoStart='no' StartOnInstall='no'>
<iis:WebAddress Id="AllUnassigned" Port="[DB_PORT]" />
<iis:WebApplication Id="SamplePortalApiApplication" Name="[SamplePortalApi][WEBSITE_ID]" WebAppPool="SamplePortalAppPool"></iis:WebApplication>
</iis:WebSite>
</Component>
</DirectoryRef>
<ComponentGroup Id="SamplePortalApiIisConfiguration">
<ComponentRef Id="InstallWebsite" />
<ComponentRef Id="SamplePortalAppPool" />
</ComponentGroup>
Database.wxs
<Binary Id="MigrationBin" SourceFile="Migration.sql"></Binary>
<Binary Id="CreateLoginBin" SourceFile="CreateLogin.sql"></Binary>
<Binary Id="CreateUserBin" SourceFile="CreateUser.sql"></Binary>
<Binary Id="DeleteLoginBin" SourceFile="DeleteLogin.sql"></Binary>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="SqlComponent" Guid="8A1C82DB-1DD3-4FB5-8600-4F370FE1E04B">
<Condition>NOT Installed</Condition>
<sql:SqlDatabase Id="SamplePortalApiDatabase" Database="[DB_DATABASE]" Server="[DB_SERVER]" CreateOnInstall="yes" DropOnUninstall="yes" ContinueOnError="no">
<sql:SqlScript Id="Migration" ExecuteOnInstall="yes" ExecuteOnUninstall="no" BinaryKey="MigrationBin" ContinueOnError="no" />
<sql:SqlScript Id="CreateLogin" ExecuteOnInstall="yes" ExecuteOnUninstall="no" BinaryKey="CreateLoginBin" ContinueOnError="no"/>
<sql:SqlScript Id="DeleteLogin" ExecuteOnInstall="no" ExecuteOnUninstall="yes" BinaryKey="DeleteLoginBin" ContinueOnError="no"/>
</sql:SqlDatabase>
<CreateFolder/>
</Component>
</DirectoryRef>
What's the best approach here? I think I could run the problematic SQL as a CustomAction rather than as part of the built in WXS functionality, but I don't think this is the most elegant approach. Is there a better way of ensuring that IIS is setup properly first?

WiX installing unselected features

I'm in the process of using WiX to create an install package with multiple features. It's using the Mondo UI to allow the user to select one or more features to install. The problem I'm having is that it's always installing all features, regardless of what the user selects.
Below is my WXS file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Setup Test 1" Language="1033" Version="1.0.0.0" Manufacturer="dbush" UpgradeCode="MYGUID="yes" InstallScope="perMachine" Comments="testing the installer" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="all" Title="all features" Description="everything" Level="1" Display="expand">
<Feature Id="file1" Title="file1" Description="file1.txt" Level="1">
<ComponentGroupRef Id="file1" />
</Feature>
<Feature Id="file2" Title="file2" Description="file2.txt" Level="10">
<ComponentGroupRef Id="file2" />
</Feature>
<Feature Id="regkey" Title="registry key" Description="registry properties to install" Level="11">
<ComponentGroupRef Id="regkey" />
</Feature>
</Feature>
<Property Id="PROP1" Value="replacement" />
<UIRef Id="WixUI_Mondo" />
<UIRef Id="WixUI_ErrorProgressText" />
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="setup_test_1" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="file1" Directory="INSTALLFOLDER">
<Component Id="file1.txt" Guid="MYGUID">
<File Id="file1.txt" Source="src/file1.txt" />
</Component>
</ComponentGroup>
<ComponentGroup Id="file2" Directory="INSTALLFOLDER">
<Component Id="file2.txt" Guid="MYGUID">
<File Id="file2.txt" Source="src/test2.txt" />
</Component>
</ComponentGroup>
<ComponentGroup Id="regkey" Directory="INSTALLFOLDER">
<Component Id="reg_key_1">
<RegistryValue Root='HKCU' Key='SOFTWARE\setup_test_1\properties'
Name='prop1' Value='[PROP1]'
Type='string' />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
What could be causing this?
I'm using WiX toolset 3.11.2 and Visual Studio 2017.
This was caused by the main Feature element having Id="all". Changing it to some other string such as "everything" allowed for individual features to be installed.
I wasn't able to find any documentation on why an Id with this name is a special case.

Wix Duplicate symbol 'File:AdminToolsConsoleFile' found

I use wix 3.5 for install project. I have a file which can be installed as service or as file. I try to make it like this:
<Component Id="MyProgrammAsService" Guid="*">
<File Id="MyFile" Name="File.exe" DiskId="1" Source="$(var.SourceDir)\MyFile.exe" />
<ServiceInstall ...
</ServiceInstall>
</RegistryKey>
</Component>
<Component Id="MyProgrammWithoutService" Guid="*">
<File Id="MyFile" Name="File.exe" DiskId="1" Source="$(var.SourceDir)\MyFile.exe" />
</Component>
But error was throw Duplicate symbol 'File:AdminToolsConsoleFile' found. How can I bypass this problem?
UPD:
Feature code:
<Feature>
<ComponentRef Id="SyncWithoutService" />
<Feature Id="MyProgrammAsServiceFeature" Level="100"
Description="Install as service"
Title="Register as service" AllowAdvertise="no">
<ComponentRef Id="MyProgrammAsService" />
</Feature>
<ComponentGroupRef Id="MyGroup" />
</Feature>

Wix installer create components based on the Parameter passed to msi

I have two Component files one for each application. I am passing a parameter MachineName to the msi.(example: MachineName=A ) I want to copy all the files mentioned in Component1.wxs and create the Directory-A, ApplicationA and AppPoolA.
The below code works partially. but it creates both the AppPool and creates both the directory all the time. How do I prevent the Directory-B and AppPoolB being created when MachineName=A?
Component1.wxs
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="Dir-A">
<Component Id="cmp04AF5786493B45809EFC4A5D12FFB07B" Guid="{6B67F4D0-81AE-47D5-9C3E-1119A560FCAE}">
<File Id="fil04AF5786493B45809EFC4A5D12FFB07B" KeyPath="yes" Source="testA.html" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="Dir-AFiles">
<ComponentRef Id="cmp04AF5786493B45809EFC4A5D12FFB07B" />
</ComponentGroup>
</Fragment>
</Wix>
Component2.wxs
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="Dir-B">
<Component Id="cmpEBBDC1C336C0421C9AFFF92D4F109355" Guid="{7D5AB6CC-A193-4AC2-827E-806077990149}">
<File Id="filEBBDC1C336C0421C9AFFF92D4F109355" KeyPath="yes" Source="testB.html" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="Dir-BFiles">
<ComponentRef Id="cmpEBBDC1C336C0421C9AFFF92D4F109355" />
</ComponentGroup>
</Fragment>
</Wix>
In my Product.wxs
<Property Id="MachineName" />
<iis:WebSite Id="TargetWebsite" Description="Default Web Site or 80 port" xmlns="http://schemas.microsoft.com/wix/IIsExtension">
<iis:WebAddress Id="TargetWebsite" IP="*" Port="80" />
</iis:WebSite>
<Feature Id="IISFeature" Title="IIs" Level="1">
<ComponentRef Id="AppPoolComponentA"/>
<ComponentRef Id="AppPoolComponentB"/>
</Feature>
<!--Do this if MachineName = "A"-->
<Feature Id="FeatureA" Title="Feature A" Level="1">
<Condition Level="1"><![CDATA[MachineName = "A"]]></Condition>
<ComponentGroupRef Id="Dir-AFiles" />
<ComponentRef Id="ComponentA"/>
</Feature>
<!--Do this if MachineName = "B"-->
<Feature Id="FeatureB" Title="Feature B" Level="1">
<Condition Level="1"><![CDATA[MachineName = "B"]]></Condition>
<ComponentGroupRef Id="Dir-BFiles" />
<ComponentRef Id="ComponentB"/>
</Feature>
<!--Do this if MachineName = "A"-->
<Component Id="AppPoolComponentA" Guid="{044A3132-C4F4-422A-836B-46BCF172D720}" Directory="TARGETDIR" Permanent="yes">
<iis:WebAppPool Id="AppPoolA" Name="AppPoolA" ManagedRuntimeVersion="4.0" ManagedPipelineMode="integrated"/>
</Component>
<!--Do this if MachineName = "B"-->
<Component Id="AppPoolComponentB" Guid="{D651DF9C-D80D-4F3E-BE7B-DB2BBA9D24C8}" Directory="TARGETDIR" Permanent="yes">
<iis:WebAppPool Id="AppPoolB" Name="AppPoolB" ManagedRuntimeVersion="4.0" ManagedPipelineMode="integrated"/>
</Component>
<Directory Id="TARGETDIR" Name="SourceDir">
<!--Do this if MachineName = "A"-->
<Directory Id="Dir-A" Name="Directory-A">
<Component Id="ComponentA" Guid="{C25BD2E1-9539-45E3-BF9B-B4DD8FB6DB47}">
<Condition><![CDATA[MachineName = "A"]]></Condition>
<iis:WebVirtualDir Id="VD-A" Alias="DirectoryA" Directory="Dir-A" WebSite="TargetWebsite" xmlns="http://schemas.microsoft.com/wix/IIsExtension">
<iis:WebApplication Id="ApplicationA" Name="DirectoryA" WebAppPool="AppPoolA"/>
</iis:WebVirtualDir>
<CreateFolder/>
</Component>
</Directory>
<!--Do this if MachineName = "B"-->
<Directory Id="ConsoleComponentsDir" Name="Directory-B">
<Component Id="ComponentB" Guid="{EA11875E-87A3-41EC-B60F-D8CA2CF2CB59}">
<Condition><![CDATA[MachineName = "B"]]></Condition>
<iis:WebVirtualDir Id=VD-B" Alias="DirectoryB" Directory="ConsoleComponentsDir" WebSite="TargetWebsite" xmlns="http://schemas.microsoft.com/wix/IIsExtension">
<iis:WebApplication Id="ApplicationB" Name="DirectoryB" WebAppPool="AppPoolB"/>
</iis:WebVirtualDir>
<CreateFolder/>
</Component>
</Directory>
</Directory>
Separate the Directory fragment into two pieces, one for the AppA and an other one for the AppB, I would create a Property "APPASELECTED", which will be set when you select the feature that you want to install.
You could even force it from the command line that way

How to use conditions in features in WiX?

I am trying to make simple Windows intaller, and I don't know how to deal with this.
I have two features - feature1 and feature2. I want feature2 to be installed only if the user selected feature1 to be installed. So I tried:
<Feature Id='core' Title='Core'
Description='ØMQ 1.0.0 core functionality and C++ API' Level='1'>
<ComponentRef Id='Core_include' />
<ComponentRef Id='Core_bin' />
<ComponentRef Id='Core_lib' />
<ComponentRef Id='Core_zmq' />
<ComponentRef Id='cpp_bin' />
</Feature>
<Feature Id='core_perf' Title='core_perf' Description='0MQ core perf' Level='999'>
<Condition Level="0">NOT (&core = "3")</Condition>
<ComponentRef Id='cpp_perf' />
</Feature>
But this doesn't install feature core_perf if the user selects feature core.
How can I fix this?
You need to move your Condition into your Component definition, and use ! (Feature state) instead of & (Feature action) so that it works when you try to add the examples by re-running the install a second time:
<Component Id="example1">
<Condition>!feature1 = 3</Condition>
</Component>
<Component Id="example2">
<Condition>!feature2 = 3</Condition>
</Component>
<Feature Id="feature1">
</Feature>
<Feature Id="feature2">
</Feature>
<Feature Id="examples">
<ComponentRef Id="example1" />
<ComponentRef Id="example2" />
</Feature>
Have you considered making feature1 the parent of feature2? Then feature2 can't be installed unless feature1 will also be installed. No condition necessary.
<Feature Id='core' Title='Core'
Description='ØMQ 1.0.0 core functionality and C++ API' Level='1'>
<ComponentRef Id='Core_include' />
<ComponentRef Id='Core_bin' />
<ComponentRef Id='Core_lib' />
<ComponentRef Id='Core_zmq' />
<ComponentRef Id='cpp_bin' />
<Feature Id='core_perf' Title='core_perf' Description='0MQ core perf'
Level='999'>
<ComponentRef Id='cpp_perf' />
</Feature>
</Feature>