How exactly are files removed during MSI uninstall? - wix

I'd like to know what exactly happens to installed files / components during the uninstall procedure.
For the install and upgrade procedure, reliable documentation exists at MSDN (see File Versioning Rules and Default File Versioning, for example).
Anyways, I couldn't find a documentation of the uninstall remove logic at MSDN or at WiX´s documentation.
So, my question is simple: I would like to know when exactly a file is removed from the system (which isn't always the case - for example if a SharedDLLRefCount exists/remains for that file).
The closest I found was the following MSDN link, which gives some advice, but basically says: "Test it yourself".
This isn't satisfying for me, because I would like to know if I can rely on a - maybe current - behaviour of MSI before I ship any installers using this behaviour to customers.
I'm searching for reliable answers to the following questions:
Under which circumstances - aside from an explicit "permanent" definition or using SharedDllRefCount - will a file/component survive an uninstall Action?
If a DLL has now a higher Version than at the time it was installed (because of hot patching or so) will it safely be removed? Note: I tested this and the current answer is yes, but I need to know if this is expected behaviour and if I can rely on it.

Component referencing for MSI files is done on a Windows Installer component basis - and not based on the old SharedDLL ref count found in the registry here: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.
SharedDLL: Strangely this SharedDLL ref-counter is sometimes also used with MSI, but this is only to provide compatibility with legacy installer and deployment technologies - I will clarify later. Legacy technologies used this SharedDLL counter as the sole way to determine if a file could be uninstalled. As soon as ref-count dropped to 0, the file could be removed.
The actual reference counting for Windows Installer is done based on Windows installer components and not shared dll ref-counters. These components are "atomic installation bundles" of files and registry settings. It is always installed or uninstalled as a whole unit. A component can basically contain "anything", but there are rules with regards to best practice when decomposing the files and registry settings you want deployed into a collection of components. Personally I always use one file per component since this avoids all kinds of problems during Windows Installer upgrades.
Key Path: Essentially each component has a "key-path" - a single file or registry key / value which is used to determine if the component is installed or not. The overall concept of MSI is that there is a 1-to-1 mapping between this absolute component key path and a unique component GUID. The GUID essentially reference counts that absolute path. I explained this in an answer several years ago, and it seems to be a comprehensible explanation for people, perhaps give it a quick read to understand this component referencing in more detail: Change my component GUID in wix?
Merge Modules: This component GUID - for that particular absolute disk or registry location - should be used by all setups that seek to deploy the file or component in question. Window Installer's mechanism to allow this is referred to as a "merge module". This is a partial MSI database that can be merged into several MSI files at build time - allowing the same components to be shared between MSI files with the correct component GUID used in all of them so that reference counting is possible. This allows these shared components to increment the ref-count each time they are installed by different products, and then the component will remain on the system until the ref-count is reduced to 0 as the products that use it are uninstalled in sequence. It should be noted that if the legacy ref-counter at HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs is not 0 at the same time, then the component will not be uninstalled. Nor will it be uninstalled if the component is set permanent or if it was installed with a blank component GUID (a special feature to "install and forget" a component - it is never dealt with again).
GUIDs: So to repeat yet again, one GUID for one absolute path (one GUID to rule them all) - one GUID has an associated ref-counter which counts the number of products that have registered the component, with its associated absolute key-path, for its use. So, as an example three products might register use of a certain component GUID, making its reference count 3, and hence making its associated key-path file or registry value stick around until all 3 products are uninstalled.
SharedDLL Enabled?: Note that the legacy SharedDLL ref-counter is not necessarily enabled for your MSI components. Some tools, such as Installshield, enable a flag to increment the legacy, shared DLL ref-counter for all files installed and you actually have to turn it off for each component to get rid of this behavior. This is in contrast to other tools, such as WiX, which does not default the shared dll ref-counter to be on for all files (I am not sure for what files they do enable it - if any). Advanced Installer also does not enable the SharedDLL ref-count flag for all components (thanks to Bogdan Mitrache for verifying this - see his comment below).
Messed up legacy reference counters - which can happen during development and test installation - may cause a Windows Installer
component that should be uninstalled to be left on disk unexpectedly.
If you see this, check on a clean system to determine if messed up
legacy ref-counters is the problem on your main machine. You then need
to manually tweak the registry to fix the ref-count for your
development machine. That will involve all applicable items under this
key: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.
Not a fun job - I had it happen when using Installshield Developer 7
way back in the day.
Failing to keep a consistent component GUID for each absolute key path will cause mysterious and unpredictable problems such as an MSI
uninstall removing files that are still shared with other products,
but the reference counting has been messed up. The MSI files
mistakenly believe they "own" the shared component and happily deletes
them. A case of mistaken identity (the same absolute path has multiple
component GUIDs pointing to it - each one reference counted to 1).
This is one of the key problems people face with Windows Installer -
hence the advice to stick with one file per component.
Update: Let's be concrete about your specific questions.
You have already pretty much answered the question. The file will remain on disk if the component's ref-count (for the component GUID) is higher than 0 after your MSI file has decremented its "registration" of it on uninstall. It will also remain if its MSI component is set to be permanent, or if it had a blank component GUID, or if the legacy SharedDLL ref-count was enabled for the component (it may not be) and the ref-count here is higher than 0: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs. Those are all the conditions I know about. I suppose there are other aspects such as advertised products, but I am honestly not sure how they will affect uninstall. Advertised products are not really installed, but "installable" by the user on demand. Reading Phil's answer I also recall that transitive components can also be uninstalled in the fashion he describes in his answer - by having the associated condition evaluate to false during reinstall.
Yes, as long as the component GUID has remained stable throught the lifetime of a specific absolute path (the full installation path for a file), then that file can have gone through any number of updates, and the reference count is only incremented if another MSI with another product code also installs it. In other words: if you have delivered 4 updates to your original MSI, and you have maintained a consistent component GUID for a specific file and updated it each time with a new version, then the component ref-count for this file is still 1 - as long as no other MSI has also installed the component - in which case it will be 2 or more, and an uninstall of your product will not uninstall it, but reduce the ref-count by 1.
Please do try to read this answer since it seems to have clarified things for other people: Change my component GUID in wix? (same as recommended above).
Finally I should note that shared components can also be installed via WiX include files - a new concept entirely introduced with WiX. These are like regular include files in C++, you simply define them once and they can be included in several WiX source files at compile time. I have honestly never made use of this, but conceptually it is similar to merge modules - the built-in Windows Installer concept to deal with shared components. There is one important difference though: merge modules are versioned as a whole, whereas WiX include files include files dynamically from your source folder. I find this better, but this is certainly a large discussion and a matter of preference. I will not elaborate this here.
If you are using WiX I would recommend you try to use WiX include files to manage your shared components. They seem - to me - to be a more flexible implementation of merge modules. On a subjective note I have never been a huge fan of merge modules, though they are essential to use if you have lots of shared files to install with different products. Why do I not like merge modules? They seem like an extra complication and a binary blob that needs extra maintenance. In effect they amount to a weird form of static linking - with all the problems we know from regular static linking. This may be getting too subjective, so I will end on that note, but for shared files and components use either merge modules or WiX include files or whatever other construct exist to achieve the same that I am not aware of.

There really are only a few rules that apply, but the difficulty is that they apply in a number of sometimes complicated scenarios.
A resource (a file) is removed if its component id ref count goes to zero and:
a) there is no remaining SharedDllRefcount for it.
b) the component has never been marked permanent in the installing MSI (because that makes it stick and can't be turned off).
c) the component was set to Transitive and an install/maintenance operation takes place that sets the associated property value to "false". Again, this is sticky to the system, not a project setting.
d) it has not been marked as msidbComponentAttributesShared by an install.
Permanent, transitive, component shared, and shared Dll can be set on a component.
Versioned shared files do not revert to the previous binary if product A installs version 1 and product B installs version 2 and then product B is uninstalled. By definition (it not always in practice) shared files need to support older clients.
During a major upgrade with RemoveExistingProducts (REP) at the "end", the upgrade applies file versioning rules for files, and increases the ref counts of components that are already installed (or installs the component if it's new with a ref count of 1). In this kind of upgrade the components are effectively shared with the same component ids that are already installed by the older product. When REP uninstalls the older product the ref counts are decremented.
So in the simplest case of an upgrade containing all identical component ids, no files are removed: if component ids A, B, C and D are in the older installed product and component ids A, B, C, and D are in the new upgrade then file versioning rules were applied and decrementing ref count leaves the files there, perhaps with higher version, when REP removes the older product. This why component rules must be followed with this kind of upgrade, or patch, or upgrade by reinstalling with REINSTALLMODE=vomus REINSTALL=ALL.
If component ids A, B, C and D are in the older installed product and component ids A, B, C, and E are in the new upgrade then the same happens except D will be removed because its ref count is zero now, assuming there are no other clients and the rules above do not apply.
Major upgrades with REP "early" are simple in the best case because it's an uninstall of the old product followed by an install of the new, so older versions of files could be installed, and again files are removed or not according to the above rules. None of the component ids need to be the same in the simplest case that there are no shared files used by other products.
Common issues involve setting components to permanent or Shared Dll (which is required ONLY if the file is shared with a non-MSI install). There seems to be the idea that these can be turned off by doing another install that changes them, but these are system settings, not project settings.

Related

Installing a binary if it is already present using WIX installer

Background : Customers have been copying a set of binaries and putting it on a specific location for them to run NinjaTrader Indicators. For Eg: lets assume The customer "A" has used First.dll, second.dll and Customer "B" has used First.dll and Third.dll (they did not use any installers, but just copied from a server location)
Current Requirement: I have to create a WIX installer with all possible updated DLLs with a caveat that it should install only those updated dll whose previous version customer has already on his machine. So if the new WIX installer has First_1000.dll, Second_1000.dll, Third_1000.dll and Fourth_1000.dll, then it should behave on Customer "A" and "B" as follows:
Customer "A": Uses this installer, his machine should have only First_1000.dll and Second_1000.dll and not others.
Customer "B": Uses this installer, his machine should have only First_1000.dll and Third_1000.dll and not others.
What I have Tried: Using the directorySearch and FileSearch, but I am not able to conditionally install, either it installs all or installs none. Other issue with this is it wont remove the previous version of the binary.
What I need: How to call a CustomAction method and use the return result to make decision to install or not, with this I can remove the previous version of the file as well.
Overall advice: don't approach deployment as a development task first and foremost. Get your files and settings deployed, and do any advanced configuration on application launch.
Do not implement any custom logic if all you need is a file copy and some registry keys - and certainly don't do it all in one custom action using WiX / MSI as a "shell" or "container" only.
There are many tools that can help you deploy your software: How to create windows installer (also lists legacy tools that are not MSI tools).
At one point I wrote this step-by-step answer for a WiX installer.
If you ask me for the easiest way to achieve what you want, then I would install all files via a single MSI and use the application itself to adjust any access to advanced features (if applicable) via the license code (if any). This minimizes your deployment complexity, and puts advanced features in a familiar context: application debugging in user context (most likely).
This avoids a world of pain of custom setup logic - which is very heavily overcomplicated by sequencing, impersonation and conditioningconcerns, not to mention runtime dependencies and other challenges. Collectively this causes the overall problem that setup logic is very hard to debug - due to the collective impact of all these aspects of complexity.
The general approach that should work is to:
Group the components (that contain one file each) into Features that when installed will do the right thing for each customer.
Use Feature conditions based on the results of the file searches and the property values set from the searches.
This example in the WiX docs, Conditional Installation seems to do almost exactly what you're looking for.
In the longer term you should build a setup that doesn't require this type of search behavior. You don't say why the file names change, but I'll guess that you are using the different names as a kind of version control. Installs, patches, service packs, upgrades and so on all replace files based on their binary versions. In a well-designed application and install, the binary versions of the existing files might all be 1.0. If the new files are all versioned 1.1 then all the old files will be replaced. If one was version 1.0 (and therefore unchanged) it would not be replaced. The file names would not change. Version control is the basis for updates, so I recommend moving in that direction.

In Wix , should Component guid be unique across msi?

In our project , People normally copy paste the WIX files and they will change the product and upgrade code.
Normally this was working fine without any issue.
Recently we faced an issue that while uninstalling an msi some registry keys were not removed and when we verified that the log file recorded some thing like this.
Disallowing uninstallation of component: { GUID } since another client
exists.
We were told that the Component guid might be used by another msi in the system.
should Component GUID also be unique across msi?
Welcome to the world of the "Component Rules". There is much that you need to know. I would start with reading:
What happens if the component rules are broken?
Organizing Applications into Components
Changing the Component Code
Defining Installer Components
Windows Installer Components Introduction.
Component Rules 101
ComponentID GUID Sloppiness Observation
About Shared Components
It might be normal - depends on the file. It might be a common Microsoft Dll that is in use by multiple products. Or a shared Dll from any other number of products.
However you imply that there's been some sloppiness in development and there might be duplicate guids in your own separate MSI setups. The short answer is that this is not really a WiX issue because it doesn't matter what tool you use to build MSI files. Component guids must be unique for a particular file or registry key. No duplicates. Actually Component guids need to be unique to a file or registry key across the entire system!!
Chris has given you plenty of reading.

WiX: Paraffin and repository/build server integration

Short version: How can I make sure that my component GUIDs remain stable using Paraffin on a build server?
I am currently working on a project that should be deployed via WiX. As this is a web project, it contains many files (still in early stage and already almost 200 files). Also, during development, files are constantly added and deleted, so maintaining the WiX component lists manually is simply not an option.
Since I read a lot about component rules and that people breaking them go to hell, I decided to go with Paraffin as a harvester. This tool is capable of updating an existing component list, thus not re-creating new GUIDs for existing components.
However, when a new component is created, the tool assigns a new GUID. Even if the component files are identical, then initial GUIDs will be different on different machines or even only at different times.
So, obviously, I need a central authority for fixing the initial GUIDs. My idea was to commit empty component lists, which are then filled by the build server calling Paraffin on build. So when I only distribute the MSIs created by the build server, I can be sure that component rules are being followed.
However, the problem with this approach is, that I have no means of tracking my GUIDs, should the build server crash or empty its local repository. I was thinking about having the build server commit the generated component list to my repository, but that doesn't seem like a clean idea.
Another solution I thought of was having all developers build (and thus call Paraffin) before commiting. Thus, each developer would create the initial GUIDs for their newly added files and commit them to the component list.
The obvious problem with this approach: People (e.g. developer A) will forget to build before they commit. So in these cases the build server will create the initial GUIDs for the new files, but those will also only be stored locally. A few commits later, developer B will come along and build the solution, creating a new GUID for the files created by developer A. He will then commit the component list containing this GUID and the build server will check it out. Now the build server has obtained a GUID (created by developer A) for a package, for which it had previously used a different (self-created) GUID, even though the files didn't change in the meantime.
So, how can I make sure, that my GUIDs remain stable between builds without relying on developers to build their solution before they commit? The approaches outlined above both seem unsatisfying to me, but are all I can think of right now.
As far as I am concerned component rules only really come into play when you have multiple installers that share components with the same guids (which should then be exactly the same resource(s)) or you are using a wixlib or a merge module which is then included as part of different installers.
From what you have said above, to me it doesn't sound like you will so, there is no harm in having different component guids for each build. It will just mean that when you upgrade the website, files that have not changed will be removed and re-installed under a different component guid. IMHO that doesn't really matter as long as the installer correctly installs all files that are required for the site to function and doesn't remove components from other products.
If you use the MajorUpgrade element, the old product will be completely removed before the new one is installed so any component guid's that are shared between the two versions will be removed and then re-installed anyway.
I always just leave my guid elements as Guid='*' that way I know that the there will never^ be any guid clashes in any of my components across my multiple products.
^ I know this is not theoretically true but in this use case it is.
Not entirely true. Changing your component GUIDs from build to build is fine if and only if you schedule RemoveExistingProducts early so that the files are off the system before you reinstall the new GUIDs. This approach works well for smallish installers with not so many files, but as your installer grows you will feel the pinch of having twice as much IO to do, as you remove and then reinstall, rather than just overwrite your files. In short, it's up to you, but you should think carefully about how large your application is likely to get before jumping in with the suggested approach.

How to diagnose/manage DLL hell with InstallShield?

We use InstallShield to build installers for a number of very similar products, each of which has shared files parked in fixed directories underneath our vendor directory. We have a DLL hell problem that I thought installers should handle.
Imagine products A and B both have file FOO, installed in directory D. FOO isn't necessarily a DLL; it can be any file.
In an ideal world, both copies of FOO are identical and when both products are installed FOO gets (overwritten) in D and everything is fine. And that's how it seems to work when everything is identical.
In practice, A and B may be from different generations, so in fact A has FOO variant A (FOO_a) and B has variant FOO_b. Now only one of FOO_a or FOO_b will end up in D if both products are installed, and consequently one of A or B will be confused because the wrong file is there.
What I would have expected from installers (and InstallShield) is that for each product P installed on a system, that a reference counter would be kept somewhere (the registry?) regarding each installed file. So when A is installed and FOO(_a) is placed, FOO is marked as belonging to A, and has reference count set to 1. If B is now installed with an FOO_b identical to FOO_a, FOO should be marked as belonging also to B, and the reference count incremented. If B is installed, and FOO_b is different than FOO_a, I'd expect the installer to complain that incompatible files FOO were being proposed for installation and the install should abort. When a product is de-installed, I'd expect reference counters for each installed file to be decremented, and the installed file only removed when the reference count goes to zero.
That's not what InstallShield appears to do. It simply smashes FOO into its target directory during an install. This is in effect the same thing as DLL hell. Is this the intended behavior of installers? Of InstallShield? Of Windows?
We thought we looked for a way to ask InstallShield to manage this, but can't find anything. I'd be pleased to have somebody tell where to look to configure this correctly. Or is it that InstallShield can't/won't do this? [If so, why am I giving them money?]
If variants of FOO are versioned and properly cumulative (this is key to all installation technologies that operate on shared locations), and if your component is shared, shares the same GUID in both products (if MSI), and shares the same installation location, then everything will work. As parts of that list of conditions are made untrue, various bad things can and will happen; some have workarounds, some are unrecoverable.
If the installation location differs, little or none of this matters, unless both products end up searching in the same location for some other reason.
If MSI, and the GUID differs (or the contents of the component differ), you've broken component rules. This may result in files not uninstalling at the final uninstall, unexpected changes in a repair, or other hard to predict results.
If the file is not properly versioned, it is not possible to know when to replace it with the other one. It is likely in some ordering, a new install will end up running with an old version. This can be mitigated in an MSI with companion files, if another file has properly increasing versions.
If the file is not cumulative with respect to versions, it is likely that a new version of the file will break the other consumer of it. In this scenario, you should not install it to a shared location.
One size fits all does get a little messy sometimes, so generally the problem space is simplified to one that can be addressed. In this case the cost is that all installation authors have to follow some rules if they want well-defined behavior.
If you are truly looking for a solution, use separate DLLs. This is simple and reliable, and in fact it's easier to talk about (as you have demonstrated when you mention FOO_a and FOO_b). It also avoids the error message, which is not helpful for your users.
Windows did have a reference counting scheme for shared DLLs, but it was by convention, so it was unreliable. If you've ever had an uninstaller ask if you really want to delete a shared component, now you know why.
No one can possibly begin to answer your question without knowing what installation technology you are using. InstallShield is a product that supports many different frameworks.
Windows only keeps track of shared references on DLL files. ( SharedDllCount ). Windows Installer ( assuming you are even using it ) also keeps track of Compoent reference counts.
However, it should be noted (again IF you are using Windows Installer ) that the file costing process that decides to overwrite a file really has nothing to do with reference counts. Reference counts come into play on uninstall.
Assuming you are using Windows Installer, take a look at:
File Versioning Rules
Also look at:
What happens if the component rules are broken?
This *is something Windows Installer and InstallShield know how to handle it assuming that you implement it correctly. Any programming language can be misused and abused intentionally or not.
if you don't want the files to be common, why in the world are you placing them in the same directory? I would rather put them in different location. Now suppose your FOO_a is being used by many programs at the same version, then for FOO_b which is required by your new program and others that might be added later, add another similar common directory and name them using generation names or something.
I believe you are correct in how the reference counting works if you are dealing with a pure MSI-based installer (it would help to know if A and B are Basic MSI or InstallScript projects). In any case, I would verify the components that contains FOO in the A and B install projects have it set as the key file, and have the Shared property set to Yes. That should cause the reference counting to be managed correctly.
Regarding what should happen when you run the installer and it has a newer version of a shared file, to my knowledge that depends mostly on whether the file has a version or not, whether it's a newer version, and properties like its last changed timestamp. If you are building an installer for B that contains FOO_b that is different from FOO_a, I'm not sure how InstallShield could know that it will break A (I could be wrong, maybe it can). One possible workaround is to use custom actions to check if FOO_b is different and if it is, make a copy of the existing one early in the install, then copy it back at the end of the process.

WiX project allowing side-by-side installation

I am in the process of creating an MSI for our product. I would like the product to be able to install side-by-side. So that I can install 1.0.0 first and later can add 1.0.1 so that the two versions are both installed.
I am using WiX to create the msi and would like to know how this can and should be done in Wix? For example
Do I need to create new Guids for all components?
How would i add the version info to wix or should i rename my product completely?
How can I create the projects so that releasing a new version requires minimal changes in the wix project?
Greetings,
Martijn
You should be able to get away with just changing the top-level productcode and UpgradeCode GUIDs to make your two products completely unrelated, and use the Productversion to identify the version. You can share component guids between products (that's how merge modules work) so that the guts of your installer (component definitions) needn't be tweaked and can still be shared.
You major challenge will be ensuring that the two decoupled products don't interfere with one another, for example by having the same default installation folder, start menu entries and the same Add/Remove programs entry. You might achieve this by including the product version number in the ProductName Property, which can look a bit techy in your install UI, but it isn't unheard of.
Regarding your first question: No, you don't need to.
But why?
I had difficulties to understand the windows installer rules in my side-by-side scenario.
You really need to understand the concepts of component rules (including when you need to brake them) and key paths. WiX doesn't abstract these aspects away.
This answer does already highlight possible interferences.
Let's consider a few examples.
The GUID of the component with the application executable does not need to be changed. This breaks the component rules, but it works, as both versions of the product define the same component in a non-interfering way.
A resource shared by both versions is not directly supported. A prominent example is the use of file extensions using ProgIDs, as shown here.
If you change the GUID (also happening when using the "*" GUID), the extension will be removed when uninstalling either version.
If you don't change the GUID, the extension will be retained, but point to the version which was installed most recently. You may go with this option as the lesser of two devils, supporting at least a scenario where users uninstall the versions in the same order in which they installed them.
There is a pitfall here: The extension needs to be the key path of the component. This makes the usage of the ProgID element problematic in a side-by-side scenario as you'll get the ICE69 warning in case you don't put the ProgID element in the same component as the referenced file. Further more, it's an implementation detail of WiX which registry entry it generates will be the key path.