How to find previously installed location - wix

I am using Wix Burn to upgrade our program with some prerequisites. The program needs to be installed in the same location that previously installed. How can I find the installed location? I am thinking about to find it from the registry Uninstall folder, however, sometimes it is not even there. Even if it is there, how do I get it from Burn?

You can use AppSearch to locate components installed by the previous installation. You can then set the installation directory dynamically by using the results of that ComponentSearch. All of this is done in the msi itself, burn is not involved in that process. The only thing you have to do with the burn bundle is to change the version number and to make sure that the UpgradeCode of the previous bundle matches the UpgradeCode of the current bundle. Bear in mind that, unlike in msi packages, the last part of the version number of a burn bundle is significant. You will also need to change the version number of the msi.
To make this work, it is necessary the previous bundle installed a component of which the GUID is known to you, and that component needs to reside inside the folder you want to upgrade.
<!-- ComponentSearch -->
<Fragment>
<Property Id="PREVIOUS_INSTALL_FOLDER">
<ComponentSearch Id="MyComponentSearch" Guid="KNOWN_GUID_OF_PREVIOUSLY_INSTALLED_COMPONENT" />
</Property>
</Fragment>
<!-- Increment Product Version, Reference Component Search, Set Target Location -->
<Product
...
Version="INCREMENTED_VERSION_NUMBER"
UpgradeCode="HAS_TO_MATCH_PREVIOUSLY_INSTALLED_UC">
<SetProperty
Id="DYNAMIC_PROGRAM_LOCATION"
Value="[PREVIOUS_INSTALL_FOLDER]"
After="AppSearch" Sequence="ui" Action="SetDynamicProgramLocation">
<![CDATA[PREVIOUS_INSTALL_FOLDER]]>
</SetProperty>
</Product>
<!-- Folder Layout -->
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="DYNAMIC_PROGRAM_LOCATION" />
</Directory>
</Directory>
</Fragment>

Related

How to avoid having two versions of a product installed with Windows Installer / MSI?

