一、现象描述
在
if ( m_hDll )
{
::FreeLibrary((HMODULE) m_hDll);
m_hDll = NULL;
}
时出现一堆的内存泄露。
二、造成原因
1、加载DLL与卸载DLL不在同一线程.
2、
三、解决方法
1、
让加载DLL与卸载DLL在同一线程.
2、
修改:在载入DLL后,调用GetModuleHandleEx使所有的DLL都是静态卸载,相当于用了导出库.这样DLL会在ExitProcess的时候才卸载
m_hDll = (HMODULE)::LoadLibrary(lpDllPath ? lpDllPath : "YourMFCPlugin.dll");
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_PIN,(LPCTSTR)m_hDll,&m_hDll);
参考文章:http://www.cnblogs.com/crazii/articles/1762296.html
去掉MFC的内存泄露报告 - bypass MFC's memory leak dump
很久就发现了MFC的内存泄露报告,原因是我把MFC的界面做成了动态加载的插件(Framework本身不是基于MFC的),它作为DLL在其他一些插件之后被加载.卸载的时候在其他DLL之前先被卸载,所以在MFC出内存泄露报告的时候,全局静态对象并没有完全析构.
其实这里MFC给出的内存泄露报告都是不精确的.
于是在加载MFC DLL的时候加上了GetModuleHanldeEx(GET_MODULE_HANDLE_EX_FLAG_PIN)参数,使所有的DLL都是静态卸载,相当于用了导出库.这样DLL会在ExitProcess的时候才卸载,MFC的Dump也在这个时候才开始,问题好了很多.
//MainApp/Framework is NOT based on MFC lib.
//hPlugin = LoadLibrary("YourMFCPlugin.dll"); //load a DLL that uses MFC lib
//GetModuleHanldeEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_PIN,(LPCTSTR)hPlugin,&hPlugin);
深入讨论:
还有更恼人的问题,由于自己实现了内存管理,内存管理对象MemoryManger作为系统的最后一个对象被析构,而即使让MFC的DLL静态释放,它还是在基础DLL的静态对象MemoryManger析构之前就dump memory leak了,所以情况就是,
MFC生成了一份不精确的memory leak report,紧接着MemoryManger对象也生成了另一份report.如果自己实现了一个Memory Manager,那么这个MFC的内存报告确实有点不爽,不能配置其关闭.
我查了半天没有找到如何关闭MFC的report,
最后用了一个很无奈的办法:将crt的debug输出定位到stderr,等到要生成自己的memory report的时候,再从新设置回来,由于程序是windows程序,所以实际上MFC的没有输出.至少在Output窗口没有MFC的内存泄露报告了.
我的基本思路记录如下,也希望对遇到类似问题的人有所帮助:
//sample code by Crazii
MemoryManger::MemroyManger()
{
#if WIN32
//将CRT Debug Reprt定位到stderr
//redirect internal CRT Debug output to stderr,instead of using system OutputDebugString
//you can redirect it to any other real hdd log file,too.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif //WIN32
}
MemoryManger::~MemroyManger()
{
#if WIN32
//恢复DebugOutput
//restore the CRT output
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
//TODO:
//Dump your "Real" Memory Leaks
//OutputDebugString...
//finally: dump default leaks that may be out of memory manager's control
_CrtDumpMemoryLeaks();
#endif //WIN32
}