Written by WATYF on Friday, 19 August 2005 (7946 hits)
Category: .NET Programming
So.... after I finally got the Beta version of TaskRunner ready, it was time to clean up some of the deployment processes, one of which was making sure that the installer worked how I wanted it to. This involved diving back into the blood-pressure-friendly world of the VS.NET Installer. Oh, how I've missed it.... I remember back in the day.... with my peeps... when I was first releasing TaskRunner 2.0. I have so many fond recollections of pounding my mouse up against my face in unabashed fury while trying to accomplish the simplest of installation tasks. Fortunately, before I lost the last shred of my sanity, I gave up on trying to get the installer to do anything more than the most basic task.... INSTALLING THE FREAKING PROGRAM! At least it could do that much... for the most part.
But apparently, I enjoy pain and suffering so much that I decided to try to work some "advanced" (and I use that term loosely here) features into the installation of Version 3.0.... unfortunately, as with most things involved in Windows programming... things did not go so smoothly...
Let's see... where, oh where, to start... Well first off, I wasted almost all of last night trying to figure out why my program would try to run a "repair" every time I started it using the Programs Menu shortcut. Of course, shortcuts produced by the Windows Installer are not just your ordinary shortcuts.... oh no.... and why would they be??? It's much better that we cram a bunch of "features" into the banal task of creating a stupid shortcut, just so we can open up the possibly of more things going wrong. Who wants a shorcut that... oh... I dunno... simply links to a program??? That's pure rubbish!! We need shortcuts that perform virus checks and defrag the hard drive before they finally get around to actually opening your stupid freaking application!!! So anyway... this problem didn't exist in the old version (2.2), so I was kinda at a loss as to why it had carried over into the new version (since I had copied the Setup project from one version to the next). Well, it ends up that the problem was caused because I had checked a totally unrelated box in the Setup properties regarding the creation of registry keys. I had set DeleteAtUinstall to True (because I wanted to clean up any registry keys that my program may have created), but since some of the registry keys that were marked to be deleted at uninstall hadn't actually been created (since they weren't need), the program was firing off an error whenever I tried to start it. This happend only when using the shortcut, of course... trying to run the executable directly caused no problems. So, because the handy, dandy Windows Installer super-shortcuts go back to the MSI and recheck every freaking aspect of your application, it was causing this error, just because it couldn't find some registry keys that it thought it should be looking for during uninstallation. So in case you missed that... my program was trying to repair itself every time it opened due to a completely unrelated registry key that only should have been checked when the program was uninstalled. I hope I've made it clear how annoying it is that something related only to uninstallation would hose something as simple as starting my program. And that's not even to mention the fact that MS shoulda been smart enough to put a simple "If exists" check in there so that it doesn't throw an exception regarding the deletion of something that's not there.... I mean.... it's not there, so you don't have to worry about deleting it. So, I just turned off the DeleteAtUinstall "feature", since apparently, it was more trouble than it was worth.
So then... today.... I'm trying to work with changing versions and installing copies over older copies and what have you... well... that process isn't very friendly. For example... when I install a version, and then try to install an older version, it will say it installed, but it really hadn't... it still keeps the newer version's executables and what not.... but it produces a second entry into the "Add/Remove Programs" window. So now it looks like you've got two versions installed in the same folder... but you really don't. And the version that you think you just installed over the newer version, didn't really install at all. The solution to this, obviously, is just to set the DetectNewerInstalledVersion property to True, and then it will (usually) give the user an error that they're about to install an old version over a new one, and it won't let them continue. But it sure would be nice if it would just overwrite the newer version if you don't have that property set to true. There's nothing like letting a user think that everything is just fine and dandy, when really, nothing that they just tried to do worked at all.
There are a couple other annoyances, like the fact that conditions can't be used to add shortcuts. I mean really... that's like, the most fundamental aspect of installers... "Would you like to install a Desktop shortcut.... would you like to install a QuickLaunch shortcut... etc". But you have to go and code your own script and add it to a custom action just to get an "advanced" feature like that. Although... I can deal with that... and I think I have come up with a way to get that functionality in... not that it's at the top of my "to-do" list.
But unfortunately, that isn't the end of my problems with the VS.NET installer... no... I've stumbled on yet another wonder of MS innovation... I like to call it, "The muted Mutex."
You see... the TaskRunner uses an embedded database as a backend (which you can read more about in my love poem to embedded databases) and because of this, you can't run more than one instance of it at the same time (because the database can only be accessed by one process at a time). So to get around this issue, I use a little thing called a Mutex. For all you non-nerds out there, think of a Mutex as a kind of territorial flag, to let other programs know, "Hey dude... this is my turf.". It's basically used to provide mutual exclusivity for a process.... ah.... see that...? "mutually exclusive".... man... we nerds are pretty creative, aren't we? Anyway.... I use the following code to create my little Mutex flag when TaskRunner first starts:
Dim mtx As New Mutex(False, "MyProgram")
If Not mtx.WaitOne(0, False) Then
MsgBox("An instance of the program is already running.", MsgBoxStyle.Information)
So... when another process tries to start using the same Mutex, it lets the user know that the same process is already running, and they aren't allowed to continue. Using a Mutex to check for multiple instances isn't the only way to skin this cat... and it does have it flaws. For example, the mutex is system wide (not user specific), so if you have Fast User Switching enabled in Windows XP and someone logs in as another user and tries to start the program, then it won't let them. But that's not an issue for me, because I wouldn't want them to be able to do that... I can only have one instance running, regardless of who the user is. There are a few other ways to accomplish this, though, such as FindWindow (which has its own issues), or checking for running Processes (which is kind of a jerry-rig way of doing it ), or locking a file, etc. etc... and frankly, I may have to experiment with those other methods because of my little "problem".
Speaking of which... back to my problem... So, the mutex method has been working just great for a couple months now (while doing the upgrade).... that is.... until the VS.NET got involved. Somehow... after I create the program by using the installer... the Mutex isn't working anymore... at all... it'll go ahead and try to start the second instance of TaskRunner, which, of course, crashes... because there's already another instance using the database. So now, I have to search far and wide to figure out why the same exact code, in the same exact executable, works just fine when you run it after compiling the program, but doesn't work at all after installing that program using the VS.NET installer. *sigh*
And sure... I could overlook all of this and just be thankful that MS was benevolent enough to give us a free installer package that was built right into VS.NET (especially considering how much the commercial options cost). But where's the fun in that?
(Update: It only took me a few minutes to figure out a new way to check if the app was already running... I just used the lock on the database as the indicator... I didn't even bother researching the Mutex issue... I'm not even gonna waste my time trying to figure out what Voodoo Magic the installer did to it. )