What is DLL Hell Problem and how to solve it?
Whenever you install a program on your computer, a DLL file is saved in the registry. Now when you install another program with the same name, the old DLL file is overwritten by the new DLL file.
This is great for the new application but not so for the old one, as the program will no longer run properly. This is the simplest example of the DLL Hell problem. In this post, we will understand more in detail about this classic problem and learn how to solve it.
Table of contents
- What is the DLL Hell problem?
- The Solution to this problem
- Modern DLL Hell problems
- Possible solutions for the above issue
What is the DLL Hell problem?
DLL Hell is a situation in which two separate applications share a common assembly, and if one of the applications makes modifications to the common assembly that isn't backward compatible, the other application crashes.
Let's look at an example to better understand this. Imagine you have two applications, Application1 and Application2, and both of them share a common Assembly, which we'll refer to as Shared.
Now, GetOperation is used by both applications ().
Now, both applications are up and running. However, after a while, Application2 decides to update itself. During the modification, Application2 renames the GetOperation() function in Shared assembly to GetOperationNew(). After that, Application2 works fine because it calls GetOperationNew() from Shared Assembly, whereas Application1 crashes. This is referred to as the DLL hell problem.
The solution to this problem
Following are the different solutions to the DLL Hell problem:
Assembly Versioning: When a.NET component or DLL is installed, the Global Assembly Cache examines the version, public key, and language information and generates a strong name for the component, which is referred to as Strong Named Assembly. As a result, two different versions of the same assembly could be placed in the GAC with the same name. There will be no potential for conflict after any assembly has been versioned, which was previously the case.
Static Linking: To address the DLL Hell problem, you can use a very easy solution. Instead of picking from the system library, the compiler statically selects all DLL files from the application folder, implying that each program has its own library that is linked to the application. This solution fully eliminates the DLL hell issue.
Windows File Protection: This concept considerably decreased the DLL hell problem. In Windows 2000, this approach was used. This approach stops an illegal program from overwriting the system DLL. If a program uses a specific Windows API, only the operating system will be able to override it. However, there was a risk when a Microsoft upgrade was required for the existing DLL.
Modern DLL Hell problems
A modern application relies on a slew of libraries, most of which are distributed as NuGet packages. Each of those libraries is reliant on a dozen others. Even basic software now has hundreds of dependencies due to the ease with which code may be reused.
Consider the possibility that some of those dependencies are shared. Take the case of Newtonsoft libraries. JSON and Serilog are so widely used that they are used by every other application or library today. So what if one of your hundred libraries is dependent on a different Newtonsoft version?
Let us explain this with an example.
Imagine, Newtonsoft.Json version 8 is used in your initial project, but Newtonsoft.Json version 9 is used in Library B. This can easily lead to a disagreement.
The problem is determined by which version is loaded. If it's version 8, Library A can call a non-existent method that only exists in version 9, causing the program to crash at runtime.
If version 9 is loaded, the starter project may be affected in the same way. In this scenario, you need to definitely update Newtonsoft.Json version 9 in your startup project. However, the same issue can emerge in more complicated ways, such as this:
This is known as the Diamond Dependency Conflict, and it can manifest itself in a variety of ways. You can't simply update the code of the libraries, unlike in the previous situation, which makes things more complex.
Possible solutions for the above issue
Make the versions fit: The most effective strategy to resolve version disputes is to avoid them in the first place. The recommended policy is to modify dependencies so that you can utilize the same library version. When there is a diamond dependency conflict, changing the libraries can cause the referenced libraries to change, which can cause the wrong versions to appear. Remember that the convention in C# is that there are no breaking API changes in similar major versions.
The following is how a version is created: MAJOR. MINOR. PATCH according to the following rules:
- When you make API modifications that are incompatible, MAJOR is increased.
- When you add functionality in a backward-compatible way, MINOR is incremented.
- When you make backward-compatible bug patches, PATCH is increased.
Binding redirects: Binding redirects can be achieved by adding some code in the app.config of the startup assembly. Binding redirects automatically resolve many conflicts, even when there are many major versions. A higher version assembly will be loaded in many circumstances, and if there isn't too much-deprecated API, it will suffice. However, bound redirects aren't always sufficient. A runtime exception occurs if one of the libraries calls a non-existent method or uses parameters that aren't valid in the loaded version. There are techniques to load assemblies of different versions side by side if you can't make versions fit and binding redirects aren't adequate.
In this post, you saw how simple library references can cause dependency problems. You saw how utilizing binding redirects might cause problems and runtime exceptions. You also observed a variety of approaches to resolving version disputes. In almost all the scenarios, modifying versions and a few binding redirects will resolve the version mismatch. In fact, since Visual Studio automatically adds such binding redirects, you'll most likely be unaware that you had an issue.