MFC中乙個危險的Bug

2021-05-24 07:56:17 字數 4214 閱讀 2002

mfc中乙個危險的bug

上次說日本海嘯警報的時候,程式出錯。在解析**的時候,發現了mfc中的乙個bug。

一。問題的產生。

這個程式,用來處理日本各種天氣預報資料,包括災害的預報。如果**,颱風之類的自然災害到來,程式會把預報資料進行處理,生成相應的警報資訊,並在電視上面顯示滾動的字幕來提示。程式本身,是幾年前公司的其他人寫的。裡面有涉及到檔案讀寫的地方,有很多地方,用了mfc中自帶的檔案讀寫類cstdiofile。

cstdiofile這個檔案讀寫類,估計大家都不陌生。這個類的父類,是cfile類。cstdiofile類本身的功能也很簡單。cstdiofile類有乙個成員函式是readstring,函式的定義如下:

virtual lptstr readstring(__out_ecount_z(nmax) lptstr lpsz, __in uint nmax);

virtual bool readstring(cstring& rstring);

msdn定義如下http://msdn.microsoft.com/library/x5t0zfyf(vs.80).aspx:

bool readstring(cstring& rstring);

throw( cfileexception );

return value

a pointer to the buffer containing the text data. null if end-of-file was reached without reading any data; or if boolean, false if end-of-file was reached without reading any data.

readstring函式能直接讀取文字中的一行資料到cstring中,很方便。讀到檔案結尾,沒有讀出任何資料的時候,返回false。很簡單的函式,但恰恰是這個函式有bug。

程式在處理資料的時候,會生成一些臨時檔案,然後會讀取這些臨時檔案中的資料,讀取操作,正是用的cstdiofile的readstring函式。讀取流程很簡單:

while(dfile.readstring(str_temp))

當時的現象為,讀取到最後一行,總是直接返回false,怎麼也讀不出最後一行來。看了看檔案的最後一行,包含2176個字元的資料,沒有換行符。沒有任何異常啊。當時沒想到是mfc的bug,因為以前有這樣那樣的毛病,多數是預報資料本身有問題,所以這次也是先分析資料了。分析來分析去,沒發現這次的資料有什麼異常。後來發現如果最後一行的檔案不是2176個字元,就能正常讀出來。奇了怪了,2176也不是什麼特殊長度啊。實驗了幾次後,覺的是在不對勁。莫非是mfc的bug?

二。發現問題所在

決定看看mfc的**再說。做了個簡單的測試程式,跟到mfc**裡一看,果然是mfc的問題!測試**如下:

cstdiofile  dfile;

dfile.open("text.txt",cfile::moderead);

cstring str;

while (dfile.readstring(str) != false )

dfile.close();

測試**很簡單,讀text.txt檔案中的每一行,然後列印出來。還是2176個字元就不行。確定了不是資料的問題,就是mfc**本身的bug。

mfc的readstring**如下:(中文是我加的注釋)

bool cstdiofile::readstring(cstring& rstring)

// if string is read completely or eof

if (lpszresult == null ||

(nlen = lstrlen(lpsz)) < nmaxsize ||

lpsz[nlen-1] == '/n')

break;

nlen = rstring.getlength();

lpsz = rstring.getbuffer(nmaxsize + nlen) + nlen; //位置後移

}// remove '/n' from end of string if present

lpsz = rstring.getbuffer(0);

nlen = rstring.getlength();

if (nlen != 0 && lpsz[nlen-1] == '/n') // 最後結果中,去掉回車符

rstring.getbuffersetlength(nlen-1);

return lpszresult != null;  // 這裡就是bug的關鍵。返回值不對!

}可以看到,readstring的底層,是用fgets來讀取檔案的。在內部,每次讀取128個字元到cstring中,然後位置後移,反覆讀取128個字元,直到遇到回車符或者檔案結束。最後把回車符去掉,返回乙個cstring。其中,lpszresult也指向每次讀出的字串。

這裡就看出問題所在了,2176個字元,正好是128的17倍!也就是說,只要檔案最後一行是128倍數個字元,就一定會返回false。

