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






4.71/5 (3投票s)
2002年6月11日
2分钟阅读

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() 中的此问题。