How do I persuade a VS2005 msi to upgrade? - c#

I have a Windows service written in C# using VS2005.
The installation is via a wizard that calls msiexec to install the msi file also created using VS2005.
I am having trouble generating an msi file that will upgrade from one version of the service to another. The wizard program handles detection of the currently installed version, stopping the service, coming up with an appropriate command-line for msiexec and then re-starting the service.
The existing msi has a version property of 1.1.02, the new one is 1.1.03. The product and upgrade codes are identical.
Uninstalling 1.1.02 manually via add/remove programs works fine, as does installing 1.1.03 onto a "clean" system.
Upgrading 1.1.02 to 1.1.03 goes through the motions but the end result is 1.1.02 installed.
The command line that the wizard uses for upgrading is:
msiexec /qb /i "MyProduct.msi" REINSTALL="ALL" REINSTALLMODE="vos"
Where am I going wrong? I'm assuming I must have missed something fairly fundamental...
The fall-back position is to inform customers that they need to manually uninstall 1.1.02 before running the wizard to install 1.1.03 but I'd rather not have to do that.
Edited to add:
Changing the product code (as VS2005 also prompts you to) actually removes the ability to upgrade at all, as it the installer won't let you do a reinstall if that product code hasn't previously been installed.
All it will then let you do is install (and then you get the usual "service already exists" -type message).

