Holy cow, I wrote a book!
Whenever the issue of DLL dependencies arises,
I can count on somebody arguing that
these dynamic dependencies should be tracked,
even if doing so cannot be proven to be reliable.
Even if one could walk the call stack reliably,
you would still get it wrong.
The example I gave originally was the common helper library,
where A.DLL loads B.DLL via an
intermediate function in MIDDLE.DLL.
You want the dependency to be that A.DLL depends
on B.DLL, but instead the dependency gets assigned to
"But so what?
Instead of a direct dependency from A.DLL to
B.DLL, we just have two dependencies,
one from A.DLL to MIDDLE.DLL,
and another from MIDDLE.DLL to B.DLL.
It all comes out to the same thing in the end."
Actually, it doesn't. It comes out much worse.
After all, MIDDLE.DLL is your common helper library.
All of the DLLs in your project depend on it.
Therefore, the dependency diagram in reality looks like this:
A.DLL depends on B.DLL,
and both DLLs depend on MIDDLE.DLL.
That common DLL really should be called BOTTOM.DLL
since everybody depends on it.
Now you can see why the dependency chain
A.DLL → MIDDLE.DLL → B.DLL is horribly wrong.
Under the incorrect dependency chain,
the DLLs would be uninitialized in the order A.DLL,
even though B.DLL depends on MIDDLE.DLL.
That's because your "invented" dependency
introduces a cycle in the dependency chain,
and a bogus one at that.
Once you have cycles in the dependency chain, everything falls apart.
You took something that might have worked into something
that explodes upon impact.
A.DLL → MIDDLE.DLL → B.DLL
This situation appears much more often than you think.
In fact it happens all the time.
Because in real life, the loader is implemented in the internal library
and KERNEL32.DLL is just a wrapper function around
the real DLL loader.
In other words, if your A.DLL
you are already using a middle DLL;
its name is KERNEL32.DLL.
If this "dynamic dependency generation" were followed,
then KERNEL32.DLL would be listed as dependent
When it came time to uninitialize, KERNEL32.DLL
would uninitialized before all dynamically-loaded DLLs,
because it was the one who loaded them,
and then all the dynamically-loaded DLLs would find themselves
in an interesting world where KERNEL32.DLL no
Besides, the original problem arises when A.DLL
calls a function in B.DLL during its
going against the rule that you shouldn't call anything
outside your DLL from your DllMain function
(except perhaps a little bit of KERNEL32
but even then, it's still not the best idea).
It's one thing to make accommodations so that existing
bad programs continue to run,
but it's another to build an entire infrastructure built on
in order to
encourage people to do something they shouldn't be doing in the first place,
and whose guesses end up taking a working situation and breaking it.
You can't even write programs to take advantage of this new behavior
because walking the stack is itself unreliable.
You recompile your program with different optimizations,
and all of a sudden the stack walking stops working
because you enabled tail call elimination.
If somebody told you,
"Hey, we added this feature that isn't reliable,"
I suspect your reaction would not be "Awesome, let me start depending on it!"