天天看点

卸载MFC DLL时出现内存泄露

一、现象描述

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

}