Thoughts about setup and deployment issues, WiX, XNA, the .NET Framework and Visual Studio
All postings are provided AS IS
with no warranties, and confer no rights. Additionally, views expressed
herein are my own and not those of my employer, Microsoft.
I read the article titled Using the .NET fusion API to manipulate the GAC yesterday. That article describes how to call fusion APIs from C++ code to programatically add assemblies to the global assembly cache (GAC) instead of using the .NET configuration utility or gacutil.exe.
In general, installing an assembly to the GAC is an application deployment activity, and is most often done during application setup. The article I read does not specifically say that C++ code should be used to install assemblies during application setup, but it does not say not to either. Therefore, I wanted to make sure that anyone reading this blog knows that you should not use C++ code or gacutil.exe to install assemblies during setup of your application.
You should use Windows Installer to install your application. Starting with version 2.0, Windows Installer has built-in functionality to install assemblies to the GAC - the MsiAssembly and MsiAssemblyName tables in particular. You can refer to this MSDN document for an overview of how to add assemblies to an MSI package and this MSDN document for a description of how to add Win32 assemblies to an MSI package. Therefore you should use this built-in functionality to handle GAC installation and uninstallation for you.
I would also like to point out that the Windows Vista Logo Program requirements document contains the following statement:
Gacutil must not be called from a custom action. Gacutil is not designed to be used during installation.
Gacutil must not be called from a custom action. Gacutil is not designed to be used during installation.
This means that if you want to obtain Windows Vista Logo certification for your application, you should use Windows Installer and the built-in GAC installation functionality for your setup.
That's not really a solution for vendors not currently using msi. I assume this is why there is no gacutil installed in vista?
Hi Aaron - This is not the only reason why gacutil is not included in Windows Vista. Gacutil has always been an SDK tool and not a part of the .NET Framework redistributable. It was inadvertantly included as part of the version of the .NET Framework that is installed on Windows Server 2003, but that was a mistake on our part because it led to setup developers using it to install their assemblies instead of the standard MSI functionality.
Can I ask why you are not using MSIs for your solutions (and try to convince you to switch)?
We have an application that has been on the market for over 10 years.
For the last few years we have been using InstallShield.
This means that every time we release a new version our customers can easily upgrade to the latest bits: InstallShield will see the previously installed version and upgrade accordingly (it might be using MSI in the backend, but I don't really care).
If we switched to MSI only how would this upgrade process work? Would we have to tell thousands of our customers to first uninstall the previous version (InstallShield) and then run the new MSI installer?
Even if we were able to easily implement the above, we now need to get new expertise in the team: our current build team knows how to work with the InstallShield projects and not pure MSI.
There's no reason not to leave gacutil in the .NET distributable - it works.
Saying that it was supposed to just be an SDK app is not a reason - it seems like you just want to make it harder to anyone who is not using MSIs.
Personally, I feel that using Windows Installer causes more problems than it's worth.
For instance...when I start a program that was installed with Windows Installer, msiexec runs and makes sure that every-single-advertised-item exists *where it was originally installed* before the program runs. So if I install a program that had created a "My New Folder" somewhere inside of "My Documents", and I deleted that folder at some time, msiexec will keep creating a new one...it's annoying.
Another instance: Advertised shortcuts in the start menu break the "Find Target" function of the shortcut properties page.
Another instance: Installing Program A that uses a dll from Program B and then running Program A sometimes causes Program B's installer to run everytime Program A is executed.
Lastly, there aren't a whole lot of good free simple Windows Installer Setup Creator programs out there. Also, look at Inno Setup and compare one of it's very simple and straight-forward setup scripts to something like a Wise .wsi file and see how over-engineered Windows Installer really is.
Maybe because we as developers want to do something a bit more interesting with our installation routines.
Maynbe because we do cross platform development and want to install on multi platforms.
Maybe we spent so much time developing our own setup we cannot justify switching to msi.
Maybe because msi wasn't available on all platforms without a large download we didn't bother using it.
Maybe we wanted to install just a few files and msi was overkill.
Maybe patching our product is easier when you don't use msi.
Maybe we can control security and distribution more leasily.
Maybe there are more reasons why we don't want to use an msi file so while you force us to register files in the GAC using an msi people will continue to find ways around it.
I think it is called choice.
I personally just copy the file via windows explorer but then I do all the installs myself.
Hi Ehuna, Wayne2k and Digital Wired - Windows Installer has a lot of upgrade options that you can use for your setup. I typically use the "major upgrade" algorithm (described at https://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/major_upgrades.asp). This feature lets you update a couple of variables in your MSI and it will automatically uninstall the previous version and then install the new version without the user having to take any extra actions.
Yes gacutil.exe works, but it is a developer tool, and developer tools go into SDKs and not runtime packages typically. It isn't really appropriate to put more tools into the runtime because that causes it to get larger, which makes it more difficult for applications to redistribute because of increased download size, etc.
We did not leave gacutil.exe out of the redistributable to try to force people to use MSIs or make peoples' lives harder or anything like that.
There are a lot of benefits of using MSIs for setup packages - transactionality, reference counting, servicing, and many others. It is a part of the Windows logo program for this and many other really good (IMO) reasons.
I understand the cost of switching can be a barrier, but I don't agree that an MSI is overkill for small packages or that patching is easier using other means. Patching is a really hard thing to get right in general, and in my experience it is not easy to get it exactly right with MSIs, but it is more reliable and easier to get right with MSIs than with other means. Windows Installer is widely available on all recent operating systems now, so there generally isn't a reason to need to carry the redistributable installer for it in a setup package except in some targeted scenarios. I'm not sure I understand how security or distribution is harder with MSI than with other installers.
I agree there are some odd behaviors with respect to advertised components in MSIs. That has been one of the trickiest things I have tried to debug in the past. There are ways to author an MSI to avoid the advertising issues that are described above though.
For tools - I use the WiX toolset to create my MSI packages. It is a shared source tool available at http://wix.sourceforge.net, and there is a great tutorial at http://www.tramontana.co.hu/wix/ to get you jump started if you're interested in learning more. You can see an example WiX source file at http://astebner.sts.winisp.net/Tools/q.wxs that I think shows how an MSI can be represented in a relatively clean, simple XML format.
I'm definitely in favor of choice when it comes to development strategies, algorithms, tools, etc, but I stand by my recommendation to use MSIs for application setups based on the reasons above and my experience with this and other setup technologies.
Our product is over 15 years old we started with install shield and are currently on version 8, because between myself and QA we haven't had the time to upgrade to the newest version. Although my preference is to ditch install shield and use msi directly. All of our new projects use installshield's msi. So its not a matter of why not, but rather when will there the time. The sad part is i don't like the GAC its problematic, and i don't want to register any dlls there I just have to.
It's been a while since I've actually dug into this, and maybe all the legacy stuff I've been dealing with has been solved, but the big problem I've run into is when you've got a series of ordered DLL/OCX/Assembly registrations (IE, one file's registration depends on another file's registration), the only way to deal with them is by creating a series of custom actions and calling them at the end of the Execute Sequence (I'm speaking in InstallShield terms here). If there's stuff to be put into the GAC, you're pretty much locked into GACUtil?
Hi CWeiss - There are some issues if you rely upon assemblies that are a part of the MSI in other parts of the setup process (such as installing services). Only commit custom actions or those custom actions sequenced after the InstallFinalize action have access to assemblies that are being installed to the GAC using the MsiAssembly and MsiAssemblyName tables during that MSI install transaction. Your options here are to ensure that your dependencies are installed by a prerequisite package or to author a commit custom action to perform the activities that you rely upon the assembly for.
Ah, now I'm remembering why we did what we did - we're using VS2005 assemblies and InstallShield 10.5, which doesn't support the latest Fusion(?) calls. Thus, we were stuck packaging our own GACUtil and installing the assemblies by hand (install to temp folder, then run GACUtil in custom actions at the end of the Execute Sequence).
I'd really love to hear a solid reason for why MSI doesn't call fusion to install assemblies to the GAC until the commit phase. This is my major problem with this pattern. The commit phase was meant to cleanup rollback data, not change system state.
For example I have an install whose requirement is to start a service that not only depends on assemblies in the GAC, but also calls a couple web services that depend on assemblies in the GAC. I can't use the StartServices pattern because of this race condition.
Commit CA's and immeadiate execution CA's after InstallFinalize don't cut it as solutions in my book because of the disable rollback issue and my desire to support managed installations. Nor do I want to ask for a reboot to get the service in the desired state on startup.
What do you mean when you say InstallShield 10.5 doesn't support Fusion? For GAC, InstallShield authors a component with an assembly which is a keyfile and then adds it to the MsiAssembly table. The processing is done at runtime by MSI via the MsiPublishAssemblies action.
This is the way MSI works and there really isn't anything wrong with InstallShield in this respect.
Okay, trying to recall through the haze - For some reason, 10.5 didn't recognize assemblies that were built under VS 2005 as being valid and would throw a 6211 error during the build process (assemblies created in VS 2003 worked fine). The assemblies registered fine when manually running them with GACUtil at the command line on the same machine.
I can't find the emails, but I was under the impression this was fixed in IS 11.
Use this instead
Hi Debose - The link you posted describes how to implement the functionality in gacutil.exe in code. Doing this as part of a setup is no better than running gacutil.exe during setup. My overall point in this blog post is that you should use built-in Windows Installer functionality to install assemblies (the MsiAssembly and MsiAssemblyName tables) instead of using custom actions unless they cannot be avoided for some reason.