65.9K
CodeProject 正在变化。 阅读更多。
Home

错误:MFC 7.0 的 CByteArray::Serialize() 读取过多数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (3投票s)

2002年6月11日

2分钟阅读

viewsIcon

70313

MFC 7.0 的 CByteArray::Serialize() 中存在一个错误,导致其读取过多数据。

引言

在最近发布的 Visual Studio.NET 版本中,MFC 的 CByteArray::Serialize() 新实现中存在一个错误。该函数已从先前版本增强,使其能够读取超过 INT_MAX 字节的数据。这很好,但显然有一行代码被错误地复制粘贴到了加载存档的部分代码中。结果是 CByteArray::Serialize() 从存档中读取了应该读取数据量的两倍。

本文展示了一种在 Microsoft 修复此问题之前解决问题的方法。我搜索了知识库,但没有找到任何相关信息,所以希望 Microsoft 已经意识到这个问题。

当前实现

这是 MFC 的 array_b.cpp 中 CByteArray::Serialize 的当前(有错误的)实现。在下面的源代码中,请注意加粗的额外的 ar.Read(pbData, nBytesToRead) 语句。

void CByteArray::Serialize(CArchive& ar)
{
	UINT_PTR nBytesLeft;
	UINT nBytesToWrite;
	UINT nBytesToRead;
	LPBYTE pbData;

	ASSERT_VALID(this);

	CObject::Serialize(ar);

	if (ar.IsStoring())
	{
		ar.WriteCount(m_nSize);
		nBytesLeft = m_nSize*sizeof(BYTE);
		pbData = m_pData;
		while(nBytesLeft > 0)
		{
			nBytesToWrite = UINT(min(nBytesLeft, INT_MAX));
			ar.Write(pbData, nBytesToWrite);
			pbData += nBytesToWrite;
			nBytesLeft -= nBytesToWrite;
		}
	}
	else
	{
		DWORD_PTR nOldSize = ar.ReadCount();
		SetSize(nOldSize);
		nBytesLeft = m_nSize*sizeof(BYTE);
		pbData = m_pData;
		while(nBytesLeft > 0)
		{
			nBytesToRead = UINT(min(nBytesLeft, INT_MAX));
			ar.Read(pbData, nBytesToRead);
			pbData += nBytesToRead;
			nBytesLeft -= nBytesToRead;
			ar.Read(pbData, nBytesToRead);
		}
	}
}

效果

这会对从存档读取 CByteArray 的任何应用程序产生灾难性的影响。在我的例子中,我将我的 Alarm++ 软件从 VC++ 6.0 升级到 VC++.NET,发现应用程序在非常不寻常的地方崩溃。我将其追溯到从存档获取错误的数据,但真正的罪魁祸首是早期的 CByteArray.Serialize() 调用,它读取了存档中过多的数据,导致所有剩余对象都变成垃圾。

解决方法

一种解决方案是删除该行并重新构建 MFC 库。我有点担心这种解决方案,因为谁知道更改发布的 MFC 库可能会引入什么其他问题。此外,如果使用 MFC DLL(我不使用,所以也许以后会考虑这种解决方案),它将引入发布您自己版本的 MFC DLL 的整个问题。

目前,我更喜欢在自己的代码中修复它,等待服务发布来修复它,然后将其删除。幸运的是,Serialize() 是虚拟的,因此我们可以从 CByteArray 派生自己的类并使用它。当然,CByteArray 在 MFC 内部使用,因此问题可能也会出现在其他地方(重新编译 MFC 库的另一个原因)。

class MyCByteArray : public CByteArray 
{
public:
	MyCByteArray() {}
	virtual ~MyCByteArray() {}

	virtual void Serialize(CArchive& ar)
	{
		UINT_PTR nBytesLeft;
		UINT nBytesToRead;
		LPBYTE pbData;

		ASSERT_VALID(this);

		CObject::Serialize(ar);

		if (ar.IsStoring())
		{
			// We can just call the base implementation here.
			CByteArray::Serialize(ar);
		}
		else
		{
			DWORD_PTR nOldSize = ar.ReadCount();
			SetSize(nOldSize);
			nBytesLeft = m_nSize*sizeof(BYTE);
			pbData = m_pData;
			while(nBytesLeft > 0)
			{
				nBytesToRead = UINT(min(nBytesLeft, INT_MAX));
				ar.Read(pbData, nBytesToRead);
				pbData += nBytesToRead;
				nBytesLeft -= nBytesToRead;
				//BUG: ar.Read(pbData, nBytesToRead);
			}
		}
	}
};

示例

我认为这不值得发布一个完整的示例项目,但这里有一个小的代码片段,向您展示它可以使用与 CByteArry 相同的方式使用。

      // create a byte array
      MyCByteArray bytes;
      for (int i = 0; i < 5; ++i)
         bytes.Add(i);

      // write out the byte array
      CFile f("test.ar", CFile::modeCreate | CFile::typeBinary | CFile::modeWrite);
      CArchive arStore(&f, CArchive::store);
      bytes.Serialize(arStore);
      arStore.Close();
      f.Close();

      bytes.RemoveAll();   // clear the array

      // read the array back
      f.Open("test.ar", CFile::typeBinary | CFile::modeRead);
      CArchive arLoad(&f, CArchive::load);
      bytes.Serialize(arLoad);

      // display it
      for (int i = 0; i < bytes.GetSize(); ++i)
         cout << "Byte[" << i << "] = " << hex << (int) bytes[i] << dec << endl;

结论

如果您的代码序列化 **CByteArray** 对象,您应该意识到 Visual Studio.NET MFC 7.0 实现的 CByteArray::Serialize() 中的此问题。

© . All rights reserved.