I have written and am maintaining a number of Wix installation source files which I use to build MSI files for distribution of my application.
I have not explicitly programmed for any kind of upgrading, updating, reinstallation or anything of the kind -- there is a single feature that consists of a number of components with stable GUIDs and I have observed that at least a clean installation does what I expect it to.
However, I (and anyone in possession of the MSI files I distribute) may seemingly install distinct versions of my application side-by-side using their respective (distinct) MSI files. Which isn't a problem in itself, except that I obviously use the same folder as installation target -- "%ProgramFiles(x86)%\Foobar" -- to install the application (version regardless) in. Meaning that in effect there is always ever one version that ends up being installed.
I would argue that Windows Installer behaves correctly in so far that it updates files from whichever MSI package it installed last. One interesting side-effect of this is that if the last MSI was of earlier version, the files in the application folders would be overwritten with the copies from that earlier version.
But none of that seems to be the actual problem to me. I want to fix the disparity between what is actually installed (a single application version) and what Windows tracks as installed -- in my case two records of two distinct application versions.
Since I install the application in a folder that doesn't depend on the version being installed, tracking multiple application versions by Windows is a mistake.
So I guess my question is, how do I fix this so that only one version is shown (reflecting reality) or what's the idiomatic approach in these kind of cases? I deliberately did not overspecify my Wix source code, hoping in return that Windows Installer would use some built-in intelligence to figure everything out on its own. But I may need to add some explicit upgrade or uninstall-previous-version-first instructions, I suppose.
My minified Wix source code (file "foobar.wxs") would look like this:
<?xml version="1.0" encoding="utf-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Name="Foobar" Manufacturer="ACME Inc." Id="*" UpgradeCode="ae9a7d6d-6c2d-446a-97d9-9dbe829d2ea8" Language="1033" Codepage="1252" Version="!(wix.PRODUCT_VERSION)">
<Package Id="*" Languages="1033" SummaryCodepage="1252" Compressed="yes" InstallerVersion="200" />
<Icon Id="foobar" SourceFile="!(wix.APPPATH)/foobar.ico" />
<Property Id="ARPPRODUCTICON" Value="foobar" />
<Property Id="ARPCOMMENTS" Value="Gives you full foobar powers" />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" />
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="Foobar" FileSource="!(wix.APPPATH)">
<Component>
<File Id="foobar.exe" Name="foobar.exe" />
</Component>
<!-- There are other components like above (assets) -->
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="foobar_menu" Name="Foobar">
<Component Id="foobar_shortcut" Guid="e80a6b95-a145-453a-b327-65a977e741fe">
<Shortcut Icon="foobar" Id="foobar_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<Shortcut Directory="DesktopFolder" Icon="foobar" Id="foobar_desktop_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<RegistryValue KeyPath="yes" Root="HKMU" Key="Software\[Manufacturer]\[ProductName]" Type="string" Value="" />
<RemoveFolder Id="remove_foobar_menu" On="uninstall" />
</Component>
</Directory>
</Directory>
<Directory Id="CommonAppDataFolder">
<Directory Id="app_data_foobar" Name="foobar">
<Component Guid="" Id="app_data_config_folder">
<CreateFolder />
</Component>
<Component Guid="" Id="app_data_config_folder_log_file">
<File Name="foobar.log" Source="foobar.log.template">
<!-- Add write access permission to the log file to members of "Users" group. -->
<!-- PermissionEx Sddl="D:AR(A;;GWGR;;;BU)" / -->
<!-- Bug with Windows Installer, can't use PermissionEx/MsiLockPermissionsEx table. See https://stackoverflow.com/questions/55145282/how-to-include-inherited-permissions-when-specifying-permissions-for-a-file-inst -->
<util:PermissionEx Append="yes" GenericWrite="yes" User="Users" />
</File>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="foobar">
<ComponentGroupRef Id="foobar" />
<ComponentRef Id="foobar_shortcut" />
<ComponentRef Id="app_data_config_folder" />
<ComponentRef Id="app_data_config_folder_log_file" />
</Feature>
</Product>
</Wix>
I am compiling the object file with the following Windows Command Prompt line:
candle.exe -ext WixUtilExtension -out %TEMP% foobar.wxs
And then generating the MSI file with:
light.exe -ext WixUtilExtension -spdb "-dAPPPATH=%apppath%" "-dPRODUCT_VERSION=%version%" -out %TEMP%\foobar-%version%.msi %TEMP%\foobar.wixobj
(using Wix 3.11.1.2318)
Upgrade Code: As long as you have set an upgrade code (which identifies a bunch of related products) you can use a major upgrade element to indicate products that are to be uninstalled as part of a new MSI's installation.
MajorUpgrade Element: Just inject a MajorUpgrade element for default treatment of major upgrades into your existing WiX source. It is a sort of "magic element" doing a lot for you making a number of (usually good) assumptions. There are older and more flexible ways to do it - if you need more detailed control (for legacy purposes usually - auto-magic does not cover all bases):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
The above is the standard use for all WiX files created in Visual Studio.
Note: I will try to tune up this answer shortly with more links, but give that a go first?
First link: Using Visual Studio to make WiX files. The Hello WiX and Visual Studio-type of scenario.
Major Upgrade Suggested Reading: A few things to know about major upgrades. All WiX markup essentially revolves around the compiled MSI's Upgrade table. It is there that major upgrade logic is configured. Custom actions could also affect things, and a few other things such as launch conditions perhaps.
WiX Documentation: How To: Implement a Major Upgrade In Your Installer
Major Upgrade - Common Problems: WIX does not uninstall older version
Major Upgrade - Manual Configuration: Adding entries to MSI UpgradeTable to remove related products (using old-style Upgrade elements)
Further:
Major Upgrades - How-To & Concept: Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs

How can i identify the current logon user in Wix toolset?

