Wednesday, 11 January 2012

ASSERT(AfxGetThread() == NULL) with VisualStudio 2010, or the DLL-hell, revamped

Everybody thinks DLL-hell is a thing of the past. Me included: #WTF, just start the Dependency Walker, look what libraries got loaded, correct it and be happy*. Not so, with the advent of Visual Studio 2010 everything got a bit more complicated!

Let me describe the problem which I encountered working in a project for one of my customers. Let's start with a...


This is only a short technical note - for those who (like me) first check the Internet for solution to weird programming questions!

The Problem:

is maybe best described by this desperate post:
Hi all,
I have a question to solve, the situation is I use a exe to load a dll(MFC regular dll),but when I use LoadLibrary() to load the dll and in the constructor of the object whose base class is CWinApp in the dll, It have a assert error, and the code line is ASSERT (AfxGetThread() == NULL).Is there anyone have the similar situation with me and have thoughts about it can tell me ,we can talk about it.
thanks in advance.
Well, the same happened to me when I was porting a C++ application from Visual Studio 2008 to VS 2010. To be honest, the situation was a little more complicated: a mixed mode C++/C# program using C++/CLI as a glue. But back to the problem: the standard explanation for that ASSERT is that you have forgotten to include the AFX_MANAGE_STATE macro (Reason 1) in exported DLL functions as described here.

Sadly, this wasn't my problem. It applies to non-MFC DLLs only**, which mine wasn't, so the whole module state management business should be cared automatically in DLL initalization code the linker is generating. This again is controlled by #define macros used whith the compiler (like _USRDLL, _AFXDLL and their hellish likes). So maybe this could be the reason? Nope, everything was correct :-(.

After more web-searching and finding only tons of happless cries for help I stumbled upon the 2nd useful link
Sounds like you have two or more CWinApp objects, and they're using the same AfxGetThread data. I suppose this could happen if your libraries are linking to different versions of MFC, including mixing of debug and release versions. You might use Dependency Walker (depends.exe) to determine the DLLs you link to implicitly, or the Sysinternals Process Explorer to determine the DLLs you're using at runtime
Well, that sounds interesting, mybe I've mixed debug and release DLLs? Nope (although I made that error later on, so it's defintely a good point to keep in mind (Reason 2)). What could it possibly be then???

The Solution:

The solution is more complicated, but somehow both typical and symptomatic for real-life nontrivial system using Microsoft technology... As my client had a really old codebase growing steadily with the years, all possible Microsoft technologies could be found there; beginning with plain old DLL, through MFC-based ones, further using C++/CLI and at last arriving at C# and .NET. This last one was burdened with a constraint: we had to use .NET v.2.0. The problem is, that Visual Studio 2010 doesn't suppport .NET 2.0! Because of time shortage they decided that only .NET 4.0 will be supported out of the box, and that the 2008 VS-compiler will be inegrated in Visual Studio 2010 to be used for older .NET versions.

Well, in itself it wouldn't pose any problems, if there wouldn't be the C++/CLI code which we used as glue between pure C++ MFC and the newer C# parts. It has to be compiled for the .NET 2.0 as well, thus should use the 2008 VS-compiler. But it is put in a DLL which int turn is loaded by a traditional C++/MFC one (created with  2010 VS-compiler!). See it? Two different compilers, 2 diffrent runtimes, 2 CWinApp objects as in (Reason 2) (or maybe the loader gets misleaded, I'm not sure about it). In any case, Visual Studio 2010 is forcing us to use 2 compilers in one application here (Reason 3).
The Mores:

I'm not sure about that one. How about this: stay away from MFC and use Qt? Or: don't migrate your code to VisualStudio 2010 (as it's rather slow to boot)? You're damned if you have to be backwards compatible? 
* see here  for author's previous letter from DLL-hell
** more hellish details: 3 kinds of DLLs you can use on Windows: ... TODO: link (but that's just plain boring!)

No comments: