Headaches. Homicidal thoughts. Horrible alliterations.
All these things happened over the the last few days.
It all started when I needed to build 64-bit versions of a few .NET 2.0 apps (a wep app, a windows service and a passel of supporting libraries). This is actually very simple to do. Visual Studio exposes a platform setting in the build configuration for each project. The default platform is anycpu. Specifying x64 sets the compiler option and instructs VS to build a 64-bit image (even on a 32-bit machine). Of course, doing this for a bunch of projects would be dull. Fortunately, I already have NANT scripts to build everything, so I added a platform parameter to the compiler tasks and voila, a slew of 64-bit assemblies in 194 seconds.
Dropped the gooey mess on the 64-bit machine, and by gosh the web app worked and so did the service. Double checked that the 64-bit system was not running any 32-bit assembly in WOW
mode. Nope, all 64-bit baby! Ran the applications through their paces, and they ran perfectly. .NET makes 64-bit apps happen with the flick of a (compiler) switch -- sweet!
Time to build the 64-bit installers for the web app and the service. Now, if 32-bit deployment projects are the 3-eyed, 9-toed, humpbacked, perpetually sniffing step-chilldren of .NET -- and they are -- 64-bit deployment projects make them look like the fairhaired, first born heirs to the kingdom.
Just like any other app, deployment apps can be simply targeted to either platform. But finding the toggle is bit confusing. It is not in the same place as "normal" applications. The platform can't be changed in the build configuration, you must select the Installer in the Solution Explorer and click F4 to expose the properties. Selecting Properties... in the context menu for the installer does NOT expose the same set of properties. OK, a little swearing later, and the web app installer is set to x64 bit and it builds the MSI. Woo hoo, looking good!
Time to test it on the x64 server. An exception! Ugghh. BadFileImageFormat. WTF
. Documentation time. The error says I am trying to run a 64-bit image on a 32-bit platform, but, but, but, I'm running a 64-bit image on 64-bit platform. Some googling, some double checking that a 32-bit assembly hadn't somehow screwed up the MSI. More swearing. Finally stumbled across this blog post
which pointed me in the right direction.
The cause turned out to be a Custom Action
that updates the web.config file, sets some directory permissions and sets up the application in IIS. The Custom Action is an installer class that is kicked off by InstallUtil in the MSI. Worked fine in 32-bit. But, VS embeds the 32-bit version of InstallUtil in the MSI, even when a x64 platform has been targeted, hence the BadFileImageFormat exception.
The workaround required downloading the Platform SDK and installing a tool named Orca
. Orca lets you inspect/alter the MSI guts. Pulled the 64-bit version of InstallUtil from the x64 box and sucked it into the MSI using Orca. Saved the MSI and tried it again. And behold it worked, Custom Action and all. Thank you
The service was bit trickier. I kept getting FileNotFound exceptions in the Windows directory. But I wasn't trying to install anything in the Windows directory, which was a bit perplexing. The problem turned out to be the default directory for installing the service had a space in it. Yes, in 2008, this is still an issue. Changed the default directory to something without spaces, and got around this error only to run into the dreaded InstallUtilLib.dll: Unknown Error., (NULL), (NULL), (NULL)
. More swearing. Well, it turns out that InstallUtil doesn't deal well with the way the installer package escapes path names when sending data to the Custom Action
. So I just sent all the properties without the surrounding quotes (since I was no longer allowing paths with spaces anyway) and the darn thing finally worked.
There's got be a better way, the whole MSI process is so cobbled together, it is astounding that anyone can create a usable installer without third party tools (and even they seem hobbled by the Windows Installer determined quirkiness) or weeks of free time.
Now, all of this could have been avoided if I hadn't used Custom Actions. But there are no Standard Actions for:
- setting directory or file permissions
- setting up virtual directories in IIS
- setting the ASP.NET version and application pool
- updating configuration files
Also, the VS deployment project relies on a Custom Action to install the service, instead of creating a service table in the MSI. Not sure why this is, but tis annoying to know it's there yet not exposed in the deployment project. If I had wanted to dive into Orca, I could have used to it set up the table in the MSI, but my patience had long since expired.
I'm not holding any hope that the situation will improve in the future. As the software world becomes ever more web-centered, there is even less call for building installers. With FTP and Xcopy it's easy enough to deploy web apps. Setting up an app in IIS manually is trivial and .NET removes most of the need for dealing with Registry foo.
If I have a client ask for install package again, I'm giving them a zip file and a page of instructions. Infinitely quicker than dealing with Misfit Solution Installers.