Custom Action - Only uninstall if installed - visual-studio

I have a Windows Installer project attached to my solution, which allows the user to optionally install two components of the application (they are Windows Services) using a "Checkboxes" dialogue box which I've added to the User Interface, and then Conditions on the Custom Actions...
i.e. in Custom Actions / Install I have
Primary output from ProjectA (Active)
- Condition = PROJECTACHOSEN (i.e. the Checkbox1Property value)
Primary output from ProjectB (Active)
- Condition = PROJECTBCHOSEN (i.e. the Checkbox2Property value)
In Custom Actions / Uninstall, I've added the same two "Primary Outputs".
The problem is, if during installation the user only chose one of the two projects then the uninstaller still tries to uninstall both of services, and of course throws an error when it doesn't find the missing one, causing the uninstall to fail.
What condition can I add in Uninstall to only install the service if it's currently installed?
I've tried:
adding the same conditions as the Install - this doesn't remove the services at all (presumably because you're not ticking the boxes during uninstallation)
Condition = "Installed" which I came across elsewhere on the web. This appears to be ignored and the uninstaller still tries to uninstall both services.

you can use ?ComponentName to check install state of a component, i used it similarly few months ago.
Ive found about it in cheat sheet from flexera here

The best solution I've managed to come up with so far is:
Add a File Search "Launch Condition" for each project that checks for the existence of the .exe file that is the service, and give it a "Property" value.
Use the property value as the "Condition" of the Custom Action.
This works but I'm not totally happy with it, as it's more important that the service is deleted than the exe and if the exe goes missing it won't work and the service will remain...
Edit
Even that doesn't work unless I set the Search "Depth" to at least 3 (even though the exe file is in the installation directory and the "Folder" set to [TARGETDIR]), but if I do that, the installer takes about 20 seconds just to start up - presumably because of the time it takes to search 3 levels of subdirs. But if I set to any less than that although the installer starts quickly it now doesn't seem to find the exe and doesn't uninstall the service...
Shouldn't [TARGETDIR] reflect my installation directory and therefore work with a depth of 0?

Related

InstallShield Block installing same MSI twice from command line

I have setup created with InstallShield 2015 Basic MSI.
Let’s say in this setup user can choose if he want to install Client or Server,
for each setup type ( Client or Server ) there are several features he can choose to install.
Now user choose to install Client setup and included 2 features under it,
If he run the setup again with UI he will enter the maintenance mode enable him to add more feature belong to Client.
My problem is that he can also run same setup again from command line (msiexec /I) and try to install the Server setup type, currently setup doesn’t block or prevent it and this may cause corruption of Client setup he already have installed.
So, I guess I should write a CA that will check if current setup is installed and check what action user is trying to run from command line and if it’s not uninstall block it ( still need to support running uninstall from command line ).
Is this logic correct? How can I know what is the command user run from command line? meaning that he run msiexec /I and not trying to uninstall from command line.
I wonder if there an InstallShield\MSI build in way \ property to prevent installing same MSI twice from command line ?
I'm having a hard time understanding you. I hope my answer is aligned with your needs.
When you first run the MSI it's in installation mode. When you run it again it's a maintenance mode. Depending on how you authored your installer, you should get a dialog with Repair, Change or Remove buttons. The Change button can be used to allow the user to add additional features or remove features. This can all be done from the command line as well using the ADDLOCAL and REMOVE properties. There properties take a , delimited list of feature names.
One place people make a mistake is using the conditions Not Installed | REMOVE="ALL" on their custom actions. This doesn't take into consideration the fact that you might have multiple features and come back during maintenance mode and reconfigure the application. MSI has feature and component installation and action state evaluators that can be used in conditions that are better suited for this job.
Update:
I would write a Type 19 error custom action that blocks the installation if the user is requesting a two conflicting features to be installed or the user is requesting one feature to be installed that conflicts with another feature that is installed. Use the feature request and installed state operators in your conditional expressions and schedule it after CostFinalize.

how to get the name of the feature being installed using wix managed bootstrapper ui

