Duderino
Duderino

Reputation: 73

MFC: Why does this heap corruption happen? (array_s.cpp / afxcoll.inl)

Crashing Code

The following code makes my program crash (program exits) by heap corruption when the third line (the 'delete' command) is called:

1: CStringArray* pStringArray = new CStringArray();
2: ClassInDll::addToStringArray(_T("asdf"), *pStringArray);
3: delete pStringArray;

with

1: ClassInDll::addToStringArray(CString s, CStringArray& sa){
2:  sa.Add(s);
3: }

addToStringArray() is static

StackTrace:

StackTrace


What seems to be the problem

Dll dependency MFC source
1 CStringArray creation MyExe.exe > MFC ...\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\array_s.cpp
2 Internal array allocation MyExe.exe > MyDll.dll > MFC ...\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxcoll.inl
3 Deletion MyExe.exe > MFC ...\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\array_s.cpp

The fact that the internal array is not deleted the same way it was created is probably the error. (Please correct me if I'm wrong)


Project Settings

I made sure that the same MFC settings are used in MyExe.exe as well as MyDll.dll, i.e.:

Use of MFC Use MFC in a Shared DLL
Use of ATL Not using ATL
Character Set Use Unicode Character Set

I test in debug mode, so there is no optimization.


Question

MyDll.dll is not the only dll that is loaded, and there is too much going on with project dependencies (to third party dlls etc), so I cannot make all this part of my question.

So my questions boil down to:

I have tried turning it off and on again ;)

Thank you in advance!


Update

PaulMcKenzie pointed out that this is the case if I build against different CRTs which was the case. One was "Multithreaded Debug DLL", the other was "Multithreaded DLL". Still, the problem persists, after making both equal. This page states that if using VS 2015 and above with Windows 10 and having project settings so that the same runtime library is used, the same CRT library is used at runtime (and therefore should use the same heap, right?).

I've made sure, that every entry within "Project Properties -> C/C++ -> Code Generation" is exactly the same in the exe and dll.

Upvotes: 4

Views: 640

Answers (2)

Duderino
Duderino

Reputation: 73

It turned out that release and debug dlls were both being loaded (because of another dll):

  • msvcr100.dll
  • msvcr100d.dll
  • msvcp100.dll
  • msvcp100d.dll

The "modules" window sure does help, if you only know that you should look there.

Thanks to PaulMcKenzie and IInspectable for leading me into the right direction.

Upvotes: 1

Adrian Mole
Adrian Mole

Reputation: 51845

Passing MFC objects (even via pointers) between modules (EXEs and DLLs, or different DLLs) is a very tricky business. If you're simply using classes in the DLL, then you should (at the very least) be using the AFX_MANAGE_STATE macro (see here) in the DLL.

However, if your DLL is defining its own MFC-derived classes (your ClassInDll seems to be such), it should really be built as an MFC Extension DLL.

The whole process of building extension DLLs (and even using the 'regular' MFC DLLs) is rather too broad for me to provide any usable source code for you here. But, from the second link above, the following section is likely relevant to the problem(s) you are seeing (bold italics are mine):

Memory Management

MFCx0.dll and all MFC extension DLLs loaded into a client application's address space use the same memory allocator, resource loading, and other MFC global states as if they were in the same application. This is significant because the non-MFC DLL libraries and the regular MFC DLLs do the exact opposite and have each DLL allocating out of its own memory pool.

If an MFC extension DLL allocates memory, that memory can freely intermix with any other application-allocated object. Also, if an application that dynamically links to MFC fails, the protection of the operating system maintains the integrity of any other MFC application sharing the DLL.

Similarly other global MFC states, like the current executable file to load resources from, are also shared between the client application and all MFC extension DLLs as well as MFCx0.dll itself.

Upvotes: 2

Related Questions