I'm trying to build an installer with Wix and i have to put a file in the Startup folder. I already found out how to build the path to the startup folder but i can't find any variabile that can identify the current user.
That's what i have done for now and it works but the part with the name of the user just creates a new directory with that name
There are a couple of things probably wrong with your idea:
You shouldn't need to build that entire directory tree to get to the StartupFolder because there is already a standard Windows Installer property named StartupFolder. This is already the path to the current user's startup folder so it's not clear why you need the value of LogonUser.
Properties are resolved by placing them in square brackets, so in the general case you'd use [LogonUser], but directory names in the Directory table are not marked as Formatted type, so calling a directory [LogonUser] won't work. You'd need to set another public property to the value of [LogonUser] and then use that property as a directory name. However, I think point 1. may be all you need, and your directory tree isn't clear about your intent.
Run on Startup
Normally you would put a shortcut to a file in the startup folder, and not an actual file. You do so by refering to the built-in Windows Installer Property StartupFolder as shown in the mockup-sample below (and as stated by Phil).
In the realm of alternatives, there are many ways to schedule something to start with Windows. What type of file is this and what does it do? In case you are interested, you can see a number of ways used to start something on login or boot by running AutoRuns (from SysInternals). There is a shocking array of possibilities (small digression).
Very often you can run things as services or scheduled tasks, rather than using other startup features. Generally services for features that need to run continuously, and scheduled tasks for stuff that needs to run every now and then. I think most people want to avoid too many things running on login - if they are not really necessary. I find the startup folder "clunky" - and also prone to user interference as well.
Self-Repair and the Startup Folder
This Experts-Exchange article describes a case when self-repair was triggered after deleting a startup folder entry (search for "startup" to find the section).
Frankly I am a bit surprised at the described scenario. When a shortcut is deleted, it should not come back automatically easily, since it is generally not the key path of its hosting component. Still, something to check when you test your MSI (delete the shortcut and then launch your app directly - if there is a shortcut to do so). If you see the problem, please let us know.
If I were to guess what really happened, they might have installed an actual file into the shortcut folder and set it as the key path (which is what it seems you are trying to do as well). Then they have put this in the same feature hierarchy as an advertised shortcut - the same feature or the top feature of the application, or a parent feature - causing self-repair to always be invoked when the advertised shortcut is invoked, and the missing file is detected in the Startup folder and self-repair ensues.
Digression: a sizeable digression, the important point is to please check this for your setup! This kind of problem really aggravate your users - the cause of it tends to elude their support guys.
Mockup WiX Sample
Here is one sample for how to install a shortcut to the Startup folder. Note that the Startup folder redirects depending on whether the setup is installed per-user or per-machine, as documented on MSDN: StartupFolder.
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Startup Shortcut" Manufacturer="Someone" Version="0.0.1"
Language="1033" UpgradeCode="PUT-GUID-HERE">
<Package InstallScope="perMachine" Compressed="yes" />
<Media Id="1" Cabinet="my.cab" EmbedCab="yes" />
<UIRef Id="WixUI_Mondo" /> <!-- Just include a default setup GUI -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="MyCompany" Name="Company">
<Directory Id="MyAPP" Name="MyApp">
<Component Feature="MyFeature">
<File Source="MyApp.exe" />
<!-- Set Advertise="no" to avoid advertised shortcut -->
<Shortcut Id="MyApp" Directory="StartupFolder" Name="MyApp"
Advertise="yes" />
</Component>
</Directory>
<Directory Id="StartupFolder" />
</Directory>
</Directory>
</Directory>
<Feature Id="MyFeature" Absent="disallow" />
<Property Id="MSIFASTINSTALL" Value="7" /> <!-- Tweak to install faster -->
</Product>
</Wix>
This should be a property set automatically in the installer at run time, LogonUser.

How do I install to LocalAppData folder?

