I recently ran into a problem where I required a self-extracting installer that would allow customers to download one single huge file in order for them to choose from a variety of setups or associated documents, like readme files or release notes. Consider a company that ships a product suite that is made up of different components like a server installation, a client installation, an administrative installation, and a command line tools installation. If each of those components has a separate setup program or an MSI file that customers have to download, this is soon going to be tedious for the customer. Having all relevant setups for each component combined in one big self-extracting installer with a user interface that allows for the selection of an individual component would be the ideal solution for this problem.
I came up with the following requirements for the solution to this problem:
- The individual setups should be stored in a compressed format in the self-extracting installer.
- The compression format should be a standard format.
- The self-extracting installer should run on Windows 2000 and higher OS versions, and should be entirely UNICODE.
- The individual setups to be extracted should be extracted on an on-demand basis, so not all embedded setups are extracted during program start. This way, only the minimum temporary storage is required on the user's hard disk, and program startup is as fast as possible.
- Every setup component should be startable either via the execution of a program whose command line and executable can be specified, or be ShellExecute'd via the setup component's file extension. An MSI file should be started via MSI APIs so that the MSI's user interface is parented to the self-extracting installer window. This way, MSI setups appear to be much better integrated into the self-extracting installer.
- The programs that make up the self-extracting installer should be reusable. There must be a convenient way to create a custom self-extracting installer with a program that serves as sort of a self-extracting installer editor.
As the compression format, I chose the zip format. There are a number of constraints or limitations in using the zip format, that I will outline below. At creation time of the self-extracting installer, a zip file, containing all files that make up the installation, is appended to a stub file (sfx.exe). The stub will extract a couple of files, immediately at runtime, into a temporary directory, and then launch a child process (setupui.exe) that implements the user interface that can be seen in the screenshot at the top of the article. The creation of a self-extracting installer is done with the program sfxmaker.exe that is built as part of this project. Using sfxmaker.exe, you can specify which files go into the self-extracting installer and which components of the self-extracting installer are offered to the user at runtime. You can also brand your self-extracting installer with your company's name so that the self-extracting installer's file version information contains your company and product name. In addition, several localization aspects can be done with sfxmaker, such as the text on the labels and buttons of the self-extracting installer's user interface.
For each component to be installed, you can specify a custom icon that appears in the list control of the self-extracting installer next to the component's description. For this to work, those icon files have to be added to the project, and must be extracted during startup of the self-extracting installer so they can be shown immediately. As a convention, all files to be extracted at startup of the self-extracting installer must start with "setupui", so these icon files must have names that start with "setupui" as well. Some files to be extracted at startup of the self-extracting installer have predefined names: If there is a file named setupui.bmp, it will be displayed on the self-extracting installer user interface like the bitmap with the bubbles in the screenshot above. If a file named setupui.ico is contained in the self-extracting installer package, it will be used as the icon of the self-extracting installer when Alt-tabbing between applications, so it is best if you add your product's icon file as setupui.ico to the installer package in sfxmaker. If a file named setupui.avi is contained in the self-extracting installer package, it will be displayed in a progress dialog if a setup program is invoked from the self-extracting installer while the installation runs, unless the setup is an MSI installation.
Sfxmaker.exe is a document view model based MFC application that works with documents with the file name extension sfx. One such sfx document created by sfxmaker.exe describes exactly one self-extracting installer binary, and has the format of a Windows INI file, albeit in UNICODE format (UTF-16). This makes an SFX file human readable, and also perfectly suited for source control systems. At creation time of the self-extracting installer package, this SFX document file is added as setupui.ini to the zip file, and setupui.exe can extract all relevant information at runtime from this file.
Seeing the Installer at Work
In order to get an impression of how such a self-extracting installer package works, please download the self-extracting installer demo, unpack and execute it. This demo shows the fictitious foobar application suite that shows the various components in the list control that can be installed or launched. The first two components launch MSI files for the foobar server and client. These two components show the special treatment of MSI based setups, because the self-extracting installer window acts as a parent window for the MSI installer windows. Now, launch the third setup, the foobar tools setup. This setup is also MSI based, but it is launched via a special command line using msiexec. Notice that there is no parent-child relationship of the self-extracting installer window and the MSI user interface, in this case, and that the self-extracting installer shows a progress dialog while this setup is running. You can override the relatively boring animation in this progress dialog if you specify a setupui.avi file in your self-extracting installer package, as mentioned before. The fourth component starts an executable that demos how you can add arbitrary EXE files to be executed from your self-extracting installer package. Notice that you will also see the progress dialog in this case.
The last two entries allow the user to launch a PDF file with the release notes and a readme text file. For both components, the icons that are displayed in the list control are extracted at runtime from the associated applications, which are Acrobat Reader and Notepad, in most cases. Extracting the associated icons at runtime helps you display the Acrobat icon without adding it to your installer package, which would be a violation of Adobe's copyrights. If there is no associated application with a file to be ShellExecute'd, like it is done with the readme file or the PDF file, then there is simply no icon visible for the list control entry of the setup component.
In this paragraph, I will show how you can recreate the self-extracting installer demo from existing source files. This way, you will get a rough idea of what you have to do in order to create your own self-extracting installer for your product. First, download the demo project for a self-extracting installer, and unzip it with directory preservation into the drive c:\ root directory. The files that make up this demo project should now all be in the directory c:\sfxdemo. You will see that this directory contains the individual MSI files for the foobar client, server, and tools installation. You will also see a couple of *.ico files for the individual setups, and the readme.txt and the relnote.pdf files. Now, download the project's source code into an empty directory of your hard disk, and find the release directory with the sfxmaker.exe file in it, and launch it. The user interface of sfxmaker.exe will look like:
Now, open the file c:\sfxdemo\sfxdemo.sfx in sfxmaker and notice how all the user interface elements are populated:
- The label "Company name" contains the name of your company, and is shown in the version info resource of the final self-extracting installer.
- The label "Product name" contains the name of your product, and is also shown in the version info resource of the final self-extracting installer.
- The list control under the label "Installation components" contains information about the installation components in the order in which they appear in the self-extracting installer's list control. You can move individual items or add and delete new items with the buttons at the right side of the list control. You can change the values for each item with a double click on the item. This will invoke a dialog where you can change the individual parameters of the component.
- The list box under the label "Files" contains all the files that are compressed into the zip file that is appended to the stub to create the self-extracting installer. Use the buttons at the right side of this list box to add or delete files. The order of the list box items is irrelevant.
- The text you can specify next to the label "Header text" is the text that appears in the label above the component list control of the resulting self-extracting installer.
- The text you can specify next to the label "Title text" is the text that appears as the window caption of the resulting self-extracting installer.
- The text you can specify next to the label "Install button text" is the text that appears on the button that launches an installation in the resulting self-extracting installer.
- The text you can specify next to the label "Quit button text" is the text that appears on the button that exits the resulting self-extracting installer.
- The combo box with the label "Vista execution level" specifies the execution level of the resulting self-extracting installer when run on Windows Vista.
Now, click the button labelled "Create SFX". This will prompt you now for the name and location of the resulting self-extracting installer. Choose a name and location of your liking, and click OK. It will now take a few seconds, and then the self-extracting installer will have been created for you.
Adding and Updating Components
If you add a new component or update an existing one in sfxmaker, a dialog like the following will be presented to you:
The items that you can specify for each component here are as follows:
- Description: This is the text that appears in the list control of the resulting self-extracting installer for the component.
- Command line: This is an optional command line for the component that is executed if the installation button is pressed for the component in the resulting self-extracting installer, or if the component is double-clicked.
- Application: Here, you can specify the application path for the file to be executed in addition to the command line.
- File to unpack: This is the name of the file as it resides in the zip file of the self-extracting installer. If the installation button is pressed for the component in the resulting self-extracting installer, or if the component is double-clicked, then this file will be extracted into the temporary directory, and then optionally either the command line will be launched, or the file will be ShellExecute'd.
- Icon file: This is the icon file that will be shown next to the component text in the user interface of the resulting self-extracting installer. If this value is empty, the icon will be determined at runtime from the unpacked file's extension. If you specify an icon file here, make sure its name starts with "setupui".
- Text: This is the text that appears under the list control in the user interface of the resulting self-extracting installer if the component is selected.
For the "Application" and "Command Line" items, you can specify text that contains environment variables. They will be resolved at runtime by the self-extracting installer. You can use the special environment variable
CURRENTDIRECTORY. This environment variable expands, at runtime, to the temporary directory where the files are uncompressed to by the self-extracting installer. Look at how the "Foobar Tools 1.0" installation in the demo project uses this variable so msiexec.exe finds the MSI file. Make sure that you add a corresponding file name to the "Files" list box for every component's "File to unpack" you add. Also make sure you add each icon file to the "Files" list box.
Using the Code
Use Visual Studio 2005 with a Vista SDK or a Server 2008 SDK integrated to compile the project. A bare bones Visual Studio 2005 installation will not work because for some reason beyond my imagination, Visual Studio 2005 is missing the import library for msi.dll, msi.lib. In addition, I heavily use
PREfast __analysis_assert statements in my code in order to make it PREfast-clean. You can also use Visual Studio 2008 which will convert the solution and project files for you into the Visual Studio 2008 format. For both development environments, you need a variant that contains MFC, so the express editions will not suffice. In order to compile the project files, first extract the source code for this article recursively into an empty directory on your hard disk. Under the project's sfx directory, create a subdirectory named zlib123 which then should be in parallel with the subdirectories include, lib, mc, setupui, sfxmaker, sfx zlibstat, and sfxcommn. This is because you also need the current version of the zlib library, version 1.2.3. Download a copy of the library here. Unzip the zlib files into this directory, again with directory preservation. Now, you should be able to compile the whole project.
The current implementations suffer from three main limitations, you have to judge yourself if they are problems for you:
- The file names embedded in the self-extracting installer should better contain 7-bit ASCII characters only. Although the entire project is UNICODE only, file names in zip archives are not UNICODE. So, in order to prevent any problems, use 7-bit ASCII characters for file names.
- The total sum of files to be added to a project may not exceed 2 GB in size. This again is a limitation of the zip format.
- Currently, all the files that are embedded in the zip file of the resulting self-extracting installer are contained in the root directory of the zip file. This way, all files added to the project must have unique names.
Extracting All Files
Using the command line parameter
-x you can extract the content of a self-extracting installer into a directory. Specifying only
-e as the command line parameter will show a directory picker dialog that you can use to select or create a directory, where all files in the self-extracting installer should be extracted to. If you use
-x=targetdirectory as the command line parameter, all files will be extracted to the target directory without showing the directory picker dialog. So for example, if you specify
-e=c:\foobar, all files will be extracted to the directory c:\foobar. If necessary, the directory will also be created.
You can open a self-extracting installer built with the code and binaries from this article with Total Commander, much like a zip file. Just select the file and type Ctrl-Pagedown. You can then select individual files from the self-extracting binary and copy them from there to somewhere else.
It would be interesting to change the user interface that is presented via setupui.exe to an HTA-based user interface. This way, the design of the UI could be entirely left to the user and his/her HTML skills. Adding a command line option to sfxmaker to run it in an automated way from a build script would also be something real cool.
- Version 184.108.40.206 - 10/19/2008
- 10/23/2008 Edit
- Added a hint that for Visual Studio 2005, a recent SDK is required, because of the missing msi.lib in Visual Studio 2005 and missing
PREfast support in Visual Studio 2005 without a recent SDK
- Version 220.127.116.11 - 11/28/2008
- Added the option to extract all files from a self-extracting installer using the
-x command line parameters
- Version 18.104.22.168 - 11/28/2008
- Fixed bug with failing installer starts on Vista with execution level set to "run as invoker"
- Added forgotten serialization of Vista execution levels
- Version 22.214.171.124 - 12/12/2008
- Added handling for
-h command line parameters which will show a messagebox with an explanation of all command line parameters
- Made UI a bit larger