6 Steps to Writing a Better Windows Setup.exe
Yesterday's post on software installers mostly explained why the Windows software installation process is gross. But when I've written installers, even for Windows, they're not too gross. Here's some advice you can actually use.
- Make the installer as small as possible Writing the installer really is a pretty unglamourous job to get stuck with, mostly because debugging it is such a pain. After all, it takes forever to run, and it leaves gunk all over your registry. And it breaks every time someone adds one more new file to the build, so it's much easier to write it at the very end of the cycle, right? There's an easy solution to this: minimize the amount of work that the installer actually does. Move all setup and configuration code to the application; the less that's in the installer, the less there is to debug, super-slowly, at the last minute right before release. Specifically, why on earth are you creating all your app's registry entries in the installer? Does that mean you've created them all by hand on your development machine? You mean all your developers did that, for the entire development cycle? Why? Why not just have your app create them automatically from default values if it can't find them at startup? Nitix puts all configuration outside the install process, and it's been easy and painless to maintain for years. I'd never do it any other way.
- Use a better installer platform Use a good, fast installer like NSIS. Ditch InstallShield, it's terrible. NSIS is worth it just for the speed increase alone. I understand that the end user doesn't care how fast the installer runs; after all, they only run it once. But don't do it for the user, do it for yourself: that speed increase translates directly into a shorter compile-run-crash cycle for you, the installer developer. And that means you'll write better code in less time. You really will, trust me.
- Use VMware Snapshots Install a fresh Windows system into VMware, take a snapshot, install your application, rewind, and repeat. Your test process isn't depending on the fact that your uninstaller really uninstalls everything, right? You're not reinstalling Windows from scratch every time, are you? Statistically speaking, you probably are. Stop it. VMware is free.
- Use static linking Whenever possible, just link things statically into your application and make one big .exe file. Don't worry, Windows is smart enough to only page in parts as you need them. DLLs are only a memory saver if they're shared between apps - but they almost never are anymore, because everyone is desperately trying to avoid DLL hell. The default Delphi behaviour is to statically link everything; take their advice, it works.
- Always just install everything Every installer always seems to have the stupid "Custom Install" option where you can choose which modules you do or don't want. Once upon a time, this made sense: why, you could cut an installation down from 40 megs to less than 10, if you were careful! But nowadays it's just a disaster. First of all apps that do this nowadays don't seem to have the same percentage flexibility in install size: either you install 1.2 gigs of stuff, or 1.15 gigs of stuff, or anywhere in between. Gee, thanks, that's a lot of flexibility, guys. It's still a 50 megabyte difference, but it just doesn't matter anymore. Secondly, every optional installation feature exponentially increases the number of combinations you need to test. And you're probably writing your installer right at the last minute. Are you really testing all the combinations? I bet you aren't. Try this: pick a random application. Uncheck all the boxes in the custom installation dialog. See how small it gets (not small). Now install it. See if it runs. Chances are, something weird will explode when you run it. Why? Well, because nobody actually does that, and certainly nobody tests it. If someone calls in to tech support about the problem, guess what they say? "Reinstall it with the default settings and it'll be fine." If you're not going to test the option, just leave it out.
- "Kram" your data files into the .exe too Surprisingly, one of the slowest parts of installation is creating and copying a million tiny files: the Windows filesystem is horrendously slow at creating files, but fast at reading and writing existing files. Here's a trick some DOS programs were using in the early 1990's and it still works today (on DOS, Windows, Mac, and Unix!): just concatenate your data files to the end of your .exe as part of the build process, and tack an index (with the filenames, offsets, and lengths) on the end. You'll be amazed at the order-of-magnitude decrease in install time. And you can do some neat tricks with this too: for example, if you often load a whole bunch of related files at almost the same time (eg. an html file and its contained images), you can improve "locality of reference" on disk by simply concatenating the files together in the right order. If you create them as separate files, Windows has to guess (usually incorrectly) which ones belong together. But if they're all in one big file, Windows knows they're related and will try to keep them together on the disk. Useless trivia: this is also how "self-extracting" zip files work. There's no magic whatsover: just take your unzip.exe and tack the .zip file to the end. (Of course, your unzip.exe has to be smart enough to seek from the end of the file instead of the start, since the zip file no longer starts at offset 0.) I used to see this trick all the time in DOS-based "demo" programs - they'd be distributed as a single .exe that contained all the music and images tacked to the end. It's a shame this idea has been mostly abandoned, because it still works great and makes your application seem a lot less unwieldy.
ssh+2FA to all your machines, anywhere, without opening firewall ports.