﻿<?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++博客-行进中开火-随笔分类-Windows &amp; MFC</title><link>http://www.cppblog.com/sigepluto/category/7799.html</link><description>C++夜未眠</description><language>zh-cn</language><lastBuildDate>Sun, 28 Mar 2010 06:03:26 GMT</lastBuildDate><pubDate>Sun, 28 Mar 2010 06:03:26 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>3</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/08/109161.html</link><dc:creator>Jakcie</dc:creator><author>Jakcie</author><pubDate>Sun, 07 Mar 2010 18:07:00 GMT</pubDate><guid>http://www.cppblog.com/sigepluto/archive/2010/03/08/109161.html</guid><wfw:comment>http://www.cppblog.com/sigepluto/comments/109161.html</wfw:comment><comments>http://www.cppblog.com/sigepluto/archive/2010/03/08/109161.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/sigepluto/comments/commentRss/109161.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sigepluto/services/trackbacks/109161.html</trackback:ping><description><![CDATA[
<p>2月27日，智利发生8.8级特大地震，1个世纪以来最强的地震！全球都在关注。其中，日本的反应尤其大。日本本身自然灾害特别多。火山，地震，海啸，以及洪水。所以，对这种自然灾害天然的比较敏感。最主要的原因，在于智利的地震，会影响到日本！这是有前车之鉴的。</p> <p>1960年智利海域发生了9.5级（太恐怖了。。。）地震。引起了海啸，一直穿过整个太平洋，从南美，一直到东亚。日本，夏威夷，菲律宾都有200多人死亡。所以，这次8.8级地震，如果再来一次海啸，那可不得了啊。这次智利爆发的地震，引发了剧烈的海啸。如今，海啸的巨浪正在横过太平洋，直奔日本海岸而来。估计到达日本时，浪高依然可以达到10-20英尺。</p> <p>日本全国都在紧急动员防范海啸，电视台在电视屏幕一角实时展示一幅日本地图，所有专家预测会遭到海啸袭击的地区都被标记出来，如今，从北海道 到冲绳，整个日本东海岸几乎都变成了一片红色。屏幕上方则在滚动播出沿海各地发布的避难通知，例如，青森县已经有一万九千多户被要求离家进入公用避难设 施。日本全国到现在为止已有40万人离开家园。整个流程顺畅，井然有序。这一切，一方面反映了日本在自然灾害面前出色的准备工作，另一方面也反映了日本民 间的恐惧。</p> <p>广播员在不断在播报各地海平面的增高情况，镜头不时切换到沿岸各地，报道当地状况和抢险准备的情况，很多沿海公路如东名高速公路已经关闭，海滨公园停止营业，船舶纷纷进入避难路线。经常有画外音插断播音员的播报，紧急通报某地海平面出现异常增高。转眼间就有了一种陷入某场战争的感觉。</p><!--more--> <p>发个图，看的比较清楚。整个日本靠太平洋的一边，全部是海啸警报。</p><a href="http://picasaweb.google.com/lh/photo/c4iVEuWU3D00ysfcm8jyIQ?authkey=Gv1sRgCPrNiYO6gbrXVw&amp;feat=embedwebsite"><img src="http://lh6.ggpht.com/_-4ZKm7IEs2Y/S5Pl5EoyFmI/AAAAAAAABs8/KhZxz3yZb9Q/s800/tsunami12%5B5%5D.jpg"></a>  <p>公司做的项目，正好是给各个电视台做的天气预报项目，地震台风海啸的预报，也包含在内。关键时刻，日本电视台打电话说，预报图显示的有误，只能看到大阪的浪高，其他地方没有浪高。导致只能显示部分的警报图。东京电视台也打电话说电视上滚动显示的警报文字，用我们的程序处理不了，显示出不来。马上乱套了，当天公司几个人都通宵在处理。最后强制显示全国的警报图。</p> <p>最后发现，日本电视台预报图显示有误，其实是正确的，本来其他地方就没有浪高。为什么呢？因为根本就没那么大的浪！</p><a href="http://picasaweb.google.com/lh/photo/bPSxltmMsHTQk_e1Z-iF8w?authkey=Gv1sRgCPrNiYO6gbrXVw&amp;feat=embedwebsite"><img src="http://lh4.ggpht.com/_-4ZKm7IEs2Y/S5Pl6TYjq6I/AAAAAAAABtE/izL_0oxvgaM/s800/clear_tsunami%5B3%5D.png"></a>  <p>日本气象厅在3月1日10点15分全面解除太平洋沿岸海啸警报。 <br>气象厅负责地震海啸检测的课长在记者会见中谢罪称，&#8220;对于海啸预测大大超过了实际情况，以及警报时间过长表示歉意。&#8221;</p> <p>&#160;</p> <p>为什么这次的地震，没有引起很大的海啸呢？</p> <p>智利此次地震所引发的海啸也具有很强的方向性，英国威尔士大学新港学院（University of Wales, Newport）的Simon Haslett 说&#8220;这回的海啸非常有方向性，而不是那种均匀向四周传播的&#8216;往池塘里扔石头&#8217;似的波浪&#8221;。他表示离震中最近的海岸，以及胡安费尔南德斯群岛（Juan Fernandez Islands）海啸非常强，但是其他方向的海啸能量和高度迅速减退。 </p> <p>而且，地震震源的相对深度—35公里—可能也减小了海床的上升，而正是海床的上升排挤了海水。英国伦敦大学学院Bill McGuire表示&#8220;相比比2004年的印度洋地震，智利地震要更深，释放到地表的能量也更少&#8221;。 </p> <p>虚惊一场啊。 </p> <p>至于东京电视台的预报文字处理不出来，经过我一步步Debug，最后发现，是MFC的一个Bug造成的！气死我了。导致所有代码涉及这个Bug的地方都要修改。下篇日志，详细说说这个Bug。</p><img src ="http://www.cppblog.com/sigepluto/aggbug/109161.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-08 02:07 <a href="http://www.cppblog.com/sigepluto/archive/2010/03/08/109161.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>