Wix installer and disabled features - wix

I'm using Wix3 beta with Feature Tree UI. I'm installing several assemblies as separate components into a custom subdirectory inside ProgramFiles, as well as into GAC. Additionally I would like to package DEBUG versions of the same assemblies as one component and let the user decide whether to install them or not. Now this feature with debug assemblies is disabled by default, but the debug assemblies are installed regardless.
Below the relevant snippet:
<DirectoryRef Id="INSTALLDIR">
<Directory Id="bin" Name="bin">
<Directory Id="Debug" Name="Debug">
<Component Id="DebugComponent" Guid="PUT-GUID-HERE">
<File Id="DebugAssemblyXXX" DiskId="1" Source="Debug\XXX.dll"></File>
</Component>
</Directory>
<Directory Id="Release" Name="Release">
<Component Id="ReleaseComponent" Guid="PUT-GUID-HERE">
<File Id="ReleaseAssemblyXXX" DiskId="1" Source="Release\XXX.dll"></File>
</Component>
</Directory>
</Directory>
</DirectoryRef>
<Feature Id="All" ConfigurableDirectory="INSTALLDIR" Title="Title" Level="1"
Display="expand" AllowAdvertise="no" Absent="disallow" Description="Desc">
<Feature Id="DebugAssemblies" Title="Debug Assemblies" Level="1000" Absent="allow"
AllowAdvertise="no" Description="Debug versions of assemblies.">
<ComponentRef Id="DebugComponent" />
</Feature>
<Feature Id="ReleaseFeature1" Title="Feature" Level="3"
AllowAdvertise="no" Description="Another description">
<ComponentRef Id="ReleaseComponent"/>
</Feature>
</Feature>
The weird thing is that if I run the msi file again and go to "Change" and disable the Debug feature, the Debug assemblies will be deleted, e.g. the logic works fine this time.
The default INSTALLLEVEL is 3.
Any suggestions?

In case somebody else gets stuck with this: apparently the top-level feature should not be named "All" as in my case - it might have some default meaning to Wix/Windows Installer. Upon renaming it to something else everything works as expected.

Related

Installer (wix built msi) does not remove a feature element (a directory) during uninstallation

I am having issues getting my installer (msi) to remove a feature based component during uninstallation. The feature element, as per below, contains conditional component group references, which are to a directory id and a number of other components in another fragment (e.g., iis:WebAppPool):
Product.wxs
<Feature Id="StandardIntegration" Level="1" Title="StandardIntegration">
<ComponentGroupRef Id="SERVICES"/>
<!-- Integration Services IIS -->
<ComponentRef Id="comp1"/>
<ComponentRef Id="comp2"/>
<ComponentRef Id="comp3"/>
<ComponentRef Id="comp4"/>
<Condition Level="0">
<![CDATA[HAS_FEATURE_SET <> "true"]]>
</Condition>
</Feature>
OtherFragment.wxs
<DirectoryRef Id="INSTALLDIR">
<Directory Id="InstancePath">
<Directory Id="SERVICES" Name="Services"/>
</Directory>
</DirectoryRef>
<Component Id="comp1" Directory="SERVICES" Win64="yes" KeyPath="yes" Guid="$(var.c)" MultiInstance="yes" Shared="no">
<iis:WebAppPool Id="wbpoolid" ManagedRuntimeVersion="v4.0" Name="CMP_[NAME]" User="AppPoolUser" Identity="other" RecycleMinutes="0" RecycleRequests="0" ManagedPipelineMode="integrated">
</iis:WebAppPool>
....
All other components referred (including the ones that contains the iis web app pool element) are removed correctly during uninstallation, but the directory ("SERVICES") is not... and it's the only file directory that's not being removed. (E.g., all other files installed by the msi are being correctly removed.)
Any help would be much appreciated, thanks!

Delete a folder using Wix

I recently tried to create an installer for my application using Wix. When I uninstalled the application all my folders and files were deleted except some created at run time by the application. After some research I found out that the reason was because those folders and files were created after the install proccess. I also found out that what I wanted to use would be the element RemoveFolderEx. Unfortunatly, I am having some trouble to use the tag. This is my current code that doesn't work:
<Feature Id="ProductFeature" Title="App" Level="1">
...
<ComponentRef Id="deeletappdata" />
</Feature>
<Component Id="deeletappdata" Guid="*" Directory="AppDataFolder">
<util:RemoveFolderEx Id="RemoveAppDataAppContent" On="uninstall" Property="AppDataAppFolderDir" />
</Component>
<Directory Id="AppDataFolder">
<Directory Id="AppDataAppFolderDir" Name="$(var.Application)"/>
</Directory>
How can I use the element RemoveFolderEx?

Only remove a folder on uninstall, not upgrade

