Features installed to different locations but referencing the same components

Casey picture Casey · Feb 9, 2011 · Viewed 8.3k times · Source

I have a product that consists of multiple features that can be installed to different locations e.g. Feature 1 is an executable installed in Program Files and Feature 2 is a website installed in wwwroot. However both Feature 1 and Feature 2 rely on many of the same dll's and hence require the components containing those dll's to be installed in 2 different locations depending on which Features are installed.

Is there a way to achieve this without defining every component twice?

To provide a further complete example of what I am trying to achieve, the following complete wxs file can be compiled using:

> candle.exe Foobar.wxs

> light.exe -ext WixUIExtension Foobar.wixobj

> msiexec /i Foobar.msi

<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>

  <Product Name='Foobar 1.0' 
           Id='E578DF12-DDE7-4BC2-82CD-FF11862862D5' 
           UpgradeCode='90F09DD5-E01B-4652-8971-515997730195'
           Language='1033' 
           Codepage='1252' 
           Version='1.0.0' 
           Manufacturer='Acme Ltd.'>

    <Package Id='*' 
             Keywords='Installer'
             Description="Acme 1.0 Installer"
             InstallerVersion='100' 
             Languages='1033' 
             Compressed='yes' 
             SummaryCodepage='1252' />

    <Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
    <Property Id='DiskPrompt' Value="Acme 1.0 Installation" />

    <Directory Id='TARGETDIR' Name='SourceDir'>
         <!-- Directory 1 (Program Files) -->
        <Directory Id="ProgramFilesFolder" Name="PFiles">
            <Directory Id="PROGRAM_INSTALLDIR" Name="Acme" />
        </Directory>

        <!-- Directory 2 (wwwroot) -->
        <Directory Id="Inetpub" Name="Inetpub">
            <Directory Id="wwwroot" Name="wwwroot">
                <Directory Id="WEBSITE_INSTALLDIR" Name="AcmeWebSite" />
            </Directory>
        </Directory>
    </Directory>

    <DirectoryRef Id='PROGRAM_INSTALLDIR'>
        <Component Id="Component1" Guid="79EC9E0B-8325-427B-A865-E1105CB16B62">
            <File Id="File1" Name="File1.txt" Source="File1.txt" />
        </Component>
    </DirectoryRef>

    <DirectoryRef Id='WEBSITE_INSTALLDIR'>
        <Component Id="Component2" Guid="702E6573-8FBC-4269-A58D-FD1157111F0F">
            <File Id="File2" Name="File2.txt" Source="File2.txt" />
        </Component>
    </DirectoryRef>

    <Feature Id="Feature.Program" 
             Title="My Program" 
             TypicalDefault="install" 
             Level="1" 
             ConfigurableDirectory="PROGRAM_INSTALLDIR" >
        <ComponentRef Id="Component1"/>
        <ComponentRef Id="Component2"/>
    </Feature>

    <Feature Id="Feature.Website" 
             Title="My Website" 
             TypicalDefault="install" 
             Level="1" 
             ConfigurableDirectory="WEBSITE_INSTALLDIR" >
        <ComponentRef Id="Component1"/>
        <ComponentRef Id="Component2"/>
    </Feature>

    <UIRef Id="WixUI_Mondo" />
    <UIRef Id="WixUI_ErrorProgressText" />

  </Product>
</Wix>

This will however result in ONLY File1.txt being installed in

C:\Program Files (x86)\Acme

and ONLY File2.txt being installed in

_C:\Inetpub\wwwroot\AcmeWebsite_

One solution is to define the components twice such as:

<DirectoryRef Id='PROGRAM_INSTALLDIR'>
    <Component Id="Component1" Guid="79EC9E0B-8325-427B-A865-E1105CB16B62">
        <File Id="File1" Name="File1.txt" Source="File1.txt" />
    </Component>
    <Component Id="Component2" Guid="702E6573-8FBC-4269-A58D-FD1157111F0F">
        <File Id="File2" Name="File2.txt" Source="File2.txt" />
    </Component>
</DirectoryRef>

<DirectoryRef Id='WEBSITE_INSTALLDIR'>
    <Component Id="Component1.Web" Guid="397E93AA-32FB-425A-A783-386E0CCA2357">
        <File Id="File1.Web" Name="File1.txt" Source="File1.txt" />
    </Component>
    <Component Id="Component2.Web" Guid="5C3AFF06-3623-4524-A90B-72B46DE5572A">
        <File Id="File2.Web" Name="File2.txt" Source="File2.txt" />
    </Component>
</DirectoryRef>

<Feature Id="Feature.Program" 
         Title="My Program" 
         TypicalDefault="install" 
         Level="1" 
         ConfigurableDirectory="PROGRAM_INSTALLDIR" >
    <ComponentRef Id="Component1"/>
    <ComponentRef Id="Component2"/>
</Feature>

<Feature Id="Feature.Website" 
         Title="My Website" 
         TypicalDefault="install" 
         Level="1" 
         ConfigurableDirectory="WEBSITE_INSTALLDIR" >
    <ComponentRef Id="Component1.Web"/>
    <ComponentRef Id="Component2.Web"/>
</Feature>

But then what happens if we add a third feature that is to be installed in another location? Do we then have to redefine every component again? With over 100 components, managing duplicate components will become a big job.

Any suggestions?

Answer

Rob Mensching picture Rob Mensching · Feb 15, 2011

You are seeing a limitation in the Windows Installer. A Component can only be installed once via an MSI. Each Component can only be installed to a single Directory. To have the contents of a Component installed to two different locations, you either have to create another Component with the same content or try to use the CopyFile element to duplicate the content.

Probably not what you wanted to hear but that is the way the Windows Installer works.

Fortunately, if you chose to go with option 1, then WiX toolset will only compress the duplicated content across the Components once. Smart cabbing rocks!