不要認為職業的程序員就不會犯這類錯誤。舉幾個例子來說明這些問題。
在IE4.0中,MSHTML的HTMLDocument對象的IPersistStreamInit::Load假定傳入的IStream流的訪問指針已經定位到開頭——而在IE5.0中,IPersistStreamInit::Load會自行調用IStream::Seek
在Visual C++ 6.0中,CHTMLView類有字符串資源未釋放問題(http://support.microsoft.com/kb/241750)
在Visual C++.Net中,CHTMLView類有兩個BUG:
參數傳遞錯誤:
HRESULT CHtmlView::ExecFormsCommand(DWORD dwCommandID, VARIANT* pVarIn,
VARIANT* pVarOut)
...{
HRESULT hr = E_FAIL;
CComPtr spDoc = (IHTMLDocument2*) GetHtmlDocument();
if (spDoc != NULL)
...{
CComQIPtr spCmdTarget = spDoc;
if (spCmdTarget != NULL)
hr = spCmdTarget->Exec(&CMDSETID_Forms3, dwCommandID,
OLECMDEXECOPT_DONTPROMPTUSER, pVarOut, pVarIn);
}
return hr;
}
COM指針未釋放錯誤:
void CHtmlView::OnFilePrint()
...{
// get the HTMLDocument
if (m_pBrowserApp != NULL)
...{
CComPtr spDisp = GetHtmlDocument();
if (spDisp != NULL)
...{
// the control will handle all printing UI
CComQIPtr spTarget = spDisp;
if (spTarget != NULL)
spTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL);
}
}
}
這些不確定的行為是很大一部分不可重復(因此也很難跟蹤)的BUG的根源。舉例來說,釋放一塊內存之后,在操作系統切換到另外一個線程之前,重新分配同一塊內存,并且寫入數據這個事件發生的可能性十分之小。為了解決這些問題,Visual C++編譯器采取了一種保護性的措施,在調試模式下再分配和釋放內存時將內存用一般不會用到的值填充,例如0xccccccc,0xdddddddd和0xfefefefe(參見編譯器文檔中的/RTC參數的說明)。這樣你可以減少不可預料的程序行為,強迫BUG重現。如果你的編譯器沒有這么做,你可以自己編寫一個調試模式下專用的內存管理程序進行這樣的工作。為什么選擇這樣的值?在Intel系統中,0xcc的含義是int 3中斷(參見http://blogs.msdn.com/oldnewthing/archive/2004/11/11/255800.aspx)——如果不小心執行了這塊數據,那么程序會馬上中斷并且提示用戶,其他的值則是典型的非法數據。如果你在為其他環境編寫程序。你可能需要查閱一些資料來決定使用什么值來在調試模式下填充內存。
MFC的另一個保護措施是內存泄漏監測器——這也是在每個文件開頭要加上#define new DEBUG_NEW的原因——但是這也變更了應用程序的行為。舉例來說,為了檢查內存泄漏,MFC總是分配比所需要多的內存,然后加入調試信息。如果你的程序有訪問越界的代碼,那么有可能擦除一部分額外分配的內存,可能的結果就是在調試模式下運行正常,而在發布模式下程序崩潰。當然,這是必須的。如果你的發布版本的程序依賴于這些額外字節,那么你就有麻煩了。
在發布模式下程序的崩潰有助于你發現問題,但是也造成定位問題的困難。你可以在發布模式下加入調試信息(沒錯,在工程的C++和連接選項中選中Program Database和Generate Debug Info)來生成一個中間版本;MSDN文章Generating and Deploying Debug Symbols with Microsoft Visual C++ 6.0(http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp)甚至教你怎么怎么發布這樣的版本,但是也要注意這樣的版本和最終發布版本還是可能有區別的——特別是在程序中有BUG的情況下。另一個辦法是在調試時將EIP寄存器修改成崩潰信息中的值,這樣可以很容易在源代碼中定位造成崩潰的代碼的位置(參見http://www.codeproject.com/debug/XCrashReportPt1.asp)。
MFC開發中另一個比較有用的定位內存訪問越界方法是,將數據封裝成對象成員變量,盡量可能讓所有類都從CObject派生,并且在代碼中大量加上ASSERT_VALID。如果成員變量被越界的訪問重寫了,那么CObject指向AssertValid的虛函數表會被改寫,而ASSERT_VALID會報告這個錯誤。
不要發布調試版本,這對用戶來說并無意義。雖然這么說可能是多此一舉,但是我在玩游戲的時候還真看見過斷言對話框。調試信息是被設計用來發現問題的,不是用來隱藏問題的。如果你確實需要這么做(微軟就定期發布核心模塊的調試信息以供軟件開發人員定位問題),那么你需要讓用戶認識到調試版本和最終發布版本的性能差異,例如在程序開始時顯示一個消息。
最新Windows SDK不支持Visual C++ 6.0
可能大部分人已經知道了,但是CSDN論壇上仍舊不斷出現關于這個兼容性的提問。最新的支持Visual C++ 6.0的版本是2003年2月版,下載地址是http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm。
取消對Visual C++ 6.0的支持的原因是為了支持新的/GS參數。XP SP2和Windows Server 2003 SP 1都增加了很多安全特性,以致于新的Windows SDK中包含的編譯器和庫文件不再和Visual C++ 6.0兼容。
原文轉自:http://www.anti-gravitydesign.com