Yes, I’m sorry to say, you probably do.
When I started looking into this a little more deeply on one of my own Windows machines, I was pretty shocked to find no fewer than 59 different files all related to the Microsoft Visual C and C++ runtime. Fifty-nine!
This is a symptom of a problem faced by software vendors that, at it’s core, is unsolvable in any pragmatic sense. The problem even has a name: DLL Hell.
Become a Patron of Ask Leo! and go ad-free!
Trying not to reinvent the wheel
The origin of the problem is very simple: programmers trying not to duplicate work that’s already been done.
For example, let’s say as I’m working on a program, I write a little bit of code that does something moderately useful; perhaps it’s a simple function to convert all alphabetic characters in a string to lower case.1 It takes a string of characters like “Ask Leo!” and converts it to “ask leo!”.
It’s not at all uncommon for many different programs to require that feature. So rather than writing or including a case-conversion function into every program that might want it, we package it up into what’s called a “library”. Any program that wants to convert a string of characters to lower case simply uses this library function. The software developers don’t have to write, test, and include their own version of that function.
The Microsoft Visual C++ Runtime is nothing more than a large collection of those kinds of functions. They’re not nearly as simple as just changing the case of characters in a string (though it is included), but they are things that are very common to programs written in Microsoft Visual C++ (a programming language), specifically for Windows.
The idea is that by providing all this functionality in one package, programs are easier and faster to write, since they don’t have to duplicate all this effort.
And that much, at least, is mostly true.
Progress versus compatibility
As time goes on, several things happen:
- Programs change and improve in response to customer and market demand.
- The software that those programs are built on – like Microsoft Visual C++ – are asked to change and improve as well, to provide the functionality required to make those customers and market happy.
- Bugs (errors) are created and bugs are fixed.
- New versions are released.
In an ideal world, version 2 of a support library would work exactly like version 1, plus new and useful functionality.
We don’t live in an ideal world. In practice, version 2 works mostly like version 1, and has some new functionality.
What that means is that a program built (and shipped) using support library version 1 might have problems if that support library were updated to version 2.
The uninstall problem
In that ideal world – you know, the one we don’t live in – there would only be one copy of the support library on each machine, and all the programs that needed it would use it. And, indeed, that was the original vision.
But you know where this is headed: version changes turn out to be a problem, because when a new version of the support library is released, it has to be installed in such a way that it does not overwrite the previous version. Older programs that rely on the previous version continue to use that, and programs that are ready for the new one use it.
So far, so good. This way you might accumulate different versions, but at least there would be only one copy of each version, which everyone who needed that version could share.
The world turned out to be even less ideal than we thought.
Consider this scenario: you install program “A” and it uses library version 1. You then install program “B” and it also uses library version 1, so it doesn’t need to install it – it can just use the copy that’s already there courtesy of program “A”. Now you uninstall program “A”. Three things can happen:
- It uninstalls the library because it installed it and it should clean up after itself, not realizing that another program relies on the library. Program “B” breaks as a result.
- It never uninstalls the library because another program might be using it. As a result, libraries check in, but they never leave.
- We devise some method of tracking how many installed programs are using the library, and only remove it when the last one is uninstalled. Unfortunately, any single program’s failure – be it a programming error or a failure to install or uninstall properly – breaks this technique. At best, you’re left with copies of the library you no longer need, and at worst, uninstalling one program can cause one or more other unrelated programs to fail.
It’s a mess. In fact, it’s such a mess that most programs now don’t bother to try and share at all.
Putting your fate in someone else’s hands
Ultimately, application vendors realized that by relying on shared libraries like this, they were putting their fate into the hands of every other application that happened to use the same version of the same library. If only one of them made a mistake, and the library was accidentally removed or updated when it shouldn’t have been, it put all the others at risk.
So, application vendors typically now install their own copy of the library that they manage and that they can rely on. Disk space is cheap – much cheaper than the errors and frustration that were happening when they tried to share.
So now, on my machine, many different applications all carry with them their own copy of the Microsoft Visual C++ Runtime.
And each is more stable as a result, by virtue of being in control of their own destiny.
Versions, versions and versions
One of the most entertaining2 scenarios of this problem is that of large application suites. It’s not at all uncommon for two things to be true:
- Even if the majority of the suite is written for a 64-bit platform, there may still be 32-bit utilities included (which run just fine on 64-bit operating systems). This means both 64-bit and 32-bit versions of the runtime libraries need to be included.
- Even if the majority of the suite is written for version “X” of the runtime library, there may still be portions that rely on version “Y”, or even version “Z”. Which means that both or all three must be included, and possibly in both 32- and 64-bit flavors.
I do want to be clear that my 59 files were not 59 different copies of different versions of the same thing. My test was a quick scan for anything that looked like a Visual C++ Runtime, and I’m sure I picked up a few false positives. But even so, checking for duplicate files showed that roughly half of the files were identical copies of one another. (As an aside, this is one reason duplicate-file finders can cause damage – as we’ve seen, it is not safe to delete copies of things like the Visual C++ Runtime out from underneath applications that installed them.)
It could have been better, but it’s not
I’m certain there are ways that this problem could have been approached differently from the start. The fact that this isn’t nearly as big a deal on non-Windows platforms is, perhaps, the single biggest indication of that. Those platforms either didn’t attempt this type of code sharing, or do it in less interdependent ways.3
And while developers still benefit from not needing to re-invent the wheel each time they need it, they do end up shipping and installing that wheel with every program that needs it. They save some work by not starting from scratch each time, but your system becomes littered with different versions of (essentially) the same thing.
What we ultimately don’t know is exactly the same thing those install and uninstall programs don’t know: we don’t know what’s safe to delete.
Thus the safest approach, by far, is to leave well enough alone and delete nothing.
Subscribe to Confident Computing! Less frustration and more confidence, solutions, answers, and tips in your inbox every week.
I'll see you there!