Custom action condition for first install
I'm very embarrassed to have to ask this despite years of making install packages!
What conditions do I use for a custom action in a merge module such that the action runs only on initial install of an application and does not run on upgrade or repair? Likewise, I need a condition that runs another CA at uninstall only.
We have several apps which need to set the same DWORD registry flag. The apps can be installed together (independently) on the same machine, so the flag must not be unset if one app is uninstalled while another is in place. Unfortunately the only permissible values for the flag are zero (or unset) and one.
I coded a C dll to be called from two custom actions to manage a "reference count" (in the registry, separate from the flag). When an app is installed the first CA increments the refcount, and when uninstalled the other CA decrements the refcount. When the refcount is zero, the flag is cleared. This goes in a merge module, to be included in all relevant apps. But I can't get the conditions right for the custom actions.
Please help
Thanks
Alan
Comments
Hi Alan, The condition that
Hi Alan,
The condition that is true ONLY on first time install is NOT Installed.
The condition that is true ONLY on uninstall is REMOVE="ALL". (must be entirely in capitals)
Some use REMOVE~="All" as the ~ symbol makes the test case insensitive, but its more to type so I just prefer to stick with capitals.
I think you may have over-engineered the reference counting solution, as you could have created a simple merge module with one component that sets the DWORD registry key, and included that in each application. Any components with a fixed GUID are automatically reference counted by Windows Installer, so the registry key would only be removed when the last app was uninstalled. No DLL necessary. No conditions necessary.
However, you have a solution in place, so just make sure that if your application installation should bomb and roll back, the DWORD registry key ends up being correctly decremented again.
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Thanks so much Ed ! (I'm very
Thanks so much Ed !
(I'm very sad to fail Installer 101!)
> a simple merge module with one component that sets the DWORD registry key, and included that in each application.
That works wonderfully in new code, and I've been doing testing along those lines, but unfortunately there is a snag: some earlier products already set the registry key.
I was suprised to find that if two different MSIs set a registry value, uninstalling either MSI *clears* the value - leaving the other product broken. I had always thought that reg values were managed by Windows Installer rather like files, where if a file is present before installing an MSI that also installs the file, the file is not removed when the MSI is uninstalled.
Thanks again
Regardless of whether you use
Regardless of whether you use MSI technology or an older scripted technology, the behaviour in respect of registry keys has been pretty much the same for all - when a key is installed, any existing value is overwritten. When a key is uninstalled, any existing value is deleted. There is no capacity for restoring a previous value UNLESS YOU CODE FOR IT.
A classic example I use is the JPG case - by default Windows File and Picture viewer are launched. If you then install Paintshop Pro, it will hook the JPG extension. If you later install Photoshop, it will hook the JPG extension for itself. If you now uninstall Photoshop, the JPG extension is left orphaned, as there is no record kept of what application was previously associated with that extension. If you were coding for the Photoshop uninstall here - would you restore the association to Paintshop Pro or the default? What if other image editors are installed? It quickly gets unmanageable.
Windows Installer does allow you to mark components as permanent, which is occasionally useful. Also, if you use merge modules, they and their contents ARE reference counted, based on component GUIDS, and this is what I was suggesting as a simple way of achieving the same effect as your DLL.
There is one further flag on components which is defined something like "do not install the component if the component's key path exists" Since both files and registry keys (and folders for that matter) can be used as key paths, you can mandate that a component is not installed if a specific key path is present. If the component is not installed, then there is no record in the registry so the component should not get uninstalled when the app is removed.
Bear in mind that windows installer manages components and not files or registry entries. Windows installer reference counts components and not individual files or individual registry keys. Thus you have a lot of power in that you can place multiple resources into a component and reference count them as a set. Place several components in a merge module and you now have a potentially huge chunk of install that is reference counted as a whole.
However, if the same resource is installed by components with different GUIDS, then each instance has a reference count of 1, so uninstalling either will result in the resource being removed, as its ref count is decremented to zero.
I don't have a simple solution to your legacy installs which have already set the registry key in question. If you are bring out a major upgrade series, perhaps one solution would be to define a different registry key and move forward with that.
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Oh dear, I need a little more
Oh dear, I need a little more help please.
NOT Installed runs both during an initial install and during an upgrade install. Is there a generic way to know if this is an upgrade so I can use it in an AND NOT ?
I could check the property defined in the upgrade table, but this code has to go in a merge module, so there's a disconnect between the merge module and the upgrade property. I can try saying that all MSIs using this code must use a specified name for the upgrade property, but that relies on other developers.
Similarly REMOVE="ALL" is true during the uninstall phase of an upgrade, but I remember the generic solution is to add AND NOT UPGRADINGPRODUCTCODE to constrain the condition to a final uninstall.
Thanks
This gets more complicated as
This gets more complicated as the behaviour of the upgrade process depends on whether it is a minor or major upgrade, and also depends on where you have sequenced the RemoveExistingProducts action.
The UPGRADINGPRODUCTCODE property is defined thus:
UPGRADINGPRODUCTCODE Property
The UPGRADINGPRODUCTCODE property is set by Windows Installer when an upgrade removes an application. The installer sets this property when it runs the RemoveExistingProducts action. This property is not set by removing an application using the Add or Remove Programs in Control Panel. An application determines whether it is being removed by an upgrade or the Add or Remove Programs by checking UPGRADINGPRODUCTCODE.
So if we assume a major upgrade, where you have sequenced RemoveExistingProducts between InstallValidate and InstallInitialize, you will find that the original package is entirely removed first (thus decrementing the counter) and the upgrade is installed (thus incrementing the counter).
If, on the other hand, you have left RemoveExistingProducts in its default location in the template, which is right at the end of the InstallExecute sequence, then the UPGRADINGPRODUCTCODE property basically does not get set until the end of the install is reached.
So we get down to the question - what sort of upgrades are you deploying and where are you sequencing the RemoveExistingProducts action?
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Thank you so much Ed. We
Thank you so much Ed. We decided to use your suggestion of making the components permanent, and if the setting gets broken by another app then the end-user will just repair.
Fwiw (and that's not much now) the package has RemoveExistingProducts between InstallInitialize and InstallValidate. For some reason that I didn't figure out, during an upgrade the counter was not getting decremented-then-incremented, so the count wasn't reliable. I think it was probably operator error, but as we took the simpler approach we'll never know (until next time...)
I very much appreciate all your help, which I've benefited from many times. Thanks again!
Alan
Would you like to reply?
Login or Register to post your comment.