There are several things that need to be done to get "upgrades" to work with MSI's if you want to automatically remove the previous version.
First some background information about the mysterious "codes". There are 3 codes (GUID's) associated with an MSI:
Package Code - This identifies a particular version of the MSI installer and should never be reused across builds. It must always be updated.
Product Code - This identifier is used to ID a particular version of the application. It is up to the installer author to decide when to assign a new product code.
Upgrade Code - This identifies the application and should not change through it's lifetime
The Upgrade Code should never change. For you upgrade scenerio, the Product Code must be changed for each version. Additionally, as you mentioned, you must bump the version number. The Product Code and Upgrade Code can be found by selecting your setup project and going to the Properties Window. The Package Code is hidden in Studio and will always be updated.
The item you are probably missing, is that you also need to set the RemovePreviousVersions setting in the Properties Window to true.

One more thing in addition to mohlsen's answer (For Visual Studio 2008):
In order for your Primary Output (your EXE!) to upgrade properly, you must increment the FILE VERSION
This setting can be found in the Project Properties: Application Tab -> Assembly Information

An easier way to manage this is to REMOVE the AssemblyFileVersion from all assemblies, including the main executable and all the managed DLLs.
In each of your AssemblyInfo.cs files, I recommend doing something like this if you don't care about the version numbers, but want to have some traceability.
[assembly: AssemblyVersion("1.1.*")]
// don't need this [assembly: AssemblyFileVersion("1.0.0.0")]
Everything still compiles fine, and if you don't have the AssemblyFileVersion defined, then the installer assumes that everything is different every time (which is probably fine if you are installing all of the DLLs next to the main EXE).
I spent a long time figuring this out, especially if I don't want to have to increment anything manually!

Related

MSI Installing twice rather than upgrading with the same UpgradeCode

I'm using Visual Studio 2017 with the Installer Projects extension to make an MSI for my app.
However, when I run the, say 0.2.7 MSI on a system with 0.2.5 already installed, it adds another item to the appwiz.cpl, and leaves the version the same, so that after the 0.2.7 install, it just runs the 0.2.5 exe.
I make sure that the UpgradeCode is the same, the PackageCode and ProductCode are changing with each version and I have RemovePreviousVersions set to true.
My source code is available here. You'll find the installer project in ChromebookGUI/Installer/.
What am I doing wrong?
You can get a list of upgrade codes for installed packages like this: How can I find the Upgrade Code for an installed MSI file?
I have another one that is a bit simpler, but I couldn't dig it up right now. I will have a look later.
Adding this answer to "evolve" it if you provide more follow-up information (to avoid too many comments).
UPDATE: the other answer is here, but I wouldn't recommend that script if you have automatic logging enabled since it will create log files in the temp folder when run.

Detect All Users .msi

I wrote a C# program in Visual Studio that uses the Setup & Deployment Project to create an .msi installer. The "InstallAllUsers" value is set to "True", so it'll install "Everyone" by default, but the users can change it to "Just Me" during setup.
It's just a basic installer - nothing fancy.
My question is this: after they install the program, is there a way to tell which option they chose? Is there a registry key that I can dig for that will tell me whether they chose "Everyone" or "Just Me" during install? I'm not programmatically adding any registry keys, and I can find the "Uninstall" key for my program, but I don't know if there's a value in there that will tell me.
* EDIT *
For a clearer picture:
As I make changes to my program, I increment the version numbers and give the updated .msi to the users, and they just rerun the installer. There was originally only supposed to be a couple of users, so I didn't make a complicated updater. Now there are many users, and the updater is in-the-works. For now, the current users are happy with the process - I give them a new .msi and they run it again - except for one thing: the installer doesn't "remember" their settings from the last time they ran the installer (their words, not mine). I can get the directory of their last install from the "Uninstall" regisrty value and set it with TARGETDIR, so I've got the installation path covered. But I'm trying to figure out if the user changed "Everyone" to "Just Me" the last time around.
One way could be check for current logged in user in registry and see if it has the software listed in in installed software list under HKEY_CURRENT_USER\SOFTWARE\*
There's an example here of enumerating products to find out which context they were installed in. If you know the ProductCode you can just do the MsiGetProductInfo part.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa368279(v=vs.85).aspx
There are APIs for this, so it is more advisable than guessing based on what might be in the registry.
The installation folder properties window in Visual Studio setup projects has an InstallAllUsersVisible property you should set to False. Otherwise it's going to be a nightmare when you do an upgrade with RemoveExistingProducts=True because that requires the upgrade to be in the same context as the original install. You'll find people trying to do an upgrade with All users of an installed Just me product and it will not work.
I don't like per-user installs due to all the problems relating to upgrades, patching, etc... Accordingly I managed to migrate per-user installs to a per-machine during a major update install using Installshield and their built-in ISSetAllUsers custom action plus some re-sequencing of various standard actions. The description can be found here: windows Installer - uninstalling previous version when the versions differ in installation policy (per-user, per-machine)
If you want to migrate all installs to a per-machine install, you could replicate this approach using Phils suggestion to read the current installation context via your own custom action and then run this custom action in place of the ISSetAllUsers custom action that Installshield provides. Then you can follow the rest of the procedure from the link above.

how do i create the Setup windows service to uninstall then install service?

I developed a windows service in C#. and my project has Installer, and Setup project.
the setup project, installs service very well and it doesn't have any problem, but when I change project and create other setup, the new setup doesn't install new service, because it's already exist!!!
is any way to create setup that it uninstalls the service then install it?
The problem is that upgrades in VS2008 setups and later use installer classes and an install sequence which is 1) Install newer product over older product, use file version checking, run install custom actions 2) Remove the old product running uninstall custom actions and getting rid of files that are no longer used. VS 2005 wasn't like this. So the installer class custom action that installs the service is running when the service already exists. Sometimes you can revert to the VS 2005 upgrade mechanism that removes ALL the older installed files first, including databases etc, so keep that in mind. You can do that by modifying the MSI file with Orca, InstallExecuteSequence table, and re-sequence the RemoveExistingProducts action to immediately after InstallValidate. Otherwise do both these:
Always install the service binary to a fixed location (that means not the browsable Program Files folder). The uninstall custom action for the service should have a condition of NOT UPGRADINGPRODUCTCODE so that an upgrade doesn't try to uninstall it (but a straight uninstall will). It needs to be in a fixed location because this upgrade solution doesn't install or uninstall the service - it just updates the binary - but obviously you don't want service entries referring to a service that has changed location.
In the upgrade have a condition of NOT PREVIOUSVERSIONSINSTALLED on the install custom action for the service. This means it will not try to re-install the service (which is why you get the "already exists" error) but it will update the exe if you have incremented its file version.
If you search the MSDN setup forum http://social.msdn.microsoft.com/Forums/en-US/home?forum=winformssetup for PREVIOUSVERSIONSINSTALLED many of the posts are to do with this issue and there is more discussion and explanation.
Editorial: Installer classes are not required because Windows Installer can do all this for you, which is why this is only ever an issue with VS setup projects. Moving to a tool that uses MSIs ServiceInstall and ServiceControl tables will get you out of the need for any code to install/uninstall/stop/start services.
You could do it by properly configuring MSI properties. Click on Installer project and press F4 to view MSI's properties window.
When you go to properties you could see two type of properties that are Upgrade code and Product Code. Every Time you create/release a new Windows service installer make sure to keep the same Upgrade code but change the Product Code.
Change DetectNewerInstalledVersions property to true.
Set RemovePreviousVersions to true.
Now Change the version number to a higher version from previous release, if your previous release version was 1.0.2, change the new version to 1.0.3.
When you install a MSI with abovementioned settings, MSI will check if any other product installed with same Upgrade code, if it finds a product then it will check if the new installation has a higher version. If all the conditions are satisfied it will first remove the existing(Older) version and install the newer version.

