You are using this term incorrectly: the cause of DLL Hell is when one DLL (often a newer one) should be used, but another is used instead (even if it is older). The causes of this were varied.
On 16-bit Windows there was a single address space and only a single version of a DLL could be loaded by all processes: the first one to load would "win", and the others would get screwed with the old copy. This was due to 16-bit Windows not having memory protection: it was more of a GUI over DOS, and thereby had cooperative multi-tasking.
Even with that fixed, many developers would require a slightly newer version of a library, and rather than ask the user to upgrade their system (an irritating consequence of not having packages or dependencies; APT FTW ;P) would just include the DLL in their installer and unconditionally overwrite any existing copy.
After the dynamic loader started supporting "local" versions of libraries (installed to the same folder as the application), a similar problem happened with COM objects, which are centrally registered: someone would install their own version of a shared GUI component, register it with the shared name, overwriting possibly-installed newer copies.
Both of these problems were actually solved, but way too many developers simply gave up on Microsoft and Windows beforehand, and then refuse to spend the time to learn about the improvements. In essence, Windows now has reasonable-ish package management, with dependencies: Windows Installer.
In addition to dependencies and versioning of packages (which can then be correctly tracked by Windows, much like APT on a Debian/Ubuntu box), Windows Installer supports the notion of "unified installer program" with "merge modules": in essence, you can include someone else's package inside of your package; that way the dependencies can correctly be checked, and old versions won't get overwritten.
There are still a few cases that are quite difficult to manage (involving libraries that require a modified ABI over time, but still receive updates), and Microsoft's solution to those is WinSxS. Honestly, while I'm much less familiar with it than on Unix, it only seems a constant multiple more crazy than .la files, which I believe solve a similar problem.
These technologies and improvements were all introduced at or before Windows XP, an operating system that was released just over a decade ago. Of course, these are all solutions that developers sometimes ignore, but if you download software for Linux that comes with a .sh installer that scribbles into /lib, you are in for similar "hell".
On 16-bit Windows there was a single address space and only a single version of a DLL could be loaded by all processes: the first one to load would "win", and the others would get screwed with the old copy. This was due to 16-bit Windows not having memory protection: it was more of a GUI over DOS, and thereby had cooperative multi-tasking.
Even with that fixed, many developers would require a slightly newer version of a library, and rather than ask the user to upgrade their system (an irritating consequence of not having packages or dependencies; APT FTW ;P) would just include the DLL in their installer and unconditionally overwrite any existing copy.
After the dynamic loader started supporting "local" versions of libraries (installed to the same folder as the application), a similar problem happened with COM objects, which are centrally registered: someone would install their own version of a shared GUI component, register it with the shared name, overwriting possibly-installed newer copies.
Both of these problems were actually solved, but way too many developers simply gave up on Microsoft and Windows beforehand, and then refuse to spend the time to learn about the improvements. In essence, Windows now has reasonable-ish package management, with dependencies: Windows Installer.
In addition to dependencies and versioning of packages (which can then be correctly tracked by Windows, much like APT on a Debian/Ubuntu box), Windows Installer supports the notion of "unified installer program" with "merge modules": in essence, you can include someone else's package inside of your package; that way the dependencies can correctly be checked, and old versions won't get overwritten.
There are still a few cases that are quite difficult to manage (involving libraries that require a modified ABI over time, but still receive updates), and Microsoft's solution to those is WinSxS. Honestly, while I'm much less familiar with it than on Unix, it only seems a constant multiple more crazy than .la files, which I believe solve a similar problem.
These technologies and improvements were all introduced at or before Windows XP, an operating system that was released just over a decade ago. Of course, these are all solutions that developers sometimes ignore, but if you download software for Linux that comes with a .sh installer that scribbles into /lib, you are in for similar "hell".