Thursday, June 25, 2009

Never use DLLs for code

Unfortunately, this advice won't be usable to some developers. For folks who are writing Active X controls, you're inherently forced to do exactly this: create a DLL which contains code.

However, for those of us who are not trying to create reusable binaries for use in an environment that requires Active X - which I suspect is still quite a significant audience, DLLs should simply never be used for anything other than localized resources.

The reason is simple: DLLs create DLL hell.

There are multiple layers to the issue of DLL hell, various different unfortunate outcomes and scenarios, all of which should be enough in and of themselves to dissuade you from using them in the first place. But when taken together, it blows my mind that anyone still thinks its a good idea to build them, and use them, ever (unless they're absolutely required by the technology in question).

The only reason they exist, is to create a common library that can be drawn upon by many clients at runtime, thus reducing the memory footprint of all of the running applications that rely upon that common code, since only one copy need be loaded by the OS, and then simply mapped into the address space of each client that was linked against it.

But, the thing is, that was a reasonable need in 1990, when Windows 3.0 was released. Memory was indeed very limited, and even the delay of having to load from disk a separate copy of the DLL for each process was a significant performance hit for the OS. DOS compilers & linkers had been using similar techniques for a few years, to squeeze extra usage out of the very limited address space afforded to the 16 bit programs of the day - such as Borland's Turbo Pascal which was able to map various units into a single shared memory section, swapping out which unit was loaded into that section dynamically, at the control of the parent software. This was a clever way to make larger programs than previously possible, and Microsoft's creation of a built-in model to support that in Windows 3.0 made perfect sense.

However, Its 2009. Nearly two decades later. Memory is dirt cheap. Machines regularly come with 4GB or more. Every single process in the machine can easily afford to have its own copy of every library that it needs to function, and still afford vast amounts of ram to data and disk cache and every other function the machine needs to operate well.

And over the years it has been proven time and time again that software which relies on external DLLs for code is far more fragile than those which do not. Microsoft has even had to retool the OS to explicitly account for the myriad issues surrounding DLLs (side-by-side assemblies), and the reality that software can't easily share common DLLs in practice because of subtle (and sometimes not-so-subtle) incompatibilities between versions of a common DLL. One DLL uses one STL library, and expects map<int> to have a certain size and structure, while another DLL was built using slightly different libraries or compiler or linker options that resulted in a map<int> that is not binary compatible with the other one. Suddenly, moving data between the main program and one or the other of these DLLs can lead to slicing, or to invalid memory access, or simply data corruption at the lowest possible levels, which inevitably leads to bad data or (if you're lucky) crashed applications.

The motivation to use DLLs makes no sense. It has no place in a modern computer. Its a nice theory, a fine vision, but one which in practice is a pile of paper cuts that adds up to a lacerated face... the lacerated face of your customer, bleeding all over and wondering why in the 9 hells they every bought your software!

Simply create static libraries, and link against those. It still gives you a simple way to create and maintain common code amongst multiple projects. It gives you the advantages of writing common code once, and debugging it to the benefit of all client programs. And when you fix or update some aspect of your library, you're updating or fixing every client program that relies upon it. But you're not creating a scenario where testing your software on one system is totally arbitrary and unrelated to how it might perform on another system due to different versions of a common DLL. How can you feel confident that your software is debugged if you don't even know if its the same software you're running on two different machines because whole chunks of your code may be different between the two depending on what other software was loaded on one vs. the other! And how can you rest easy knowing that depending on what other software your customer installs after yours, your software may stop working at any time - suddenly using a new version of a common DLL that you haven't tested against, and may well exhibit new and difficult to diagnose or even recognize bugs.

Again, the motivation for DLLs is long, long antiquated. Its a hang-over from an age long gone. Only the absolutely most anal-retentive amongst us would think that it has any place in today's world of software design and distribution.

DLLs are still great for dynamically loading different resources - dialogs, fonts, etc., at runtime. But they have absolutely no business loading code at runtime. Computers are already complicated enough, and already rely upon too many variables - such as the patch level of the OS itself, and the state of the registry, and so on, with out needlessly adding additional moving parts in the form of core-code that your app needs to run varying over time or machine that you're installed on.

Do yourself and your customers a favor: insulate yourself from DLL Hell - simply do not ever use DLLs for code that you write, and always supply compatible versions of 3rd party DLLs in your application's executable folder with your installs, so that your software will choose those over any other installation's copy of the DLL, and your software therefore will continue to run as correctly a year from now as it did when it was first installed.

No comments:

Post a Comment