.NET Setup Package Installer

I have a .NET 3.5 Setup Package Project which installs my application successfully.
The setup package deploys a number (around 70) custom files for use from within the application.
From time to time I have the requirement of deleting some of these files, however upon restarting the executable, it automatically runs a portion of the Setup MSI again, and re-installs these files.
The only way I can achieve my desired result at the moment is to delete the files after starting the executable.
I have looked through the attributes on the files in the setup package such as Vital and PackageAs, however cannot seem to identify the required setting to achieve this.
Does anybody have any idea what is needed to acheive this ?
Much thanks
From http://msdn.microsoft.com/en-us/library/kz0ke5xt%28v=VS.100%29.aspx
"
How do I turn off repair for a file that users are expected to modify or delete?
Visual Studio creates advertised shortcuts so that when the program is started it verifies that all its files exist. To change this behavior and cause it not to repair the file, select the files in the setup project and change the Condition property to NOT REINSTALL so that the file will not get reinstalled on a repair and its Transitive property to TRUE so that the condition is re-evaluated. This will cause the Installer to flash on the screen the first time after the file is deleted, as it verifies that the file should not be reinstalled, but you will not see the installer after that.
"
This has annoyed me too, and I don't know the specific setting you have to change to prevent this, but I can tell you a work-around that I've used. I found that it only happens when I run the application from the shortcut that was installed as part of the installation. So, if you create a new shortcut to the application and run it via that shortcut, then the files you've deleted won't be automatically restored.
Windows Installer supports three types of product upgrades: major upgrades, minor upgrades, and small updates. A synopsis of which upgrade to use under what circumstances can be found here: http://helpnet.flexerasoftware.com/robo/projects/installshield12helplib/MajorMinorSmall.htm
What you want to do is set you installer up in such a way that the new version of your software sees the file deletions as upgrades to a prior version. If you fail to do this and then you delete files, the program installation thinks it is damaged, and tries to restore the files you deleted when the program is executed again.
In InstallShield (the installer product with which I am familiar), there is a Product GUID and an Upgrade GUID. The Upgrade GUID always stays the same within your product family of different versions. The Product GUID changes with each new MAJOR or MINOR release. In general, if you follow this pattern, and then specify file deletions in your new version, the installer should interpret the file deletions as upgrades, and you should not get any errors on subsequent execution of the newly-installed version.

MSI uninstall not executing Installer class tagged with [RunInstaller]

We are developing a MSI installer solution (based on VDPROJ, unfortunately) that will install an instance of our server application. The installer has all been designed to support the concept of installing multiple instances. This means we've written a small utility that takes a 'stub' MSI and then outputs a configured MSI with a unique Product Code, Product Name etc amongst other things. The Product Code is updated in both the Property table and Summary Information stream.
Everything seems to work fine on the install step. Our two test instances, AcmeCorp and ZurgCorp, are installed side-by-side just fine. Their Windows Services are all registered and function as expected.
However, we have encountered a very strange issue when it comes to uninstalling one of these instances. It doesn't matter which instance we uninstall first. But which ever one we choose, the uninstall will proceed very quickly - but so quickly that it actually misses out calling our .NET Installer subclass (tagged with RunInstaller and being called by the CustomAction).
The uninstall will be 'successful' and the MSI will be gone from Add/Remove Programs list. But the Windows Service remains behind because the MSI uninstaller never run our assembly to remove it!
Now if we go ahead and uninstall the second instance as well, this one will uninstall just fine including the Windows Service.
So what on earth is going on here?
It smells to me of some form of reference counting. But where? We've followed the book on this when it comes to making sure the Product Code's are different. Maybe there is something we are missing to do with Component Id's?
Thanks.
This is can be considered a hack, but it solves the issue. Of course there will be ramifications for MSI's that you intend to issue patches or upgrades for. But for our particular scenario this is not an issue because an upgrade will always involve uninstalling the server and then reinstalling it using the newer MSI.
Anyway, the workaround is this:-
Use Orca to look at your MSI. Go into the table called Component. In this table is a column called ComponentId. The values for all of these must be updated to have a new value (a fresh GUID). None of the other columns matter, the Component column which has a C__ prefix is only used internally by the MSI database. The critical one, as explained, is the ComponentId because this is the one that gets stored (albeit in some hashed/encrypted form) in the Windows Registry and is used by MSI to keep reference counters.
More detail on this ComponentId column is here: http://msdn.microsoft.com/en-us/library/aa368007(VS.85).aspx
To automate this, I am going to improve our "Instance MSI Generator" utility to basically generate a new GUID for each record in the Component table.

Categories

Resources