Channels ▼
RSS

Writing Your Own Install and Uninstall Code


Writing Your Own Install and Uninstall Code


Shrinking the DLL

When I wrote my Windows program What To Do, I knew I was going write my own install and uninstall code. I wanted to have full control over what users saw from the moment they began their install to the last thing they saw during a possible uninstall. This article covers some of the techniques I found most useful.

Auto Uninstall

The most challenging part of writing the uninstaller was figuring out how to make it delete itself after it was done removing program files and directories. I wanted it to work on everything from Windows 95 to Windows XP, without making the user download any additional components. I searched on the Web and found several references to self-deleting executables, but all the proposed solutions had problems. Most only worked with certain versions of Windows. Others modified thread priorities in ways that could cause timing problems. Some caused extra windows or error messages to appear. I figured there must be a better solution. I discovered that you can use a self-deleting DLL to create a self-deleting executable with none of the limitations of previous solutions.

The Windows rundll32.exe Utility

How can we execute code in a DLL without creating an EXE to load and call it? Every Windows version beginning with Windows 95 has shipped with a system utility called "rundll32.exe." It allows you to execute any function exported from a DLL. You use it like this:

rundll32.exe DllName,ExportedName args

ExportedName is the exported name of the function in the DLL. When writing a DLL to use with rundll32, I declare the function to be exported like this:

extern "C" __declspec(dllexport)
void CALLBACK FunctionName (
 HWND hwnd,
 HINSTANCE hInstance,
 LPTSTR lpCmdLine,
 int nCmdShow
)
{ ... }

The rundll32.exe documentation lists the function arguments, but empirically, I've found that the only value you can rely on is lpCmdLine, which receives the value of args passed when you run rundll32.exe. Using __declspec(dllexport) causes the function to be exported, and using extern"C" makes the exported name _FunctionName@16 (the function name gets mangled to include the size of the function arguments).

rundll32.exe loads the specified DLL and then calls the exported function passing as args the value of lpCmdLine. The official documentation for rundll32.exe can be found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/rundll32.asp.

A Self-Deleting DLL

Listing 1 shows the source code for a DLL that will delete a file and then delete itself. DllMain will be called when the DLL is first loaded, and there it records the module handle, which will be used later to get the DLL's filename.

In the function MagicDel, lpCmdLine will be the name of the executable file that the DLL should delete (for example, the uninstaller's filename). Deleting it is easy — sleep for a while to allow the executable's process time to exit and then call DeleteFile. To get fancier, pass the handle of the executable's process to MagicDel and wait on it before calling DeleteFile.

Making the DLL delete itself is a little trickier. rundll32 calls LoadModule to load the DLL into its address space. If the DLL function was allowed to return, rundll32 would just exit, causing the DLL to be released (but not deleted). Instead of letting that happen, we want to execute the following code:

FreeLibrary(DLL module handle);
DeleteFile(DLL filename);
ExitProcess(0);

The function can't just make this sequence of calls directly because FreeLibary would make the code page invalid. To work around this, the function pushes an equivalent set of assembler instructions onto the stack and then begins executing them with a ret instruction. The final call to ExitProcess prevents the process from trying to run any more of the code. I created the assembly code block using the same technique that Gary Nebbit presented in a former Windows Developer Network "Tech Tip" (see http://www.windevnet.com/documents/s=7257/wdj0109g/).

If you build the DLL using Visual Studio with default options, the resulting binary is about 40K. By removing the unused C runtime code, you can shrink it to 2.5K (see the sidebar "Shrinking the DLL").

A Self-Deleting Executable

An executable can delete itself by storing a copy of the self-deleting DLL as a resource, then recreating it and launching a process running rundll32.exe to do the deletion.

Listing 2 shows the header and resource files used to store the DLL as a resource. Resource type values 256 and above are available for user-defined types. Alternately, the DLL binary could be stored directly in source as an array of bytes.

Listing 3 shows the rest of the code for the executable. WriteResourceToFile accesses the binary resource so that it can recreate the DLL on disk. The Windows Resource APIs provide a pointer to the raw data.

SelfDelete recreates the DLL and then builds the command line to launch rundll32.exe. The command line will look like this:

path\rundll32.exe magicdel.dll,_MagicDel@16 path\executableName

rundll32.exe resides in either the Windows or System directory, so SelfDelete tests to find the right location. When CreateProcess is called to execute the command line, it sets the STARTF_FORCEOFFFEEDBACK flag to prevent Windows from displaying the busy cursor while rundll32.exe runs. That way, there will be no indication to the user that a new process is running. After this new process exits, both the DLL and the original executable are gone.

To make a self-deleting executable that does not depend on the C runtime library DLL, the executable must statically link in the runtime library code. Change the project options under C/C++/Code Generation to set "Runtime Library" to either "Single Threaded" or "Multi Threaded" (or any value that does not include the DLL).

This self-deleting technique works reliably with all Windows versions. The actual uninstaller for my Windows program first copies itself to the Windows temp directory so that it can remove all program files and directories. Finally, it uses the self-deleting DLL to delete itself.

Install

I knew how I wanted my installation program to work. The user would just download my executable over the Internet and run it. I wanted the file to be compressed for faster downloading, and I wanted the first thing the user saw to be my program instead of some other company's installer. Fortunately, Windows provided just the support I needed.

First, I created an interactive setup program that displays a license agreement, prompts the user for install choices, copies files, and does the rest of the setup work. Then I stored a compressed version of that setup program as a binary resource inside the installer. All the installer has to do is write the binary resource to disk, decompress it, and launch it as a new process. Storing and writing the binary resource was easy — I used the aforementioned binary resource handling code.

Every Windows platform since Windows 95 has had an API for decompressing files — LZCopy. Listing 4 shows the source for the installer that makes use of it. The compressed setup program is stored as a binary resource, just as before. DecompressFile shows how to use the LZCopy API. The installer recreates the AppSetup.exe, and then runs it. To build successfully, the installer executable needs to add lz32.lib to the project options under Linker/Input/Additional Dependencies. And as before, statically link in the runtime library code by changing the project options under C/C++/Code Generation to set "Runtime Library" to either "Single Threaded" or "Multi Threaded." Note that the installer doesn't have to wait for the setup program to complete because AppSetup.exe can use the self-deleting DLL to delete itself when it's done.

The trickiest part of using LZCopy is that it seems to only decompress files that have been compressed with the Microsoft compression utility compress.exe, a command-line utility that used to ship in some Microsoft SDKs. The only official source for it now appears to be as part of a self-extracting archive available at ftp://ftp.microsoft.com/ softlib/mslfiles/CP0982.EXE. If you download and run that, it will unpack compress.exe (as well as several other files that you can ignore and delete). You use it like this:

compress SourceName DestinationName

Using the decompression support built into all versions of Windows made it easy to write the installer. Note that all Windows versions include the utility expand.exe, which allows you to decompress files from the command line.

Complete Control

Using a self-deleting DLL, binary resources, and the decompression support built into Windows can help you create your own installer and uninstaller. And that allows you to control every facet of the experience from the moment users begin installing your software.
w::d


Alex Tilles is the creator of What To Do, a to-do list manager that adapts to the way you organize. He was also a development lead at Microsoft for several years in the Systems and Internal Tools groups. He can be reached at alex@handcraftedbytes.com.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video