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

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!

Related

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.

Decoupling Wix Component from ComponentGroup

I'm trying to generate Wix source from a custom Visual Studio extension. As such, I'd like to be able to (somehow) just add one file (plus project reference) to the Wix Project, and have the new DLLs added in to the Product.
As an example:
<Product Id="*" Name="blah" Version="..." Manufacturer="foo" UpgradeCode="...">
<Package InstallerVersion="200" ... />
<MajorUpgrade DowngradeErrorMessage="..." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="blah" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!-- Custom actions, Directories, etc .... -->
</Product>
Then in separate files (which I want to be generated), I have some Fragments:
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE1" Guid="...">
<File Id="FILE1" Source="file1.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
and
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE2" Guid="...">
<File Id="FILE2" Source="file2.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
So far so good. The problem is that I need to tie those together with something like:
<Fragment>
<ComponentGroup Id="ProductComponents">
<ComponentRef Id="CMP_FILE1" />
<ComponentRef Id="CMP_FILE2" />
</ComponentGroup>
</Fragment>
That works, but I don't want to do that, because it requires editing of the ComponentGroup when I want to add the next file.
So I want to try to localise the information into my added file. I can live with it always being part of the same Feature.
I tried adding the Feature attribute to Component element:
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE1" Guid="..." Feature="ProductFeature">
<File Id="FILE1" Source="file1.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
but that didn't seem to add the Component to the parent feature (empty Media table warning from Wix on build, and Orca confirmed it).
I also tried adding the ComponentGroup to each generated file, but of course I can't duplicate Id attributes, and unique Id just pushes the coupling problem up into Feature...
Is there a way to add a Component without editing the ComponentGroup?
No. But you could generate (rather than edit) your ComponentGroup[#Id="ProductComponents"]. The file where it is defined can be "hidden" by generating it into the obj folder and dynamically adding it to the compile. This is effectively what HarvestDirectory and the other targets that call heat do.
While your extension is adding project references and files into the project, it can also add an MSBuild Include that defines a Target with BeforeTargets="Compile". That Target can do the generation and add the generated file to the Compile ItemGroup.
You just have to have a contract that the extension will use a particular ComponentGroup Id for this purpose. (Heat uses unique names for component and file ids to prevent conflicts. I suggest you do that too, especially for "hidden" source files.)

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.

Wix installer and disabled features

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.