為什麼會這樣呢,因為readstring在每次讀取128個字元的時候,用lpszresult指向讀取到的字串。如果讀滿了128個字元,就繼續讀,如果讀到的字元不夠128個,那麼就結束讀取。

當一行資料正好為128的倍數,又沒有回車符的時候,會發生什麼呢?比如最後一行資料是128個,那麼,讀一次128個字元,會繼續讀下一次,但是下一次的讀取,什麼也沒有讀到,lpszresult就指向null,最後的返回值,是return lpszresult != null; 所以返回false。

但之前讀到的128個字元,已經在cstring裡面了。也就是說實際上讀取已經成功了,但還是返回了false。返回值不恰當!

bug的描述:當檔案的最後一行資料,正好是128的倍數個字元的時候,用readstring讀取,一定會返回false。但實際上讀取是成功的,返回的cstring中的資料是正確的!(vc6.0中存在這個bug,vs2005中,沒有這個bug)

這個bug,只會影響到最後一行資料。因為如果有換行符的存在,lpszresult就不會為null。

三。解決方法

要解決這個問題,也簡單,修改一下判斷readstring成功與否的語句:

while (dfile.readstring(str) != false || str.getlength() != 0)

在返回false的情況下,cstring的長度不為0,就不算讀取失敗。或者這樣:

if(!dfile.readstring(str) && str.getlength() == 0)

在返回false並且cstring的長度為0,則算讀取失敗,否則就是讀取成功。

這個程式,是用vc6.0做的,我有看了看vc2005中的**,發現這個bug被修復了,**如下:

bool cstdiofile::readstring(cstring& rstring)

// if string is read completely or eof

if (lpszresult == null ||

(nlen = (int)lstrlen(lpsz)) < nmaxsize ||

lpsz[nlen-1] == '/n')

break;

nlen = rstring.getlength();

lpsz = rstring.getbuffer(nmaxsize + nlen) + nlen;

}// remove '/n' from end of string if present

lpsz = rstring.getbuffer(0);

nlen = rstring.getlength();

if (nlen != 0 && lpsz[nlen-1] == '/n')

rstring.getbuffersetlength(nlen-1);

return nlen != 0; //返回值變了!

}我們看到,vc2005中,讀取部分的**與vc6.0中的**完全一樣。不一樣的地方只是返回值的部分。vc2005的readstring中,返回值為

return nlen != 0;

也就是說,只要讀出的cstring的長度不為0就為讀取成功。與我修改後的方法完全一致。就這樣向客戶解釋,然後修改了。悲劇的是,幾年前所有程式中所有使用readstring函式的地方,都要進行修改。。。

mfc的這個bug比較隱蔽,平常不容易發現,但一旦遇到特殊長度的資料,就會表現異常。所以,在用vc6.0開發的時候,盡量避免使用readstring,或者在使用中,多判斷一步讀取出來的cstring長度。避開這個bug。

MFC中乙個危險的Bug

上次說日本海嘯警報的時候,程式出錯。在解析 的時候,發現了mfc中的乙個bug。一。問題的產生。這個程式,用來處理日本各種天氣預報資料,包括災害的預報。如果 颱風之類的自然災害到來,程式會把預報資料進行處理,生成相應的警報資訊,並在電視上面顯示滾動的字幕來提示。程式本身,是幾年前公司的其他人寫的。裡...

快排中乙個巨集的bug

今天寫快速排序,碰到乙個問題 使用了下面的巨集 define swap a,b int temp a a b b temp 咋看沒有問題,但是在使用時a,不是乙個值而是乙個表示式 swap v m v i 這樣在編譯器預處理的時候會展開為 int temp v m v m v i v i temp ...

由框架中乙個BUG引起的

今天加班在給new guys做培訓的時候,發生了乙個莫名其妙的問題,幾經周折,才發現是公司現有框架的乙個bug。xml converter valueobject objectid lib class com.icsc.tm.mscdao.tmjcs03vo type unique converte...