维护的项目是用VC6.0开发的,安装到现场运行发现每个月都要崩溃一次,经过检查dmp文件发现是由VC6.0的crt库中new操作所对应的代码引起Microsoft visual Studio/VC98/Crt/Src/dbgheap.c),这个文件中_heap_alloc_dbg()函数用到一个long型的变量用来计数,一旦该计数器到达long型的最大值就会引发int 3中断《VC++6.0之new调用的bug》:

   
   
   
   
  1. /* break into debugger at specific memory allocation */ 
  2.         if (lRequest == _crtBreakAlloc) 
  3.             _CrtDbgBreak(); 

    这个问题其实很容易解决,经查询vs2003已经修改为这样,已经消除了这个bug:

   
   
   
   
  1. /* break into debugger at specific memory allocation */ 
  2.         if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc) 
  3.             _CrtDbgBreak(); 

    从逻辑上消除这个bug很容易,但问题是这个是MFC的库,到网上查询解决办法,有以下几种方案:

    1、用UE打开msvcrtd.dll,使用16进制编辑模式,查找得到的二进制指令,发现确实只有二处,把紧接着它们的0xCC替换为0x90,问题解决

    2、修改debug report模式,即用_CrtSetReportMode和_CrtSetReportFile将_CrtDbgBreak弹出的对话框屏蔽掉,代码如下:

   
   
   
   
  1. _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); 
  2. _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT ); 
  3. _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); 
  4. _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); 
  5. _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); 
  6. _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); 

    3、改用内存池之类的技术,减少new的使用

    4、将项目改为VS2003以上的IDE去编译

    首先试着用UE编辑这个dll,发现自己这方面知识太欠缺不知道如何改,问了身边几个人也没明白的,而且这个方案屏蔽了_CrtDbgBreak的功能也可能带来其他问题,只能放弃。

    满心欢喜的尝试第二种方案,却发现根本屏蔽不了,该弹出照样弹出,貌似只能截获一部分,但具体为什么不能生效也没搞懂。

    紧接着尝试了用内存池方式来减少new的使用,使用了boost提供的内存池试了一下还蛮容易上手使用的,不过缺点是这种方法治标不治本,MFC自己还有大量的new操作。

    比较无奈的尝试了一下用VS2008编译这套代码,竟然产生了上千条错误,很费劲的改了一天,能编译出EXE了。拿到运行环境一运行竟然崩溃……

--------------------------------------------------------------------------

    幸好最后找到了解决办法,那就是本文要隆重介绍的终极解决方案

    之前一直认为dbgheap.c文件是mfc一部分,不能改写!偶然发现了其实MFC提供了重编译它的makefile文件,请参考《重新编译生成C运行时库》。

    首先把dbgheap.c文件中bug修改过来,然后按照这篇文章介绍的方法重编译C运行时库,就一切OK啦!

    需要注意的是msvcrtd.dll我没有生成出来,编译到这里发生错误停止了,但libcmt.lib可以编译出来,最后我的项目只能使用静态链接方式使用MFC库。