VC:快速侦测断言错误导致的内存泄露

VC:快速侦测断言错误导致的内存泄露  


2010-09-18 21:40:16|  分类: 疑难杂症 |字号 订阅
        程序在退出时,由于断言错误,导致内存大量泄露。之前曾经遇到过类似的情况,当时是一步一步地调试,最后解决。这次再次遇到,而且情况更加复杂,所以,我想出了一个快速侦测的方法。


        替换系统默认的断言语句:(加在stdafx.h文件的前面,或者是第一个编译的文件里面,以便能够替换所有的断言)
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s \n", __FILE__, __LINE__, __FUNCTION__);\
 _ASSERTE((x));


 


        这里使用ATLASSERT。因为我使用的是VC 2008,2008中,MFC的ASSERT是采用的ATLASSERT的,而ATL也是ATLASSERT的,同时,console控制台程序也可以方便地使用ATLASSERT。


g_TraceNoTime的实现:
void g_TraceNoTime(const char* pszFormat, ...); //打印调试信息
void g_TraceNoTime(const wchar_t* pszFormat, ...); //打印调试信息
#define PACKET_BUFFER_MAX_SIZE (64*1024)
void g_TraceNoTime(const char* pszFormat, ...)
{
 char buf[PACKET_BUFFER_MAX_SIZE]={0};
 va_list arglist;
 SYSTEMTIME t;
 GetLocalTime(&t);
 va_start(arglist, pszFormat);
 vsprintf_s(buf+strlen(buf), sizeof(buf)-strlen(buf), pszFormat, arglist);
 strcat_s(buf, sizeof(buf), "\r\n");
 va_end(arglist);


 OutputDebugStringA(buf);
}


void g_TraceNoTime(const wchar_t* pszFormat, ...)
{
 wchar_t buf[PACKET_BUFFER_MAX_SIZE]={0};
 va_list arglist;
 SYSTEMTIME t;
 GetLocalTime(&t);
 va_start(arglist, pszFormat);
 vswprintf_s(buf+wcslen(buf), sizeof(buf)/sizeof(wchar_t)-wcslen(buf), pszFormat, arglist);
 wcscat_s(buf, sizeof(buf)/sizeof(wchar_t), L"\r\n");
 va_end(arglist);


 OutputDebugStringW(buf);
}


      这样虽然可以,但是还是不能看到断言的内容,于是改为:
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s "#x"\n", __FILE__, __LINE__, __FUNCTION__);\
 _ASSERTE((x));


      对Unicode的支持:#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s "#x"\n", __FILEW__, __LINE__, __FUNCTIONW__);\
 _ASSERTE((x));


       暂时就这样。运行一下,看看结果。最后一个TRACE打印的结果:
****.cpp(1072):****::CreateBitmapEx (eCompression == BI_RGB) || (eCompression == BI_BITFIELDS)


      是我自己写的一个函数里面的断言!


      基本上知道问题出哪儿了,但是,如果改了这一处断言,其他地方还是会出问题!因为这些断言发生时,程序正在退出,系统资源正在释放!所以,在发现程序退出时,就不能再执行这些断言语句!——只要正确地回收某些资源,就可以保证程序不会在退出时执行到这里,这样就可以解决问题。


 


      于是:
#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s, "#x"\n", __FILE__, __LINE__, __FUNCTION__);\
 try\
 {\
  _ASSERTE((x));\
 }\
 catch (...)\
 {\
  g_Break();\
 }\


//断点函数
int g_Break();


int g_Break()
{
 return 0;            //断点
}


        这样的代码,执行的效果应该很好!当断言导致进程崩溃时,异常被捕获到g_Break();,所以,我只要在g_Break()里面下一个断点,然后就等着断言崩溃就行了!


        然而,这只是我的一厢情愿。程序并不能执行到这里,这个断言崩溃很严重,直接是进程崩溃,异常都捕获不到!


 


        没有关系,查看TRACE信息。
*****.cpp(57):****::Close, Close socket! m_is_closed_notify=1


        在断言崩溃前面,最后一个正常的TRACE调试信息是这句,那么,就在这里下断点!


        即:先在这句下断点,中断发生后,再在g_Break()里面下断点。


#undef ATLASSERT
#define ATLASSERT(x) \
 g_TraceNoTime("%s(%d):%s, "#x, __FILE__, __LINE__, __FUNCTION__);\
 g_Break();\
 _ASSERTE((x));\


 


         经过数个小时的跟踪,最后找到问题所在。将下面的代码


void C****Dlg::OnCancel()
{
 HWND hwnd=m_hWnd;


 DestroyWindow();


........


DWORD thread_id=GetWindowThreadProcessId(hwnd, NULL);
 PostThreadMessage(thread_id, WM_QUIT, 0, 0);


}


 改为:
void C****Dlg::OnCancel()
{
 HWND hwnd=m_hWnd;


 DWORD thread_id=GetWindowThreadProcessId(hwnd, NULL);
 PostThreadMessage(thread_id, WM_QUIT, 0, 0);


........


 DestroyWindow();


}


By:zhanyonhu


 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值