Thursday, August 27, 2009

Wix way should you go?

Now that I know Rob still reads my mostly stagnant blog, I guess it is the appropriate time to write a long-overdue post.

When I started working with Windows Installer technology, back when it was first introduced with Office 2000, I played around with customizing the package for the IT department to push out a customized version. The tools were quite primitive, the technology was new and largely unknown, and the concept of having blogs, yet alone Microsoft folks blogging, seemed completely foreign. Support was pretty much nonexistent, and much of the documentation was unintelligible. Fast-forward 10 years, and what a difference that makes! Today there are several free and low-cost repackaging tools for transitioning non-Windows Installer based setups to the MSI format, authoring tools, and lots of community support.

Most setup authoring tools have significant issues. Non-MSI or script based installations have issues because they encourage hacks - I can't tell you how many installations I encountered that install services by writing keys to the CurrentControlSet hive and forcing you to reboot merely so the Service Control Manager can pick up that addition. Furthermore, if you are targeting any sort of enterprise where more than one of your setups will be installed IT departments want MSI deployments for very good reasons. GUI based Windows Installer tools fail to do a good job of grouping related things into the same component, and dynamically adding a directory of files at build time breaks patching semantics horribly. Another big disadvantage to these tools lie in the setup author because he or she does not need to understand the underlying technology and can get away with "programming by coincidence" (as described in The Pragmatic Programmer).

I remember several paradigm shifts throughout my experiences with setup technology - nested MSIs, merge module distribution, and chaining installations. During this time the stock price of Rolaids likely skyrocketed. The biggest challenge was attempting to get developers to take a more proactive approach to deployment considerations as they were writing their code. One approach that I took was the use of merge modules - developers of feature-units would package their build output in an MSM that was consumed when building the final product. Using Visual Studio 2005+ with their deployment projects was not only difficult, but downright impossible because of how limited, shortsighted, and buggy deployment projects are. Adding custom actions to these modules involved a complex and convoluted post-build scripting process that nobody understood, but it DID move teams towards the direction of thinking of deployment while coding.

These days, the tag-team of MSBuild plus Wix 3.0 is THE enabler to accomplishing those goals and largely eliminating the disadvantages of the GUI-based tools. Since there is close to a one-to-one correlation of XML elements to the Windows Installer tables, it is quite simple to follow if you understand the underlying Windows Installer engine. To use WiX to author a complete installation, you MUST have an understanding of the Windows Installer engine. To make a few tweaks or additions once the basic skeleton of the installer is laid out, just about any developer can do it provided access to the WiX documentation. I have team members that are NOT setup developers add services, event log sources, and more with no official training.

Some of the more compelling points in favor of WiX is how you can use it to easily and properly make multiple product editions which share components, separate units of related components into their own WXS file(s) for easier understanding and maintenance, and integrate it easily as a first-class citizen into an MSBuild project. No other product is available to my knowledge that accomplishes those goals. Best of all - WiX is free, fast, and easily installable onto any developer machine.

If you are looking to switch authoring tools, take WiX for a test run by using the dark.exe decompiler to convert your existing MSIs and play around with it a bit. Subscribe to the WiX mailing list and ask a few questions. You just might like it.

Congratulations to Rob and the entire team and individuals who have contributed to it, as well as the community of developers who support it via the mailing list on a daily basis. If you are ever in Pittsburgh, let me know. I'll buy you a beer.

Tuesday, August 04, 2009

Visual Studio 2008 GenerateBootstrapper task and UAC

I am extremely disappointed in the Visual Studio 2008 bootstrapper to say the least. While the concept of a bootstrapper is great, it is obvious that the designers did not look at real-world deployment scenarios.

Let me give one example scenario: Installing prerequisite packages and interacting with Vista's User Access Control (UAC). Most of the time, the author of the bootstrapper (by the way that packages are authored and selected) is pretty confident as to how things are required (and I use the word required here very specifically) to be installed: per-user or per-machine. In some cases, such as the .NET framework and many third party redistributables, this is a per-machine only situation. Perhaps some components could be installed per-user, but generally if one part is installed per-user, then all parts capable of being installed per-user should be.

The above understanding should certainly be incorporated as part of a bootstrapper from the start. If some components require a per-machine installation, UAC should be prompted for once, start the elevated process, and this elevated process could be 'commanded' by the non-elevated bootstrapper main process. If per-user components exist, the option to install all of them could be given to the user, and appropriate ALLUSERS=x or command line arguments could be passed to the chained installations capable of doing per-user installations, and the elevated or non-elevated boostrapper process is used based on the requirements of the package and user selection.

Instead, the VS 2008 bootstrapper prompts for elevation of each chained installation, even though the entire POINT of a bootstrapper is a single entry point and interaction for the user to get a piece of software installed onto a users machine. We as developers consider setups a massively coordinated symphony of prerequisites and third party components. The user considers software they purchase or wish to install as a single piece and a single process, and it should seem this way to them.

For setups that are rather long and/or chained-installation heavy, prompting after each chained installation is aggravating to the user and is completely unnecessary from their perspective. This design flaw requires the user to babysit the entire process. We as developers hope that the user chooses to elevate each one - if not the entire setup fails with a rather hopeless error message requiring a support call.

Corporate users are the only ones who generally care about per-user installations, and generally can follow a dependency chain of installations to push apps to the users. Most of them will NOT use our generated bootstrapper, anyway.

The point of this post is to tell you how to work around the 'elevate for each' behavior of the generated setup.exe by hacking the setup bootstrapper used by the MSBuild GenerateBootstrapper task so that it prompts for elevation once at startup and installs everything elevated. This is not ideal because sometimes it is not necessary to elevate, but it certainly makes the end-user result better for the 'happy path' for 99% of all users.

Normal caveats apply - if you break something, its not my fault. Keep a backup of all modified files. The solution presented here will change the behavior for ALL GenerateBootstrapper build tasks unless as part of your msbuild script you replace the appropriate file with the version that gives you the appropriate results.

The Windows SDK actually contains this bootstrapper, and it can be located here on a default installation on a 'normal' PC: C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Engine\setup.bin. Copy this somewhere and rename it to setup.exe. In Visual Studio 2008, do a File->Open->File and find your setup.exe. This will open up a resource viewer/editor. The RT_MANIFEST 'folder' is what you want to expand. Right-click on "1" and export it, saving it as a text file. Open the text file and find the requestedExecutionLevel node - change the level attribute's value to 'requireAdministrator'. Use CTRL-A and CTRL-C to copy the contents of this file to the clipboard. Back at the resource editor, double-click the '1' entry to get it in the hex editor. Select all but the first three bytes and paste your clipboard contents in there. Save and exit. Now rename the setup.exe back to setup.bin and replace the original file, saving a backup, of course. The next time your build happens, the setup.exe will now use the new manifest, giving you one prompt and doing EVERYTHING elevated.

I won't get into the other issues with this bootstrapper - no autogeneration of MSI logs, no timestamps in its own log, immediate run and exit that hinders seld-extracting archive packages for single-file download, no MSI caching for patch scenarios like the Office team uses, spanning over multiple CDs/DVDs etc. I will wait for Wix 3.5's Burn to hopefully resolve these issues and the ones not mentioned. Rob Mensching, are you listening? I'll even volunteer to help write it.