Following directory setting works perfectly for me.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ProgramFilesFolder">
<Directory Id='INSTALLDIR' Name='MyApp'/>
</Directory>
</Directory>
However, when I tried changing "ProgramFilesFolder" to "LocalAppDataFolder", I got lots of error when using light to link and generate my msi:
D:\runGroup.wxs(53) : error LGHT0204: ICE38: Component cmpA5561BE36D80EB58252E69DDA0C2FF8C installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
D:\main.wxs(38) : error LGHT0204 : ICE64: The directory INSTALLDIR is in the user profile but is not listed in the Remove File table.
Looks like "LocalAppDataFolder" is not acceptable for WiX, while I believe it is one of the system folder properties which defined in here.
What am I supposed to use for LocalAppData folder?
I converted an application from being a perMachine install to be a perUser install. In order to properly convert the install I had to add a registry key for each of the components I have.
Originally I had the following:
<Component Id="C.MyExe">
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)" Source="$(var.MyExe.TargetPath)" DiskId="1">
<Shortcut Id="SC.StartMenu"
Directory="D.ApplicationMenuDir"
Name="$(var.AppName)"
WorkingDirectory="INSTALLDIR"
Icon="MY_ICON.ico"
IconIndex="0"
Advertise="yes"
/>
...
When I moved the exe component to the user install I had to do something like this:
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="MyAppDirectory" Name="$(var.AppName)">
<Component Id="C.MyExe" Guid="{MY_GUID}">
<CreateFolder />
<RemoveFolder Id="RemoveMyAppDirectory" On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)"
Source="$(var.MyExe.TargetPath)" DiskId="1" Checksum="yes">
</File>
</Component>
...
The most important part is that you will have to add a registry key which points to HKEY_CURRENT_USER. I added a registry value for each component which indicates that the component is installed.
I also had to remove the following: Advertise="yes".
I had this problem recently. I wanted to convert my installer from per-machine to a per-user but was getting ICE38. I asked on wix-users and one opinion was that you can ignore ICE38 because that was meant as a check for per-machine installs.
See the discussion at wix-users.
Since that is the case, ICE38 is (in my opinion) incorrect and you will want to ignore it. ICE38 implies you are installing per-user resources in the context of a per-machine installation but never verifies that this is so.
Actually authoring a per-user install requires that you ignore ICE38
because it won't ever be accurate for that world.
[Edit]
Looks like you got help here.
From Peter Shirtcliffe:
This is my own, admittedly inexpert, understanding of per-user installations:
Installing to subdirectory of LocalAppDataFolder is perfectly OK in a
per-user MSI. Because of certain scenarios relating to roaming users, you
need to add components containing elements for any
directories you create under LocalAppDataFolder. That's why ICE64 is
appearing.
The ICE38 error is slightly misleading: since you have a per-user
installation, it's safe to ignore as long as the user cannot pick an
alternative installation location that is common to all users. ICE38 is
checking for the situation where multiple users all install the same
component to the same path.
Just posting to help other people (like me).
Ok, just found that we can do it by overwriting "ProgramFilesFolder":
<SetProperty Id="ProgramFilesFolder" Value="[LocalAppDataFolder]" Before="CostFinalize"><![CDATA[NOT Privileged]]></SetProperty>
Another thing to do is, in <Package> we need to set InstallPrivileges to limited.
Well, I can see no reason why "ProgramFilesFolder" can be used directly while "LocalAppDataFolder" can't.
Are you installing per-user or per-machine? Also, what OS versions are you targetting? You might want to read:
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7

WIX installer root directory and versioning

