Mixing MSVCRT Versions

So, I have a C ++ library with a statically linked copy of MSVCRT. I want someone to be able to use my library with any version of MSVC Runtime. What is the best way to achieve this?

I am already very careful how things are.

  • Memory never misses the DLL barrier that will be freed.
  • C ++ Runtime objects are not passed through barriers (i.e. vectors, maps, etc., unless they are created on that side of the barrier).
  • No files or resource descriptors are passed between barriers.

However, I still have some simple code that causes heap corruption.

There is an object in my library:

class Foos { public: //There is an Add method, but it not used, so not relevant here DLL_API Foos(); DLL_API ~Foos(); private: std::map<std::wstring, Foo*> map; }; Foos::~Foos() { // start at the begining and go to the end deleting the data object for(std::map<std::wstring, Foo*>::iterator it = map.begin(); it != map.end(); it++) { delete it->second; } map.clear(); } 

And then I use it from my application like this:

 void bar() { Foos list; } 

After I call this function from anywhere, I get a debug warning about stack corruption. And if I actually let this run out, it will actually damage the stack and segfault.

My calling application was compiled using the tools of the Visual Studio 2012 platform. The library was compiled using the tools of the Visual Studio 2010 platform.

Is it just something that I should absolutely not do, or am I really breaking the rules for using several modes of operation?

+7
c ++ visual-c ++ msvcrt
source share
4 answers

Memory never misses the DLL barrier

But it is so. Many times actually. Your application has created storage for the class object, in this case on the stack. And then it passes a pointer to the methods in the library. Starting with a constructor call. This pointer is inside the library code.

In a scenario like this, something is wrong that he did not create the correct amount of storage. You have a VS2012 compiler to view the declaration of your class. It uses the VS2012 implementation of std :: map. However, your library was compiled with VS2010, it uses a completely different implementation of std :: map. With a completely different size. Huge changes thanks to C ++ 11.

This is just a complete memory corruption at work, the code in the application that writes the stack variables will damage std :: map. And vice versa.

Mapping C ++ classes across module boundaries is filled with such traps. Only ever consider this when you can guarantee that everything is compiled with the same version of the compiler and with the same settings. No shortcuts on this; you cannot mix debug and release code. The creation of the library, so no implementation details may be available, you must abide by these rules:

  • Show only clean interfaces with virtual methods, argument types must be simple interface types or pointers.
  • Use the factory class to instantiate an interface.
  • Use reference counting to manage memory, so it always releases a library.
  • Draw basic details such as packaging and calling with strict rules.
  • Never allow exceptions to cross a module boundary; use error codes.

You will feel good when you write the COM code, as well as the style that you see, for example, in DirectX.

+9
source share

map member variable is still created by the application with some internal data allocated by the application, not the DLL (and they can use different map implementations). As a rule, do not use stack objects from a DLL, add something like Foos * CreateFoos() to your DLL.

+3
source share

C ++ Runtime objects are not passed through barriers (i.e. vectors, maps, etc., unless they were created on that side of the barrier)

You do just that. The Foos object is created by the main program on the stack and then used in the library. The object contains a map as part of it ...

When compiling the main program, it looks at header files, etc., to determine how much stack space is allocated for the Foos object. And it calls the constructor, which is defined in the library ... which could expect a completely different layout / size of the object

+3
source share

This may not suit your needs, but do not forget that implementing all of this in header files simplifies the problem (sort of) :-)

0
source share

All Articles