Features installed to different locations but referencing the same components
Asked Answered
R

3

8

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?

Regale answered 9/2, 2011 at 5:23 Comment(1)
For anyone looking for how this issue was resolved, I ended up using Method 3 from this Microsoft Support article so that the website could load it's dlls from the Program Files bin location and installed a single copy of the dlls there. Only a small number of dlls need to be duplicated as they are required to load the web app before it can setup the AssemblyResolve handler. Our software is a bit weird in that the website uses reflection to load a large number of objects out of a large number of dlls, making defining duplicate components or defining thRegale
S
15

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!

Squamosal answered 14/2, 2011 at 23:0 Comment(1)
Working link: robmensching.com/blog/posts/2007/6/1/… (and many 🙏Rob!)Banker
M
1

I recommend creating a separate feature which contains only the common components. It shouldn't be installed by default. You can then create a custom action which marks this feature for installation only when one of your actual features is installed.

To mark the feature for installation you can use MsiSetFeatureState function: http://msdn.microsoft.com/en-us/library/aa370387(VS.85).aspx

The custom action which does this can be conditioned with the feature action of your features: http://msdn.microsoft.com/en-us/library/aa368561(VS.85).aspx

Marlowe answered 9/2, 2011 at 7:17 Comment(2)
I still don't understand how this helps to install the common components to 2 different locations as specified by each feature. Perhaps you could elaborate on how you see that working?Regale
Why do you want to duplicate the common components? Usually common resources are placed in a public location (for examample Program Files\Common Files) and all products use them from this location.Marlowe
W
0

Features can reference a directory for Browse capability but that only means something if the components use directories that are that directory or a child of that directory. Otherwise the component will go to the directory specified. In other words, you could have INSTALLDIR for the feature and most components yet have ANOTHERDIR ( say [CommonFilesFolder]Company\Shared for another component and it will go there. That component can then belong to multiple features and you'll be ok.

Weatherly answered 9/2, 2011 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.