I have created a silent installer Using WIX for my application. I want it to install my application to C:\MyApps folder but its Directory Id='TARGETDIR' Name='SourceDir' tag randomly picks C or D drive. I want to enforce my installation to C drive only. Also in case, I provide version number greater than 4.0.5, I am geting an error during installation saying "This installation package cannot be installed by the Windows Installer Service. You must install a newer version of the Window Installer service." I am having Windows XP professional SP3 Version 2002.
To begin with, I think you should start your WiX journey with the tutorial available here. It contains the answers to the most of basic questions you'll face with the first thing. You should also be aware that understanding WiX means understanding the concepts of Windows Installer first - otherwise some points will seem a weird magic to you.
When you create a new WiX setup project in Visual Studio, it generates a template with some placeholders. It is recommended to start modifying this template. For instance, the directory structure:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent" Guid="ba7d579f-5234-4448-b880-109f589d58e5"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</Directory>
</Directory>
</Directory>
This snippet defines the INSTALLLOCATION folder under the ProgramFileFolder, and this is a better approach than to place it under the C:\ root. You can still change the installation location by modifying the INSTALLLOCATION property at install time (for instance, base on user's input).
The quick answer to your questions are:
...randomly picks C or D drive...
That's expected - it picks the drive with the most free space by the time of installation. If you stick to the way WiX template defines by default, it will fall under C: (actually, under Program Files folder).
...You must install a newer version of the Window Installer service...
Basically, it means what it says - the version Windows Installer on your machine is lower than the one you require in your package. If you try to solve the above problems with this change, then it has nothing to do with the Windows Installer version. You should require higher version than it is specified by default only in case you are going to use new features of Windows Installer.
Hope you'll make right conclusion out of this brief intro - start with the tutorial. :-)
The problem with your versions is you're changing the Windows Installer version when you change your Product version.
<Package
Id='*'
InstallerVersion='406'
Compressed='yes'
Description="Installer Number 406" />
The InstallerVersion attribute should be the minimum required version of Windows Installer required to install this package. You have Windows Installer v4.5 installed. When this is set to 406, it looks for Windows Installer v4.6 which frankly, doesn't exist. Setting this to 301 (version 3.1) is usually sufficient.
InstallerVersion='301'
While your description attribute is fine, I would find the following more meaningful:
Description="My Product v4.0.6 Installer"
Try this:
<Fragment>
<Property Id="_BrowseProperty" Value="INSTALLDIR" Secure="yes"/>
<CustomAction Id="SetDataLocationDefault" Property="INSTALLDIR" Value="[WindowsVolume]$(var.Title)\" />
<InstallUISequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="$(var.Title)">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
</Fragment>
I think this should work!
Do not rely on TARGETDIR, and use the custom property, like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
The template is taken from Yan's answer. Set INSTALLLOCATION to the desired folder C:\MyApps, that should do the trick.

WiX Conditional Feature/Component Orphaned on Uninstall

Edit: Quoting myself because I summarized the issue much better in one of the comments below…
I have a condition that is true when the package is installed, but
not true when it is removed. I expected MSI to remember that it had
installed the conditional component and remove it with the uninstall,
but this is not the case. I am trying to find out A) the proper way to
clean up this orphaned component, and B) the best way to protect
against this problem in the future.
I guess my question boils down to, is it safe to just delete an orphaned feature/component after a product is uninstalled? And is there any way to check what, if anything, is still referencing a component that I believe to be an orphan? And how do I fix my installer to keep this from happening in the future?
We have a wix project to install a library, Foo. This installer puts copies of Foo.dll into the GAC, and a folder, Program Files\Reference Assemblies\Foo\<version> by default. The installer also adds two registry keys, one is a custom key which stores the path of the Foo folder for reuse in future installs, the other tells Visual Studio to include the full <version> folder path in its search for installed libraries so that Foo shows up in the “Add References” dialog. Multiple versions of the Foo library can be installed on the machine at a time, each will be located in the appropriate <version> folder under Foo.
Foo 2.0.0 had a bug that slipped through testing, Foo 2.0.1 contained the bug fix, no other changes. It was decided that since the bug fix was the only change, we would add a policy file to the GAC which would redirected references for Foo 2.0.0 to Foo 2.0.1. This policy file was added to the installer as a new component inside of a new feature. An upgrade tag was added to detect and remove Foo 2.0.0 when Foo 2.0.1 was installed. The installation of the policy feature was made conditional on Foo 2.0.0 being detected. Everything seemed to be working and Foo 2.0.1 was pushed out.
Now, a year later, we discover that we again missed noticing a bug, this time in the installer setup rather than the library code. It turns out that when Foo 2.0.1 replaces 2.0.0, and is then uninstalled, the policy file is orphaned and remains in the GAC while all other files and keys are removed. I have tested this on a clean install of windows (virtual machines can be so useful) and confirmed that the problem can be replicated, i.e. no additional references to the component have snuck in to cause it to stay behind.
All of this was originally done in WiX 3.0 but we have recently moved up to using WiX 3.5. Our WiX code looks like this:
<Product Id="Guid 1" Name="Foo v2.0.1" Language="1033" Version="2.0.1" Manufacturer="My Team" UpgradeCode="Guid 2">
<Package InstallerVersion="300" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Upgrade Id="Guid 2">
<UpgradeVersion Minimum="2.0.0" Maximum="2.0.0" IncludeMaximum="yes" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADE2X0X0"></UpgradeVersion>
</Upgrade>
<Property Id="FOODIR">
<RegistrySearch Id="FooPath" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="RefAssemb" Name="Reference Assemblies">
<Directory Id="FOODIR" Name="Foo">
<Component Id="FooLibPath" Guid="Guid 3">
<RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
<RegistryValue Name="InstallPath" Type="string" Value="[FOODIR]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOVERSION" Name="v2.0.1">
<Component Id="Foo_VSFile" Guid="Guid 4">
<File Id="Foo_DLL" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes"></File>
</Component>
<Component Id="Foo_VSRegKey" Guid="Guid 5">
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v3.5\AssemblyFoldersEx\Foo v2.0.1" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="[FOOVERSION]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOGAC" Name="GAC">
<Component Id="Foo_GAC" Guid="Guid 6">
<File Id="Foo" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes" Assembly=".net"></File>
</Component>
<Component Id="Foo_Policy_2x0x1" Guid="Guid 7">
<File Id="Foo_PolicyDLL" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.dll" KeyPath="yes" Assembly=".net"></File>
<File Id="Foo_PolicyConfig" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.config" CompanionFile="Foo_PolicyDLL"></File>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFoo" Level="1">
<ComponentRef Id="Foo_GAC"/>
<Feature Id="Foo_VSSupport" Level="1">
<ComponentRef Id="FooLibPath"/>
<ComponentRef Id="Foo_VSFile"/>
<ComponentRef Id="Foo_VSRegKey"/>
</Feature>
<Feature Id="Foo_Policy_v2x0x1" Level="0">
<ComponentRef Id="Foo_Policy_2x0x1"/>
<Condition Level="1">UPGRADE2X0X0</Condition>
</Feature>
</Feature>
</Product>
is it safe to just delete an orphaned feature/component after a
product is uninstalled?
No, it's not. If you just delete it, its component registration information is still left on the machine.
And is there any way to check what, if anything, is still referencing
a component that I believe to be an orphan?
Not really. But if there is something referencing one of your components, it's most likely another product developed by you or an older version of your current product which wasn't uninstalled correctly.
It's very unlikely that a random product would reference your component or assembly.
And how do I fix my installer to keep this from happening in the
future?
Use major upgrades which uninstall the old component and install the new one. No special policy files, no conditional installations or removals.
Multiple versions of the Foo library can be installed on the machine
at a time, each will be located in the appropriate folder
under Foo.
Why? If you have a single product, you can use major upgrades. This way the user will have only one version installed with only one version of your assembly.
Versioned assemblies installed side by side make sense only for different products.
It was decided that since the bug fix was the only change, we would
add a policy file to the GAC which would redirected references for Foo
2.0.0 to Foo 2.0.1. This policy file was added to the installer as a
new component inside of a new feature.
This is a hack and most likely this is what is causing the problem. Your new installed should have uninstalled the old version along with Foo 2.0.0.
Major upgrades should always be standalone.