MFC:为什么会发生这种堆损坏? (array_s.cpp / afxcoll.inl)
MFC: Why does this heap corruption happen? (array_s.cpp / afxcoll.inl)
崩溃代码
当调用第三行('delete' 命令)时,以下代码使我的程序因堆损坏而崩溃(程序退出):
1: CStringArray* pStringArray = new CStringArray();
2: ClassInDll::addToStringArray(_T("asdf"), *pStringArray);
3: delete pStringArray;
和
1: ClassInDll::addToStringArray(CString s, CStringArray& sa){
2: sa.Add(s);
3: }
addToStringArray() 是静态的
请注意,这实际上不是我的代码,而只是我可以重现错误的最低限度。它也可以用 CArray 重现。
我证实,堆损坏确实不会发生在之前该代码行
gflags /p /enable MyExe.exe /full
堆栈跟踪:
好像是什么问题
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
内部数组没有按照创建时的方式删除,这可能是错误所在。 (如有错误请指正)
项目设置
我确保在 MyExe.exe 和 MyDll.dll 中使用相同的 MFC 设置,即:
Use of MFC
Use MFC in a Shared DLL
Use of ATL
Not using ATL
Character Set
Use Unicode Character Set
我在调试模式下测试,所以没有优化。
问题
MyDll.dll 不是唯一加载的 dll,而且项目依赖项(对第三方 dll 等)有太多影响,所以我不能把这部分问题全部提出来。
所以我的问题归结为:
- 我的假设是否正确 array_s.cpp 和 afxcoll.inl 彼此不兼容?
- 为什么一段代码调用 array_s.cpp,而另一段代码调用 afxcoll.inl?
- 还有什么问题?
我试过将其关闭再打开 ;)
提前致谢!
更新
PaulMcKenzie 指出,如果我针对不同的 CRT 进行构建,情况就是如此。一个是“多线程调试 DLL”,另一个是“多线程 DLL”。在使两者相等之后,问题仍然存在。 This page 声明如果使用 VS 2015 及更高版本 Windows 10 并具有项目设置以便使用相同的运行时库,则在运行时使用相同的 CRT 库(因此应该使用相同的堆,对吧?)。
我已经确定,“项目属性 -> C/C++ -> 代码生成”中的每个条目在 exe 和 dll 中都完全相同。
在模块(EXE 和 DLL,或不同的 DLL)之间传递 MFC 对象(甚至 通过 指针)是一件 非常 棘手的事情。如果您只是在 DLL 中 使用 类,那么您应该(至少)使用 AFX_MANAGE_STATE
宏(参见 here ) 在 DLL 中。
但是,如果你的 DLL 是定义它自己的 MFC 派生的类(你的ClassInDll
似乎是这样),它真的应该构建作为 MFC Extension DLL.
构建扩展 DLL(甚至使用 'regular' MFC DLL)的整个过程过于宽泛,我无法在这里为您提供任何可用的源代码。但是,从上面的第二个 link 开始,以下部分可能与您看到的问题相关(粗体斜体是我的):
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.
事实证明,发布 和 调试 dll 都被加载(因为另一个 dll):
- msvcr100.dll
- msvcr100d.dll
- msvcp100.dll
- msvcp100d.dll
“模块”window 肯定有帮助,如果你只知道你应该看看那里。
感谢 PaulMcKenzie 和 IInspectable 引领我走向正确的方向。
崩溃代码
当调用第三行('delete' 命令)时,以下代码使我的程序因堆损坏而崩溃(程序退出):
1: CStringArray* pStringArray = new CStringArray();
2: ClassInDll::addToStringArray(_T("asdf"), *pStringArray);
3: delete pStringArray;
和
1: ClassInDll::addToStringArray(CString s, CStringArray& sa){
2: sa.Add(s);
3: }
addToStringArray() 是静态的
请注意,这实际上不是我的代码,而只是我可以重现错误的最低限度。它也可以用 CArray
重现。 我证实,堆损坏确实不会发生在之前该代码行
gflags /p /enable MyExe.exe /full
堆栈跟踪:
好像是什么问题
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 |
内部数组没有按照创建时的方式删除,这可能是错误所在。 (如有错误请指正)
项目设置
我确保在 MyExe.exe 和 MyDll.dll 中使用相同的 MFC 设置,即:
Use of MFC | Use MFC in a Shared DLL |
Use of ATL | Not using ATL |
Character Set | Use Unicode Character Set |
我在调试模式下测试,所以没有优化。
问题
MyDll.dll 不是唯一加载的 dll,而且项目依赖项(对第三方 dll 等)有太多影响,所以我不能把这部分问题全部提出来。
所以我的问题归结为:
- 我的假设是否正确 array_s.cpp 和 afxcoll.inl 彼此不兼容?
- 为什么一段代码调用 array_s.cpp,而另一段代码调用 afxcoll.inl?
- 还有什么问题?
我试过将其关闭再打开 ;)
提前致谢!
更新
PaulMcKenzie 指出,如果我针对不同的 CRT 进行构建,情况就是如此。一个是“多线程调试 DLL”,另一个是“多线程 DLL”。在使两者相等之后,问题仍然存在。 This page 声明如果使用 VS 2015 及更高版本 Windows 10 并具有项目设置以便使用相同的运行时库,则在运行时使用相同的 CRT 库(因此应该使用相同的堆,对吧?)。
我已经确定,“项目属性 -> C/C++ -> 代码生成”中的每个条目在 exe 和 dll 中都完全相同。
在模块(EXE 和 DLL,或不同的 DLL)之间传递 MFC 对象(甚至 通过 指针)是一件 非常 棘手的事情。如果您只是在 DLL 中 使用 类,那么您应该(至少)使用 AFX_MANAGE_STATE
宏(参见 here ) 在 DLL 中。
但是,如果你的 DLL 是定义它自己的 MFC 派生的类(你的ClassInDll
似乎是这样),它真的应该构建作为 MFC Extension DLL.
构建扩展 DLL(甚至使用 'regular' MFC DLL)的整个过程过于宽泛,我无法在这里为您提供任何可用的源代码。但是,从上面的第二个 link 开始,以下部分可能与您看到的问题相关(粗体斜体是我的):
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.
事实证明,发布 和 调试 dll 都被加载(因为另一个 dll):
- msvcr100.dll
- msvcr100d.dll
- msvcp100.dll
- msvcp100d.dll
“模块”window 肯定有帮助,如果你只知道你应该看看那里。
感谢 PaulMcKenzie 和 IInspectable 引领我走向正确的方向。