維護的項目是用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》:
- /* break into debugger at specific memory allocation */
- if (lRequest == _crtBreakAlloc)
- _CrtDbgBreak();
這個問題其實很容易解決,經查詢vs2003已經修改為這樣,已經消除了這個bug:
- /* break into debugger at specific memory allocation */
- if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
- _CrtDbgBreak();
從邏輯上消除這個bug很容易,但問題是這個是MFC的庫,到網上查詢解決辦法,有以下幾種方案:
1、用UE打開msvcrtd.dll,使用16進制編輯模式,查找得到的二進制指令,發現確實只有二處,把緊接著它們的0xCC替換為0x90,問題解決
2、修改debug report模式,即用_CrtSetReportMode和_CrtSetReportFile將_CrtDbgBreak彈出的對話框屏蔽掉,代碼如下:
- _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
- _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
- _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
- _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
- _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
- _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庫。
本文出自 “Hello Snow!” 博客,請務必保留此出處http://hellosnow.blog.51cto.com/164899/732143