For most of us, the term "DLL Hell" is a Windows throwback from the '90s that we never thought we'd hear again. But not only has DLL Hell come back on the Windows programming scene, it has done so with a vengence: In the 1990s, your application sometimes crashed due to use of the incorect DLL version. In 2010, your application may not run at all.
DLLs were introduced with the first releases of Windows, mainly for the following of reasons:
- It's a fundamental part of the operating system. Most system parts are provided by DLLs, so that many programs can use them at the same time (i.e. to draw text).
- To save disk space and RAM (disks as well as RAM were very expensive and of limited size at the time).
- To make system parts serviceable by replacing DLLs by a newer version with fixes and improvements and make these updates available to all programs instantly.
DLLs were not only introduced for the core OS, but made available for programs as well. Microsoft frameworks such as MFC, .NET, ATL, GdiPlus, etc. as well as for the C (CRT) and C++ runtime libraries made use of DLLs. But in the process, a complex mechanism was exposed: Parts of programs (Microsoft's DLLs) became maintainable without notice to (or even a chance to intervene by) the program.
Since an OS is a "living thing" with new hardware, drivers, and viruses showing every day, it is a good idea to have the capability to handle new problems without installing a entire new OS. This is a different story for programs, however. While programs should be maintained and possibly update themselves frequently, like the OS they should do so consistently. If we update some parts of a program, the program itself might be confronted with modified behavior that it is not prepared for. Which leads us to the original problem that DLLs introduced: What is part of the OS, and what belongs to a program?
Everything would have been okay if the implementation of DLLs would have been clean and non-ambiguous. Unfortunately that wasn't the case, as Microsoft and others began releasing different versions of the same (system and non-system) DLLs that had no proper versioning. Installers of programs overwrote existing versions of DLLs, leading to the first iteration of DLL-Hell: Installing an application could possibly break other applications (because they were build for a different "version" of the DLL). And even worse, uninstalling an application could remove some DLLs that other applications depended on. Most applications installed their DLLs in the system directory (and many still do) in hope to share them with other programs but there was no way to keep two different versions of a DLL in the system folder as the file name usually remained unchanged.
Many steps were taken to stop DLL-Hell. Some helped, but all of them introduced new problems -- file protection, version resources, the system keeping a usage count for each DLL (installers asking you on unistallation to confirm the removal of a specific DLL). Ron Burks wrote a delicious article entitledA Brief History of Windows Programming Revolutions in which he detailed the process.
The most recent approach to get rid of these problems is the side-by-side manager introduced with Windows XP. According to Microsoft online documentation:
Side-by-side assemblies are used by the operating system as fundamental units of naming, binding, versioning, deployment, and configuration. Every side-by-side assembly has a unique identity. One of the attributes of the assembly identity is its version....
Starting with Windows XP, multiple versions of side-by-side assemblies can be used by applications running at the same time. Manifests, and the assembly version number, are used by the loader to determine the correct binding of assembly versions to applications.
Let's start by defining a couple of terms:
- Assembly: A number of DLLs boundled together with a manifest file.
- Manifest: An XML-File containing a unique ID for the assembly as well as the exact version number (the same which is included in the VERSIONINFO resource of each DLL) and information about the assemblies update policy.
Correct versioning of software is a critical. A version number is a unique ID of a software package. You can think of it like a person's fingerprint. Which makes it surprising how little care vendors sometimes take care with it. A common denominator of version numbers may be:
- The first and the second part of the version number specifies a products release. Different releases usually have different functionality and other major differences.
- The next one (or if existent the next two) part of the number specify the bug fix (also called "maintenance") and/or build number. Different versions of those parts should not have modified functionality, higher numbers should indicate more stable releases. Of course, in practice sometimes bug fixes introduce unwanted modification of existing behavior. Sometimes without notice to the manufacturer notwithstanding thorough testing. Sometimes accepted as a welcome improvement. Sometimes introducing problems.
Keeping versions properly distinct is easy. Some manufacturers just use the whole version String in the filename of the DLL. Another approach is based on the version info resource etc. As a general rule no two released binaries never shall have identical version numbers.