views:

178

answers:

7

This MSDN article says that if my application loads VC++ runtime multiple times because either it or some DLLs it depends on are statically linked against VC++ runtime then the application will have multiple CRT states and this can lead to undefined behaviour.

How exactly do I decide if this is a problem for me? For example in this MSDN article several examples are provided that basically say that objects maintained by C++ runtime such as file handles should ot be passed across DLL boundaries. What exactly is a list of things to check if I want my project to statically link against VC++ runtime?

A: 

One issue I've seen with having different versions of the MSVC runtime loaded is that each runtime has its own heap, so you can end up with allocations failing with "out of memory" even though there is plenty of memory available, but in the wrong heap.

Chris Card
Certainly true with VC6, but if I read the new CRT code correctly this no longer happens.
MSalters
vc6, vc7 and vc8 all had their own heaps, and I've worked with apps that had all three loaded at the same time.
Chris Card
A: 

The major problem with this is that the problems can occur at runtime. At the binary level, you've got DLLs making function calls, eventually to the OS. The calls themselves are fine, but at runtime the arguments are not. Of course, this can depend on the exact code paths exercised.

Worse, it could even be time- or machine-dependent. An example of that would be a shared resource, used by two DLLs and protected by a proper reference count. The last user will delete it, which could be the DLL that created it (lucky) or the other (trouble)

MSalters
Could you provide some simple example of what exactly might work wrong please?
sharptooth
No, unfortunately. In this respect it's similar to memory corruption, missing mutexes or race conditions: you get very weird effects that are very hard to explain and troubleshoot. There are more similarities; this problem too doesn't lend itself to static analysis.
MSalters
A: 

As far as I know, there isn't a (safe) way to check which version of the CRT a library is statically linked with.

You can, however, check the binaries with a program such as DependencyWalker. If you don't see any DLLs beginning with MSVC in the dependency list, it's most likely statically linked. If the library is from a third part, I'd just assume it's using a different version of the CRT. This isn't 100% accurate, of course, because the library you're looking at could be linked with another library that links with the CRT.

dauphic
A: 
  • if CRT handles are a problem, then don't expose CRT handles in your dll exports :-)
  • don't pass memory pointers around (if an exported class allocates some memory, it should free it... which means don't mess with inline constructor/destructor?)
pascal
A: 

This MSDN article (here) says that you should be alterted to the problem by linker error LNK4098. It also suggests that passing any CRT handles across a CRT boundary is likely to cause trouble, and mentions locale, low-level file I/O and memory allocation as examples.

Permaquid
+1  A: 

It's OK to have multiple copies of the CRT as long as you aren't doing certain things...:

Each copy of the CRT will manage its own heap.

This can cause unexpected problems if you allocate a heap-based object in module A using 'new', then pass it to module B where you attempt to release it using 'delete'. The CRT for module B will attempt to identify the pointer in terms of its own heap, and that's where the undefined behavior come in: If you're lucky, the CRT in module B will detect the problem and you'll get a heap corruption error. If you're unlucky, something weird will happen, and you won't notice it until much later...

You'll also have serious problems if you pass other CRT-managed things like file handles between modules.

If all of your modules dynamically link to the CRT, then they'll all share the same heap, and you can pass things around and not have to worry about where they get deleted.

If you statically link the CRT into each module, then you have to take care that your interfaces don't include these types, and that you don't allocate using 'new' or malloc in one module, and then attempt to clean up in another.

You can get around this limitation by using an OS allocation function like MS Windows's GlobalAlloc()/GlobalFree(), but this can be a pain since you have to keep track of which allocation scheme you used for each pointer.

Having to worry about whether the target machine has the CRT DLL your modules require, or packaging one to go along with your application can be a pain, but it's a one-time pain - and once done, you won't need to worry about all that other stuff above.

Scott Smith
+1  A: 

It is not that you can't pass CRT handles around. It is that you should be careful when you have two modules reading/writing the handle.

For example, in dll A, you write

 char* p = new char[10];

and in the main program, write

 delete[]p;

When dll A and your main program have two CRTs, the new an delete operations will happen in different heaps. The heap in the DLL can't manage the heap in the main program, resulting memory leaks.

Same thing on File handle. Internally File handle may have different implementations in addition to its memory resource. Calling fopen in one module and calling fwrite and fclose in another one could result problems as the data FILE* points could be different in CRT runtimes.

But you can certainly pass FILE* or memory pointer back and forth between two modules.

Sherwood Hu