I am using WiX to install a executable and I have used ManagedBootstrapperApplicationHost for CustomUI.
Is it possible to get the name of the feature being installed at the time of installation ?
If possible then how can we get the name of the feature ?
Any help would be appreciated.
Thanks.
Features aren't installed one after another. For example if 3 features are being installed, each with 10 files then the InstallFiles action will install all 30 files at the same time. Same thing with registry entries. So you can't display a UI that says "Installing Feature1" and then later on "Installing Feature2" because that doesn't happen. All you can know is that some list of features are being installed.
Your comment asks about finding out whether a feature installed successfully or not. This issue never comes up - there are never some features that install and others that fail. An MSI install is a transaction and it either all works or fails and rolls back and deletes changes it made so that the system is restored to its previous state.
It's not clear why the list of features is so important to display. If you use the MSI's internal UI there is a feature selection dialog where the user selects which features are to be installed; if you use the Burn UI the same thing is available, so the user can see what features have been chosen.
Inside the MSI the list of features being installed (after they've been selected) is in the ADDLOCAL property, but that's the internal name. It could be used to display a list of the features that were installed at the end, but again by definition what was chosen is installed otherwise the install would have rolled back entirely. I don't believe I've ever seen an install where the list of MSI features installed is displayed at the end - it's redundant info. It would be useful to know the scenario you have, or what problem you're trying to solve, and if you believe that you need to display a list because some might install and others might fail then there is no point, as I have said.

VS setup project destroys self when installed files are deleted

This should be pretty simple, but I can't seem to get it. I have a setup project (VS2010) that packages a few dozen image files (along with my SQLite file) and copies them to the user's computer when the program is installed. As these are essentially "stock" images, it's ok if the user deletes them (there is functionality to do so from within the program.) However, after one or more of these images have been deleted, the next time the program starts it gives a "Windows installer" dialog box, and deletes all of the remaining data files!
What I think is happening is the program sees the missing files, assumes the installation has been corrupted, and tries to go into some kind of recovery/uninstall mode. I'd like to know how to indicate in the setup project that the files need to be installed, but may be removed by the user at any time.
I have tried several combinations of File properties, and nothing seems to do quite what I want, which is for my installer to put them where I say and never think about them again. Do I have to reinvent the wheel and do this through a custom action??
EDIT: Transitive and Vital had both been set to True. Setting them to False causes the program to re-add the deleted images back after it has been restarted! I'll probably go with a custom action if I don't get an answer.
When using a file association or advertised shortcut Windows Installer automatically checks if component key paths are missing. If a key path is not found, a repair is trigger to reinstall the component.
Most likely your installer repair process does something that removes the other files.
A solution is to not register your components with Windows Installer. This is done by using a null component GUID and it's not supported by Visual Studio setup projects (it is however supported by most of the other setup tools).
Another solution is to make sure that your image files are not key paths in their components. This is also not supported by Visual Studio.
If you want to use a setup authoring tool which offers more control, you can take a look at this list: http://en.wikipedia.org/wiki/List_of_installation_software

Duplicate entries in Add/RemovePrograms control panel when using MsiSetExternalUI

I created a setup for my product targeting Windows XP and later, to be installed using windows installer (WI). The resulting .msi file has a product code, let's say PC1 (actually a guid), and an upgrade code UC1 (also a guid). After some time, I created a new setup for a newer version of my product. The new .msi file has a new product code PC2 and the same upgrade code UC1 (also called a major upgrade). My company wants to install the .msi file with our own installer. For that, we basically use MsiInstallProduct to install the .msi file, while the entire UI is in our own install program (and we use MsiSetExternalUI to ask WI to send us notifications). The problem that I am having is the following:
if the two builds of the product are installed on the same machine using "msiexec /i myapp.msi" then there will only be one entry in the "Add/Remove Programs" of "Programs and Features" control panel applet. or in other words, during the installation of the new build, the old one is uninstalled.
if the two builds are installed on the same machine programatically using MsiInstallProduct, there will be two different entries in control panel.
Once again, only if I try to install it programatically (using either MsiOpenPackage+MsiDoAction or MsiInstallProduct), the upgrade does not happen and I end up with two entries in the control panel. I also found that if I do not set an external UI callback using MsiSetExternalUI, before calling MsiInstallProduct or MsiDoAction, then the upgrading part of a new installation also works as expected, no duplicate entries in the CP.
The callback that I use for MsiSetExternalUI is basically the same as the one in this MSDN article:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa368786(v=vs.85).aspx
What can I do (or what I need to handle in my callback) to avoid having duplicate/multiple entries in control panel?
Thanks and best regards,
Levente
Re our comments above, I did a google search for CLIENTUILEVEL and the first several hits indicate to me that CLIENTUILEVEL having a null value is normal and that REMOVE=ALL is working. The comments indicate to go a little furthor down the log and find out why the uninstall (Remove existing products) is failing. If you could email me a complete log file ( chrpai#iswix.com ) I could look through it for you.
RemoveExistingProduct standard action
Link to article describing how to interpret Windows Installer log files (see comments)
RemoveExistingProducts running but not uninstalling Options
I ran into the same behavior with my ManagedMsiExec sample project: http://blogs.msdn.com/b/delay/archive/2012/01/09/make-things-as-simple-as-possible-but-not-simpler-managedmsiexec-sample-app-shows-how-to-use-the-windows-installer-api-from-managed-code.aspx
Changing the logging behavior of my app didn't help in my case. But after (independently) noticing the same "CLIENTUILEVEL= REMOVE=ALL" strangeness in the logs, I found a workaround which was to explicitly call MsiSetProperty and set CLIENTUILEVEL to 0 before calling MsiDoAction.
This appears to me to be a bug with Windows Installer itself (incorrectly setting CLIENTUILEVEL during RemoveExistingProducts), but perhaps there's something else going on I don't understand. At any rate, I've had success with this change and maybe others can, too. :)

Windows Installer - force users to remove via Add?remove Programs

We have an installer solution written in Visual Studio 2005 Installer; that calls a C# custom action and we have hit a known issue, regarding the fact that on an upgrade - the old install code is run and not the new code, because Windows is running a cached version of the custom action dll. We know this and although not over the moon about it - we have moved on.
When we release a new version of the installer and a user runs it, we now want it to check to see if an ealier version is installed - if there is one; we want to display a message telling them that they have to remove the old version via Add/Remove Programs. We know if they do a manual uninstall followed by an install, then all is fine and dandy - BUT it doesn't matter how many times we tell our users, via documentation; that this is what they have to do - they will still try and just run the new installer, without removing the old version first.
Therefore, we would like to put up a message and thus force them to to what they are told !! I've seen some installers do this ( though of course not sure what installer package was used to create these ). We only have VS 2005 and of course orca !!
Cheers,
Chris.
This can be done through a custom launch condition:
create a search which determines if the old version is installed (you can search for a component, registry entry or file)
use the search property as a custom launch condition
For example, if the search property is OLD_VERSION, the launch condition can look like this:
Condition: NOT OLD_VERSION
Description: An older version was found. Please uninstall it using "Programs and Features" in Control Panel.
When OLD_VERSION property is set to a value (an older version is found), this launch condition will show the message and stop the install process.
This doesn't quite make sense. Have you remembered to change the package GUID in your new setup? The package GUID identifies a specific setup file, and if two MSI files have identical GUID they will be treated as the same file regardless of whether they are or not. This could trigger a cached version of the MSI to be invoked and all sorts of hell breaks loose.
I would recommend reading up on "major upgrades" which will allow automatic uninstall of the existing version before the new version is installed. You also need to make sure you understand the basics of the technology before deploying to the wild. You must NEVER use identical package GUIDs for any MSI files. It's practically always wrong, and will lead to very mysterious problems.
I can't write up the whole major upgrade solution here, but basically it involves authoring the "Upgrade" table of your MSI to detect versions to uninstall. You need to change the package code, product code and version number (only 3 digits matter) and keep the same upgrade code (two MSI files with the same upgrade code "know" they are related - i.e they are from the same product family). Check MSDN for samples of major upgrades.
NB! If you have deployed MSI files with duplicate package GUIDs to your developer machine, it could have stray installs that must be cleaned up with MSIZap or similar. Use caution, or better yet test your new installer on a clean test system. Developer systems are full of junk and not generally good for MSI testing.

Resources