I've created a merge module following the instructions in the Getting Started Wix guide located at: http://wix.sourceforge.net/manual-wix2/authoring_merge_modules.htm.
Here is the merge module wxs:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Module Id="SomeRepositoryMergeModule" Language="1033" Version="1.0.0.0">
<Package Id="f11e7321-a687-4d53-8be7-21a8ae0721a6" Manufacturer="SomeCompany Technologies" InstallerVersion="200" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="MODULEINSTALLLOCATION" Name="Some Repository">
<Component Id="ServicesHostWindowsService" Guid="257D1FAE-4AFF-4155-BDB8-D81F50E5862B">
<File Id="ServicesHostInstallerExecutable" KeyPath="yes" DiskId="1" Name="WindowsServiceHost.exe" Source="..\WindowsServiceHost\bin\Output_For_Installer\WindowsServiceHost.exe" />
<File Id="ServicesHostConfig" KeyPath="no" DiskId="1" Name="WindowsServiceHost.exe.config" Source="..\WindowsServiceHost\bin\Output_For_Installer\WindowsServiceHost.exe.config" />
<File Id="SomeCompanyCommon" KeyPath="no" DiskId="1" Name="SomeCompany.Common.dll" Source="..\WindowsServiceHost\bin\Output_For_Installer\SomeCompany.Common.dll" />
<File Id="SomeRepositorySqlScript" KeyPath="no" DiskId="1" Name="SomeRepository.sql" Source="..\..\..\..\..\DB\SomeRepository\SomeRepository.sql" />
<File Id="LogConfigXml" KeyPath="no" DiskId="1" Name="log.config.xml" Source="..\WindowsService\log.config.xml" />
<ServiceInstall Id="ServicesHostInstallElement" ErrorControl="normal" Start="auto" Type="ownProcess" Vital="yes"
Name="AServer WindowsService Host"
Description="The windows service responsible for hosting SomeCompany Some Repository's WindowsService."
/>
<ServiceControl Id="ServicesHostController" Name="AServer WindowsService Host" Remove="uninstall" Start="install" Stop="uninstall" Wait="no" />
</Component>
</Directory>
</Directory>
</Directory>
<ComponentGroupRef Id="Product.Generated" /><!-- Harvested by heat -->
</Module>
</Wix>
And here is the main product wxs:
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="SomeCompanyGlobalDir" Name="SomeCompany Technologies">
<Directory Id="INSTALLLOCATION" Name="Some Repository">
<Merge Id='SomeRepositoryPrimaryModule' Language='1033' SourceFile='..\SomeRepositoryMergeModule\bin\Output_For_Installer\SomeRepositoryMergeModule.msm' DiskId='1' />
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFeature" Title="SomeRepositoryStandaloneInstaller" Level="1">
<!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
<MergeRef Id="SomeRepositoryPrimaryModule"/>
</Feature>
<UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION" />
When I build the installer and run it, the files from the merge module are not going into the directory selected by the user from within the installer UI. No matter what, they go into the "[Program Files]\SomeCompany Technologies\Some Repository\" directory.
If I remove the reference to Program Files from the merge module directory path and use a root dir with a name of "." to pick up the parent directory of the parent MSI then the merge module picks up the user selected directory just fine. But then Visual Studio throws an error on build that harvesting won't work because the path must be rooted in one of the standard directories in order to use automatically generated Guids.
So how can I get the merge module to take the directory selected by the user at install time while still keeping the merge module path rooted in a standard directory?
You are confusing MSI because ProgramFilesFolder is a reserved property name. Change that value to "MergeRedirectFolder" and by virtue of the Merge element under the Directory element with Id of INSTALLLOCATION MergeRedirectFolder will become associated with INSTALLLOCATION. The directory Some Repository ([MODULEINSTALLLOCATION]) will then be a subdirectory of INSTALLLOCATION.
Also feel free to checkout the ISWIX project on CodePlex. It's useful for authoring and maintaining merge modules and has sample source code relevant to what you are trying to do here.
Related
What does error "error LGHT0204 : ICE57: Component 'XXX' has both per-user data and a keypath that can be either per-user or per-machine" mean?
Is it possible to fix this error?
In any case Wix creates the msi file and it is possible to install the application. Is it the real error or just a warning? Can I ignore this error in case if it is a warning? Or something should be corrected?
This is wxs file for my Single Package Authoring installation:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Name='Foobar 1.0' Id='GUID' UpgradeCode='GUID'
Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>
<Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"
Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
<Property Id="ALLUSERS" Secure="yes" Value="2" />
<Property Id="MSIINSTALLPERUSER" Secure="yes" Value="1" />
<Property Id='ApplicationFolderName' Value="Acme" />
<Property Id='WixAppFolder' Value="WixPerUserFolder" />
<Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
<Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='Acme' Name='Acme'>
<Directory Id='INSTALLDIR' Name='Foobar 1.0'>
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="Foobar 1.0">
<Component Id="ProgramMenuDir" Guid="GUID">
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKMU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="INSTALLDIR">
<Component Id='MainExecutable' Guid='GUID'>
<File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='FoobarAppl10.exe' >
<Shortcut Id="startmenuFoobar10" Directory="ProgramMenuDir" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" />
</File>
<RegistryKey Root="HKMU" Key="Software\[Manufacturer]\[ProductName]">
<RegistryValue Name="FoobarEXE" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
</Component>
</DirectoryRef>
<Feature Id='Complete' Title='Foobar 1.0' Description='The complete package.'
Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>
<Feature Id='MainProgram' Title='Program' Description='The main executable.' Level='1'>
<ComponentRef Id='MainExecutable' />
<ComponentRef Id='ProgramMenuDir' />
</Feature>
</Feature>
<Icon Id="Foobar10.exe" SourceFile="FoobarAppl10.exe" />
</Product>
</Wix>
The intention of the ICE57 checks is to verify components are not mixing per-user and per-machine files and/or registry settings. However Rob Mensching's comment in http://sourceforge.net/p/wix/mailman/message/26687047/ indicates the ICE57 checks are not perfect:
IIRC, this is a bug in ICE57. The Windows Installer team didn't look at
ALLUSERS property when evaluating these values... that was a long time ago
though so my memory may have decayed a bit.
Your sample looks correct with respect to Single Package Authoring rules. The two main target directories are ProgramFilesFolder and ProgramMenuFolder, both of which are redirected appropriately for per-user installs. The registry key root is HKMU, which ends up as HKLM on per-machine installs and HKCU on per-user installs.
This looks like a bug in ICE57. Sadly we don't have access to the ICE57 source, so it is difficult to fix directly. There are a couple of options to work around the problem:
Option 1 - Suppress ICE57
Not ideal because it suppresses all four rules this group, not just the rule that is failing.
Option 2 - Use Advertised Shortcuts
Change your short-cut to advertised, and lose the associated registry key. The MainExecutable component simplifies to:
<Component Id='MainExecutable' Guid='GUID'>
<File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1'
Source='FoobarAppl10.exe' KeyPath='yes'>
<Shortcut Id="startmenuFoobar10"
Directory="ProgramMenuDir"
Name="Foobar 1.0"
WorkingDirectory='INSTALLDIR'
Icon="Foobar10.exe"
IconIndex="0"
Advertise='yes'/>
</File>
</Component>
The ICE57 checks seem to be more reliable with advertised short-cuts.
Note about ICE Checks
All of the ICE checks are stored in the file darice.cub. This is actually a Windows Installer database file that you can open with Orca. In a typical WIX installation this file can be found in: C:\Program Files (x86)\WiX Toolset vX.Y\bin. If you open darice.cub in Orca you can see the ICE checks as custom actions. Most refer to one or more binary DLLs embedded in the file. A few are implemented as VBS.
This question has been asked dozens of times. Rather than post all the links, please search for ICE57 in SO and refer to the explanations and answers. For example this:
(WiX) Program files shortcut for per-machine install
and this:
How to fix ICE57.Per-User installation
Short answer: Ignore it if you always install per user and fix it if you install per system. It's a static test on the MSI file and can't know which context the install will eventually be, so it gives the warning anyway.
This question is asking whether one of the ICE57 validators creates a false positive error report.
I am using WIX 3.9 to generate an installer. I want a per machine installation with non advertised shortcuts.
This WXS example installs a text file and a shortcut to open the text file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="ShortcutTest" Language="1033"
Version="1.0.0.0" Manufacturer="Widget Co"
UpgradeCode="--YOUR GUID1--">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes"/>
<Feature Id="ProductFeature" Title="ShortcutTest" Level="1">
<ComponentRef Id="TextFile" />
<ComponentRef Id="ShortCut" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="ShortcutTest">
<Component Id="TextFile" Guid="--YOUR GUID2--">
<File Id="File" Name="TextFile.txt" Source="TextFile.txt" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="Shortcut Test">
<Component Id="ShortCut" Guid="--YOUR GUID3--">
<RegistryValue Root="HKMU" Key="Software\WidgetCo\ReadMeTextFile\TextFile" Name="Installed" Type="string" Value="yes" KeyPath="yes"/>
<Shortcut Id="Shortcut"
Name="Open Text File"
Description="Opens a text file"
Target="[INSTALLFOLDER]TextFile.txt"
WorkingDirectory="INSTALLFOLDER"/>
<RemoveFolder Id="ApplicationProgramsFolder" Directory="ApplicationProgramsFolder" On="uninstall"/>
</Component>
</Directory>
</Directory>
</Directory>
</Product>
</Wix>
If you build the above example into an MSI package, you get this Internal Consistency Evaluator (ICE) error:
D:\Robert\Documents\Visual Studio 2013\Projects\ShortcutTest\Product.wxs(27,0): error LGHT0204: ICE57: Component 'ShortCut' has both per-user data and a keypath that can be either per-user or per-machine.
ICE57 is implying an inconsistency between per-user and per-machine data. But, the key path of the component is HKMU, which in a per machine installation resolves to HKLM (HKEY_LOCAL_MACHINE). The location of the shortcut derives from 'ProgramMenuFolder', which in a per-machine installation resolves to C:\ProgramData\Microsoft\Windows\Start Menu\ (on Windows 8.1). None of the component's resources appear to have any per-user association.
You can build the installer package into an MSI by suppressing ICE57. The resulting MSI package installs without any obvious errors. Multiple users can log on and access the shortcut. Any user can un-install the package and all of the resources in the package are removed.
The answer to Wix create non advertised shortcut for all users / per machine has an interesting workaround, which is to author advertised shortcuts and then turn off advertising. Seems a round about way of creating un-advertised shortcuts.
A common fix for the ICE57 error is to change the <RegistryValue...> root to HKCU (HKEY_CURRENT_USER). However this creates an installer that can leave a user registry key behind when un-installed. For example if user A installs the package, a registry entry is added to user A's registry hive. If user B removes the package, the registry entry is not removed from user A's registry hive.
In this scenario is the ICE57 error a bug in the Internal Consistency Evaluators? Or is there something I have miss-understood?
While researching another problem I found this comment on http://sourceforge.net/p/wix/mailman/message/26687047/ from Rob Mensching:
IIRC, this is a bug in ICE57. The Windows Installer team didn't look at
ALLUSERS property when evaluating these values... that was a long time ago
though so my memory may have decayed a bit.
It looks like a bug in ICE57.
Move your Shortcut to the be a child of File and add the Adversite="yes" attribute. The RegistryValue should do the trick for converting the shortcut from perUser to perMachine.
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MyApp" />
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ApplicationProgramsFolder" Name="My App Name" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ComponentGroup_Core">
<Component Id="Component_App" Guid="INSERT_GUID_HERE" Directory="INSTALLFOLDER">
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[AppName]"
Name="AppInstalled" Type="string" Value="yes" KeyPath="yes"/>
<File Id="MyApp" Name="My Test App.txt">
<Shortcut Id="Shortcut"
Name="Open Text File"
Description="Opens a text file"
Directory="ApplicationProgramsFolder"
WorkingDirectory="INSTALLFOLDER" />
</File>
</Component>
<Component Id="Component_MenuFolder" Guid="INSERT_GUID_HERE"
Directory="ApplicationProgramsFolder">
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[AppName]"
Name="MenuFolderInstalled" Type="string" Value="yes"
KeyPath="yes"/>
<RemoveFolder Id="RemoveFolder_App" On="uninstall" />
</Component>
</ComponentGroup>
</Fragment>
I have my bootstrapper project and I need to add a shortcut only when another third part msi is selected on bootstrapper's UI. So I end up with another little msi like this (removing not relevant data):
<Wix >
<Product >
<Package />
<MajorUpgrade />
<MediaTemplate />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="FolderName">
</Directory>
</Directory>
</Directory>
<Component Id="ApplicationShortcut" Guid="PUT-GUID-HERE" Directory="ApplicationProgramsFolder">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="App"
Description="desc"
Target='"[ProgramFiles64Folder]Folder1\Folder2\app.exe"'
Arguments=' -n name'
/>
<RemoveFolder Id="RemoveProgramMenuDir" Directory="ApplicationProgramsFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\ACME\App" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
<Feature Id="ProductFeature" Title="Shortcuts" Level="1">
<ComponentRef Id="ApplicationShortcut" />
</Feature>
</Product>
But I get
ICE71: The Media table has no entries.
That source doesn't compile anyway because of issues with missing languages, productcodes and so on. It would have helped if you'd posted a complete working example. After fixing those issues and seeing ICE71, you basically need to delete mediatemplate and add a proper media entry such as (in angle brackets)
Media Id="1" Cabinet="product.cab" EmbedCab="yes"
then all you get is warning LGHT1079 : The cabinet 'product.cab' does not contain any files. If this installation contains no files, this warning can likely be safely ignored. Otherwise, please add files to the cabinet or remove it.
I would just augment the third party MSI with a transform that creates a shortcut when that MSI is installed. This way you don't have to do anything special in the bootstrapper.
After installation of perUser msi package I cannot uninstall it.
When selecting (Browse) package - that I installed - there is an error: 'selected package is not valid package for this product'.
Here's my code:
<?xml version='1.0'?><Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Id='a871a539-5954-44b7-810d-caed5d09e4c5' Name='x' Language='1033'
Version='1.1.0.0' Manufacturer='M' UpgradeCode='a871a539-5954-44b7-810d-caed5d09e4c5'>
<Package Description='x'
Comments='x'
Manufacturer='x' InstallerVersion='200' Compressed='yes' InstallScope='perUser' />
<Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='AppDataFolder' Name='AppData'>
<Directory Id='xFolder' Name='x' />
</Directory>
</Directory>
<Component Id='xComponent' Guid='a871a539-5954-44b7-810d-caed5d09e4c5'
Directory='xFolder'>
<RemoveFolder Id="Removex" On="uninstall" />
<!-- registry entry to be used as keypath -->
<RegistryValue Root="HKCU"
Key="Software\M\x"
Name="component.xcomponent.installed"
Type="integer"
Value="1"
KeyPath="yes"/>
<File Id='myFile' Name='myFile.txt' DiskId='1' Source='myFile.txt' />
... files here
</Component>
<Feature Id='xFeature' Title='x feature' Level='1'>
<ComponentRef Id='xComponent' />
</Feature>
Installation process is ok - no errors. Files are in correct place. Registry key is added.
What am I doing wrong?
Yes - as Wim Coenen said - that question helped me resolve problem.
However instead of executing steps in that question I cleaned registry.
The problem was that I installed this package many times changing GUID and other parameters, so there was quite a mess in registry. I searched registry by Manufacturer and Name.
After cleaning up everything was ok.
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?
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!
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
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.