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.

1 comment:

Rob Mensching said...

Yep, I hear you. I think everything on your laundry list at the end of this blog post is in the expected to be in the first release of Burn except the multi-CD/DVD spanning. I'm jut not seeing enough products with multiple CD/DVDs to hold our release without that feature. It's definitely needed just won't make the first pass.