views:

272

answers:

2

We are on the eve of product launch, and at the last minute I am being bombarded with crash reports that appear to be related to our installer, which is a WiX3 project with separate outputs for x86 and x64 builds. These have been an ongoing problem that I always thought were fixed, only to find out that they were still lurking.

The product itself is a collection of binaries that communicate with each other via .Net remoting, including a Windows Service and a small COM component that is loaded as an addon in another app. The service runs as SYSTEM, the COM piece runs in a low-rights context, while the other pieces run in normal user contexts. Other pieces include an third-party COM object library DLL and a shared DLL with the .net Remoting interfaces.

I've observed flat-out weird behavior with MSI, particularly on version upgrades. Between MS' anal strong-name implementation (specifically, the exact version check before loading a given assembly), a documented WiX/MSI bug that sees critical files erased on upgrades (essentially, if a file in the upgrade MSI has the same version number as the existing install, that file is deleted)(edit: having trouble producing said documentation...), and having to work around Wow64 virtualization (x86 MSI can only write to registry/HD locations via Wow64, yet x64 MSIs cannot run on x86 computers...), I am about ready to trash the whole thing and port it over to a different install system.

What I am looking for on tips + tricks, techniques, or suggestions on how to properly do things so that I am not fighting with Windows Installer's twisted sense of logic. I am tired of fighting with WiX/MSI/Windows Installer. All it needs to do is place files and registry keys where I tell it to, upgrade them when appropriate, and don't delete anything until the user uninstalls. Instead, dependencies are deleted willy-nilly, bringing up a whole bunch of uncatchable exceptions (can't wrap a try{} block around function declarations) and GPF'ing the whole app.

I am particularly interested in 'best practices' and examples regarding shared and dependency DLLs, and any tips on making sure if a file needs to go to GAC, that it actually goes to the GAC and stays there until it is appropriate to remove it.

Thanks!

Tom

+1  A: 

Start off by reading The Definitive Guide to Windows Installer.

Done? Great. Now think about scrapping your existing install, starting from scratch and filing an application bug for everything you currently have to "workaround" in setup. For nearly every single bit of "twisted logic" that you've been fighting with is there for a purpose, to make your installation more reliable and repairable.

If you don't want the reliability and resiliency of Windows Installer, or if you're trying to bypass it in any way then use something else. Windows Installer does much, much more than simply just "place files and registry keys where I tell it to".

When you're writing an MSI package, you define how you want the target system to look. That's the way it looks after setup, the way it should automatically repair to if a file is deleted or a key data file is corrupted. You define how the system should roll back to if a user later cancels during an upgrade from 1.0 to 2.0. Windows Installer is 100% data driven. From a development point of view this is the hardest concept to understand, you can't just edit a config file or write some more data and expect it to persist. Once you get your head around this, Windows Installer becomes a real piece of cake and you design your programs and features to work within it's limitations, rather than trying to break free of them :)

A few other useful links...

sascha
I think that I understand more than you think I do. I really like the declarative workflow for MSI... I really do. For complex installations MSI is supposed to be exactly that... cake. I would hate to have to code the logic for a complex install scenario myself. The limitations of the medium are enough to drive an already beleaguered programmer insane, and more importantly, infuriating when one's 'declarations' are being followed inconsistently. These are the problems that my OP grapples with.
Tom the Junglist
I've added a couple more links that provide some useful reading.
sascha
A: 

I'm kind of afraid to step into this one but the limitations are only there if you haven't followed best practices upstream.

Take the "delete files" "bug". I haven't seen this one in a while, but if I recall, this is typically caused by the following scenario:

build A: file 1: 1.0.0.0

build B: file 1: 1.0.0.0

Now you go do a major upgrade where you have RemoveExistingProducts scheduled after CostFinalize. FindRelated Products detects the ProductCode to be removed, Costing says oh I see I have 1.0.0.0 but 1.0.0.0 is already installed so nothing for me to do here and then RemoveExistingProducts comes by and removes the old version thereby deleting file1.

I always worked around this one by scheduling RemoveEXistingProducts way earlier then suggested by Microsoft. It's been working for me for years.

As for dependency hell, I manage a software product line that consists of hundreds of features expressed by hundreds of merge modules and thousands of files ( 15,000 + ) consumed by dozens of installers on dozens of various feature, integration, main, maintenance and maintenance_integ branches.

How do I do it? One fragment at a time and lot's of automation, SCM and virtual machines to make sure that everything does actually work the way it's intended.

Being this close to your product shipping, the only "answer" I can offer truely offer you is to let you know people like myself are always available for hire. You'd be suprised how fast some of us can turn projects around and get software shipping.

Christopher Painter