﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-行进中开火-随笔分类-C++ &amp; C</title><link>http://www.cppblog.com/sigepluto/category/7797.html</link><description>C++夜未眠</description><language>zh-cn</language><lastBuildDate>Sun, 28 Mar 2010 21:23:00 GMT</lastBuildDate><pubDate>Sun, 28 Mar 2010 21:23:00 GMT</pubDate><ttl>60</ttl><item><title>MFC中一个危险的Bug</title><link>http://www.cppblog.com/sigepluto/archive/2010/03/26/110622.html</link><dc:creator>Jakcie</dc:creator><author>Jakcie</author><pubDate>Fri, 26 Mar 2010 14:15:00 GMT</pubDate><guid>http://www.cppblog.com/sigepluto/archive/2010/03/26/110622.html</guid><wfw:comment>http://www.cppblog.com/sigepluto/comments/110622.html</wfw:comment><comments>http://www.cppblog.com/sigepluto/archive/2010/03/26/110622.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/sigepluto/comments/commentRss/110622.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sigepluto/services/trackbacks/110622.html</trackback:ping><description><![CDATA[
<p>&nbsp;</p> <p>上次说日本海啸警报的时候，程序出错。在解析代码的时候，发现了MFC中的一个Bug。</p> <p>一。问题的产生。</p> <p>这个程序，用来处理日本各种天气预报数据，包括灾害的预报。如果地震，台风之类的自然灾害到来，程序会把预报数据进行处理，生成相应的警报信息，并在电视上面显示滚动的字幕来提示。程序本身，是几年前公司的其他人写的。里面有涉及到文件读写的地方，有很多地方，用了MFC中自带的文件读写类CStdioFile。</p> <p>CStdioFile这个文件读写类，估计大家都不陌生。这个类的父类，是CFile类。CStdioFile类本身的功能也很简单。CStdioFile类有一个成员函数是ReadString，函数的定义如下：</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 58px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">virtual</span> LPTSTR ReadString(__out_ecount_z(nMax) LPTSTR lpsz, __in UINT nMax);
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">virtual</span> BOOL ReadString(CString&amp; rString);</pre></pre>MSDN定义如下<a href="http://msdn.microsoft.com/library/x5t0zfyf(VS.80).aspx">http://msdn.microsoft.com/library/x5t0zfyf(VS.80).aspx</a>：<pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 118px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">BOOL ReadString(CString&amp; rString);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">throw</span>( CFileException );
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">Return Value
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">A pointer to the buffer containing the text data. NULL <span style="color: #0000ff">if</span> end-of-file was reached without reading any data; <span style="color: #0000ff">or</span> <span style="color: #0000ff">if</span> boolean, FALSE <span style="color: #0000ff">if</span> end-of-file was reached without reading any data.</pre></pre>
<p>ReadString函数能直接读取文本中的一行数据到CString中，很方便。读到文件结尾，没有读出任何数据的时候，返回FALSE。很简单的函数，但恰恰是这个函数有Bug。</p>
<p>程序在处理数据的时候，会生成一些临时文件，然后会读取这些临时文件中的数据，读取操作，正是用的CStdioFile的ReadString函数。读取流程很简单：</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 90px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">while</span>(dFile.ReadString(Str_temp))
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">{
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    doSomething();
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>当时的现象为，读取到最后一行，总是直接返回FALSE，怎么也读不出最后一行来。看了看文件的最后一行，包含2176个字符的数据，没有换行符。没有任何异常啊。当时没想到是MFC的Bug，因为以前有这样那样的毛病，多数是预报数据本身有问题，所以这次也是先分析数据了。分析来分析去，没发现这次的数据有什么异常。后来发现如果最后一行的文件不是2176个字符，就能正常读出来。奇了怪了，2176也不是什么特殊长度啊。实验了几次后，觉的是在不对劲。莫非是MFC的Bug？</p>
<p>二。发现问题所在</p>
<p>决定看看MFC的代码再说。做了个简单的测试程序，跟到MFC代码里一看，果然是MFC的问题！测试代码如下：</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 147px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    CStdioFile  dFile;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    dFile.Open("<span style="color: #8b0000">text.txt</span>",CFile::modeRead);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    CString str;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">while</span> (dFile.ReadString(str) != FALSE )
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        printf("<span style="color: #8b0000">%s</span>", str);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    dFile.Close();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre></pre>
<p>测试代码很简单，读text.txt文件中的每一行，然后打印出来。还是2176个字符就不行。确定了不是数据的问题，就是MFC代码本身的Bug。</p>
<p>MFC的ReadString代码如下：（中文是我加的注释）</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">BOOL CStdioFile::ReadString(CString&amp; rString)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">{
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    ASSERT_VALID(<span style="color: #0000ff">this</span>);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    rString = &amp;afxChNil;    <span style="color: #008000">// empty string without deallocating</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">const</span> <span style="color: #0000ff">int</span> nMaxSize = 128;  <span style="color: #008000">//临时字符串的长度</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    LPTSTR lpsz = rString.GetBuffer(nMaxSize);  <span style="color: #008000">//保存每次读取到的字符串到CString中</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    LPTSTR lpszResult;  <span style="color: #008000">//指向每次读到的字符串</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">int</span> nLen = 0;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">for</span> (;;)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        lpszResult = _fgetts(lpsz, nMaxSize+1, m_pStream); <span style="color: #008000">//读取操作</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        rString.ReleaseBuffer();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #008000">// handle error/eof case</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #0000ff">if</span> (lpszResult == NULL &amp;&amp; !feof(m_pStream))
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            clearerr(m_pStream);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            AfxThrowFileException(CFileException::generic, _doserrno,
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">                m_strFileName);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #008000">// if string is read completely or EOF</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #0000ff">if</span> (lpszResult == NULL ||
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            (nLen = lstrlen(lpsz)) &lt; nMaxSize ||
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            lpsz[nLen-1] == '\n')
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            <span style="color: #0000ff">break</span>;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        nLen = rString.GetLength();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen; <span style="color: #008000">//位置后移</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #008000">// remove '\n' from end of string if present</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    lpsz = rString.GetBuffer(0);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    nLen = rString.GetLength();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">if</span> (nLen != 0 &amp;&amp; lpsz[nLen-1] == '\n') <span style="color: #008000">// 最后结果中，去掉回车符</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        rString.GetBufferSetLength(nLen-1); 
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">return</span> lpszResult != NULL;  <span style="color: #008000">// 这里就是Bug的关键。返回值不对！</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>可以看到，ReadString的底层，是用fgets来读取文件的。在内部，每次读取128个字符到CString中，然后位置后移，反复读取128个字符，直到遇到回车符或者文件结束。最后把回车符去掉，返回一个CString。其中，lpszResult也指向每次读出的字符串。</p>
<p>这里就看出问题所在了，2176个字符，正好是128的17倍！也就是说，<font color="#ff0000"><strong>只要文件最后一行是128倍数个字符，就一定会返回FALSE。</strong></font></p>
<p><font color="#000000">为什么会这样呢，因为ReadString在每次读取128个字符的时候，用lpszResult指向读取到的字符串。如果读满了128个字符，就继续读，如果读到的字符不够128个，那么就结束读取。</font></p>
<p><font color="#000000">当一行数据正好为128的倍数，又没有回车符的时候，会发生什么呢？比如最后一行数据是128个，那么，读一次128个字符，会继续读下一次，但是下一次的读取，什么也没有读到，lpszResult就指向NULL，最后的返回值，是return lpszResult != NULL; 所以返回FALSE。</font></p>
<p><font color="#000000">但之前读到的128个字符，已经在CString里面了。</font><font color="#ff0000">也就是说实际上读取已经成功了，但还是返回了FALSE。返回值不恰当！</font></p>
<p><font color="#ff0000">Bug的描述：当文件的最后一行数据，正好是128的倍数个字符的时候，用</font><font color="#ff0000">ReadString读取，一定会返回FALSE。但实际上读取是成功的，返回的CString中的数据是正确的！（VC6.0中存在这个Bug，VS2005中，没有这个Bug）</font></p>
<p><font color="#000000">这个Bug，只会影响到最后一行数据。因为如果有换行符的存在，lpszResult就不会为NULL。</font></p>
<p><font color="#000000">三。解决方法</font></p>
<p><font color="#000000">要解决这个问题，也简单，修改一下判断ReadString成功与否的语句：</font></p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 42px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">while</span> (dFile.ReadString(str) != <span style="color: #0000ff">FALSE</span> || str.GetLength() != 0)</pre></pre>
<p>在返回FALSE的情况下，CString的长度不为0，就不算读取失败。或者这样:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; height: 44px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">if</span>(!dFile.ReadString(str) &amp;&amp; str.GetLength() == 0)</pre></pre>
<p>在返回FALSE并且CString的长度为0，则算读取失败，否则就是读取成功。</p>
<p>这个程序，是用VC6.0做的，我有看了看VC2005中的代码，发现这个Bug被修复了，代码如下：</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">BOOL CStdioFile::ReadString(CString&amp; rString)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">{
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    ASSERT_VALID(<span style="color: #0000ff">this</span>);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    rString = _T("<span style="color: #8b0000"></span>");    <span style="color: #008000">// empty string without deallocating</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">const</span> <span style="color: #0000ff">int</span> nMaxSize = 128;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    LPTSTR lpsz = rString.GetBuffer(nMaxSize);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    LPTSTR lpszResult;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">int</span> nLen = 0;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">for</span> (;;)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        lpszResult = _fgetts(lpsz, nMaxSize+1, m_pStream);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        rString.ReleaseBuffer();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #008000">// handle error/eof case</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #0000ff">if</span> (lpszResult == NULL &amp;&amp; !feof(m_pStream))
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            Afx_clearerr_s(m_pStream);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            AfxThrowFileException(CFileException::genericException, _doserrno,
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">                m_strFileName);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #008000">// if string is read completely or EOF</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        <span style="color: #0000ff">if</span> (lpszResult == NULL ||
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            (nLen = (<span style="color: #0000ff">int</span>)lstrlen(lpsz)) &lt; nMaxSize ||
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            lpsz[nLen-1] == '\n')
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">            <span style="color: #0000ff">break</span>;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        nLen = rString.GetLength();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #008000">// remove '\n' from end of string if present</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    lpsz = rString.GetBuffer(0);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    nLen = rString.GetLength();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">if</span> (nLen != 0 &amp;&amp; lpsz[nLen-1] == '\n')
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">        rString.GetBufferSetLength(nLen-1);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">    <span style="color: #0000ff">return</span> nLen != 0; <span style="color: #008000">//返回值变了！</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>我们看到，VC2005中，读取部分的代码与VC6.0中的代码完全一样。不一样的地方只是返回值的部分。VC2005的ReadString中，返回值为</p>
<p>return nLen != 0;</p>
<p>也就是说，只要读出的CString的长度不为0就为读取成功。与我修改后的方法完全一致。就这样向客户解释，然后修改了。悲剧的是，几年前所有程序中所有使用ReadString函数的地方，都要进行修改。。。</p>
<p>MFC的这个Bug比较隐蔽，平常不容易发现，但一旦遇到特殊长度的数据，就会表现异常。所以，在用VC6.0开发的时候，尽量避免使用ReadString，或者在使用中，多判断一步读取出来的CString长度。避开这个Bug。</p><img src ="http://www.cppblog.com/sigepluto/aggbug/110622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sigepluto/" target="_blank">Jakcie</a> 2010-03-26 22:15 <a href="http://www.cppblog.com/sigepluto/archive/2010/03/26/110622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>快速扩展文件大小</title><link>http://www.cppblog.com/sigepluto/archive/2010/03/03/108814.html</link><dc:creator>Jakcie</dc:creator><author>Jakcie</author><pubDate>Wed, 03 Mar 2010 09:57:00 GMT</pubDate><guid>http://www.cppblog.com/sigepluto/archive/2010/03/03/108814.html</guid><wfw:comment>http://www.cppblog.com/sigepluto/comments/108814.html</wfw:comment><comments>http://www.cppblog.com/sigepluto/archive/2010/03/03/108814.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sigepluto/comments/commentRss/108814.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sigepluto/services/trackbacks/108814.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在实际的开发过程中，我们可能需要在文件生成时就立即将文件更改为指定的大小，以便于后续简化对文件的并发操作，这一点在各类的下载工具中有很好的体现。<br>具体方法有：lseek或者 truncate，在Windows中，可以选择 _lseek 或者 SetEndOfFile。&nbsp;&nbsp;<a href='http://www.cppblog.com/sigepluto/archive/2010/03/03/108814.html'>阅读全文</a><img src ="http://www.cppblog.com/sigepluto/aggbug/108814.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sigepluto/" target="_blank">Jakcie</a> 2010-03-03 17:57 <a href="http://www.cppblog.com/sigepluto/archive/2010/03/03/108814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>