I am trying to configure an inherited WiX installer for use with my software.
The software logs to day-specific files in C:\SomePath\LogFiles like 2014-05-19.txt, 2014-05-18.txt, and so on (not really relevant to question, but perhaps worth noting that there will be files present that are created by the software, but not by the installer itself).
My WiX installer creates the LogFiles directory like this (lots of elements, GUIDs etc removed for readability):
<Wix>
<Product Id="SOME_GUID" Version="SOME_VERSION" UpgradeCode="OTHER_GUID">
<Feature Id="EMPTY_DIRECTORIES" Title="Empty Directories" Level="1" Display="hidden">
<ComponentRef Id="SomeFolder" />
<ComponentRef Id="LogFiles" />
<ComponentRef Id="SomeOtherFolder" />
</Feature>
<DirectoryRef Id="DIR_LOG_FILES">
<Component Guid="" Id="DELETE_DIR_LOG_FILES">
<RemoveFile Id="DELETE_DIR_LOG_FILES_FILES" Name="*.*" On="uninstall" />
<RemoveFolder Id="DELETE_DIR_LOG_FILES" On="uninstall" />
</Component>
</DirectoryRef>
</Product>
<Fragment>
<Directory Name="SoftwareName" Id="SOFTWARENAME">
<Directory Id="DIR_LOG_FILES" Name="LogFiles">
<Component Id="LogFiles" KeyPath="no" NeverOverwrite="no" Permanent="no" Win64="no" Location="local">
<CreateFolder>
<util:PermissionEx CreateChild="yes" CreateFile="yes" Delete="yes" Read="yes" ReadAttributes="yes" ReadExtendedAttributes="yes" ReadPermission="yes" Traverse="yes" GenericRead="yes" GenericWrite="yes" User="Everyone" />
</CreateFolder>
</Component>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="LoggingComponents" Directory="WHERE_THE_LOGGING_DLLS_LIVE">
<ComponentRef Id="DELETE_DIR_LOG_FILES" />
</ComponentGroup>
</Fragment>
</Wix>
I had hoped that this setup would cause the directory and contents to be deleted only on uninstall. Unfortunately the deletion seems to trigger on upgrade as well. Is there a way to configure WiX to tell the difference and react appropriately?
In your build process do you update the version of the dll's you want to produce as that should overwrite it. Do you have any remove file/folder tags as they could also cause an issue.

WiX merge module dependencies

I have an issue with some existing installers which I have repeated in a simple test case as follows:
Installer1 installs App1 and LibraryA(v1).
Installer2 installs App2, LibraryA(v2) and LibraryB. LibraryA(v2) requires LibraryB, which is why LibraryB is now installed. LibraryA(v1) had no such dependency. LibraryA(v2) should overwrite LibraryA(v1).
If I run Installer1 then Installer2, then uninstall Installer2, LibraryB gets removed, but LibraryA remains at v2 (sensible - I wouldn't expect it to revert to a prior version).
Forgetting my existing problem and imagine I was starting from scratch, how would you suggest I construct my WiX project to cope with such a situation? In my case, all of the libraries are defined as merge modules - something I'm not in a position to change (wixlibs are out of the question).
I have tried, to no effect, to use a Dependency element to create a dependency between the LibraryA(v2) merge module and the LibraryB merge module - it just seems to issue a linker warning if I forget to reference LibraryB in the installer, rather than creating an actual dependency.
The .wxs scripts in my test case look something like this (they all install to the same folder for ease of testing):
LibraryA(v1).wxs:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="LibraryAComponent" Guid="d98dd742-c3d3-4aee-8d84-87f2b3c837dc">
<File Source="v1\LibraryA.dll" />
</Component>
</Directory>
</Directory>
LibraryA(v2).wxs:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="LibraryAComponent" Guid="d98dd742-c3d3-4aee-8d84-87f2b3c837dc">
<File Source="v2\LibraryA.dll" />
</Component>
</Directory>
</Directory>
<Dependency RequiredId="LibraryBMergeModule.DD524F28_EAE0_47B8_A895_3AF2F7A7361A" RequiredLanguage="1033"/>
LibraryB.wxs:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="LibraryBComponent" Guid="46e6e0da-2a99-4f0d-bed2-e764e16b9eed">
<File Source="LibraryB.dll" />
</Component>
</Directory>
</Directory>
App1.wxs:
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" DiskId="1" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="WiXTest" Name="WiXTest">
<Merge Id="LibraryAv1" Language="1033" SourceFile="LibraryAv1.msm" />
<Component Id="App1Component" Guid="93D11AFF-5307-4355-B261-0096775B6A89">
<File Source="App1.exe" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="Libraries" Title="Shared Files" Level="1">
<MergeRef Id="LibraryAv1" />
</Feature>
<Feature Id="App" Title="Application" Level="1">
<ComponentRef Id="App1Component" />
</Feature>
App2.wxs:
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" DiskId="1" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="WiXTest" Name="WiXTest">
<Merge Id="LibraryB" Language="1033" SourceFile="LibraryB.msm" />
<Merge Id="LibraryAv2" Language="1033" SourceFile="LibraryAv2.msm" />
<Component Id="App2Component" Guid="173C71B6-E403-4AC1-894D-06799C6782A4">
<File Source="App2.exe" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="Libraries" Title="Shared Files" Level="1">
<MergeRef Id="LibraryB" />
<MergeRef Id="LibraryAv2" />
</Feature>
<Feature Id="App" Title="Application" Level="1">
<ComponentRef Id="App2Component" />
</Feature>
I'm guessing that this is entirely the wrong way to go about things and am looking for some pointers to bring me back on track. It feels like you need to have a PhD in Windows Installer in order to use WiX correctly.
I am no WIX expert but yes for MSI you need at least a PhD. At first you should know the rules about MSI components:
What Every Developer Should Know About MSI Components
From painful experience I do know that if several MSIs bring in the same components it is no good. If possible I would strive for the single source principle so that your libs are installed always by the same MSI (let it be a infrastructure MSI). Your application MSIĀ“s then simply check if the infrastructure has been installed and that was it.
Coming back to your question what you should do with your merge modules. I would create for each merge module an extra msi (yes customers do not like crowed installed software catalogs) to be sure that if you are in need to service the libraries you have full freedom.
I do not know your software structure but it could be that you will need LibraryA v1 and v2 at the same time so you should think about deploying your libraries to the WinSxS cache or if they are managed the GAC. Or you come up with something similar in your folder structure or file name convention.

Features installed to different locations but referencing the same components

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.