我正在处理非常旧的遗留代码,我正在将它从32位移植到64位。
其中一个让我头疼的问题是MFC的序列化。32位和64位之间的一个区别是指针数据的大小。这意味着,例如,如果出于某种原因,我序列化了CArray
的大小,如
ar << m_array.GetSize();
由于GetSize
返回一个INT_PTR
,因此32和64平台之间的数据是不同的。为了使序列化数据与以32位和64位编译的相同应用程序完全兼容,我在存储阶段强制了数据类型,在阅读阶段也强制了相同的数据类型。(非常肯定32位对于此数据足够了)
商店
ar << (int)m_array.GetSize();
阅读
int iNumSize = 0;
ar >> iNumSize ;
换句话说,无论应用程序是以32位还是64位编译,都可以将此数据序列化为int
。
现在我对CArray
类型的序列化有一个疑问;要序列化CArray
,代码使用构建的CArchive
序列化
//defined as CArray m_arrayVertex; on .h
m_arrayVertex.Serialize(ar);
并且此Serialize
是在MFC文件afxtemp.h
中使用此模板定义的
template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::Serialize(CArchive& ar)
{
ASSERT_VALID(this);
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar.WriteCount(m_nSize);
}
else
{
DWORD_PTR nOldSize = ar.ReadCount();
SetSize(nOldSize, -1);
}
SerializeElements<TYPE>(ar, m_pData, m_nSize);
}
其中(afx.h
)
// special functions for reading and writing (16-bit compatible) counts
DWORD_PTR ReadCount();
void WriteCount(DWORD_PTR dwCount);
这里我的问题:ReadCount
和WriteCount
使用不同平台之间大小不同的DWORD_PTR
......这种串行化在32/64位兼容,还是由于大小变化,串行化数据分别仅在每个平台上工作?
我的意思是数据可以被32和64应用程序读取,没有错误-评论说它也适用于“16位”,我没有发现任何关于这个序列化的细节。
如果这不起作用,有一个变通方案,以这样的方式序列化CArray
的数据是完全兼容的32和64应用程序?
编辑:两个答案都很好。我只是接受先到者为先的解决方案。非常感谢两个,希望能帮助到其他人!
2条答案
按热度按时间mctunoxg1#
正如您所写的,
ReadCount
返回一个DWORD_PTR
,它是32位宽还是64位宽,这取决于代码是被编译为32位还是64位代码。现在,只要实际的对象计数适合32位,那么由32位或64位程序编写的文件之间的互操作性就不存在问题。
另一方面,如果你的64位代码序列化一个
CArray
,它有超过4294967295个元素(这是不太可能发生的),那么你会遇到麻烦,如果你想从一个32位程序读取反序列化这个文件,但在一个32位程序中,CArray
不能存储超过4294967295。长话短说,您不需要做任何特殊的事情,只需要序列化/反序列化您的数据。
zphenhs42#
CArray
示例化的项目计数的存储和检索分别在CArchive::WriteCount
和CArchive::ReadCount
中实现。它们可以向流写入16位(
WORD
)、32位(DWORD
)或64位(在64位平台上为DWORD_PTR
)值,也可以从流读取这些值。0xFFFF
,则将项目计数写入16位WORD
值(WORD)0xFFFF
)转储到流中,后跟DWORD
)0xFFFF'FFFF
,则将项目计数写入32位DWORD
值(DWORD)0xFFFFFFFF
)转储到流中,然后将项目计数作为64位值(DWORD_PTR
)下表根据
CArray
中的项目计数总结了流布局(其中表示流中不存在的值):| 项目计数 * n *|
WORD
|DWORD
|DWORD_PTR
|| - ------|- ------|- ------|- ------|
| * n 〈0xFFFF| 无 |❌|❌|
| 0xFFFF〈= n 〈0xFFFF 'FFFF|0xFFFF| 无 *|❌|
| * n == 0xFFFF 'FFFF(仅限32位)|0xFFFF|0xFFFF 'FFFF|❌|
| 0xFFFF 'FFFF〈= n (仅限64位)|0xFFFF|0xFFFF 'FFFF| 无 *|
反序列化流时,代码读取项计数值,检查它是否与"无效值"标记匹配,如果找到标记,则继续使用较大的值。
只要
CArray
保存的值不超过0xFFFF'FFFE
,这就适用于所有位数。您不能拥有用尽整个地址空间的CArray
。当从64位进程进行序列化时,您只需要确保数组中的项不超过
0xFFFF'FFFE
。对于具有少于
0xFFFF'FFFF
(4294967295)个项的CArray
,序列化流是逐字节相同的,而不管它是在32位平台上还是在64位平台上创建的。在32位平台上,
CArray
恰好包含0xFFFF'FFFF
项,这是一种奇怪的情况1。如果在64位平台上将其流输出并读回,流中的size字段将被误认为是"无效值"标记,从而导致灾难性后果。幸运的是,这不是我们需要担心的事情。32位进程不能分配大小为可用地址空间的倍数的容器。这涵盖了在32位平台上序列化的流在64位平台上使用的场景。实际上,一切都按设计工作。
然后转向另一个方向:在64位平台上创建的、要在32位平台上反序列化的流。此处唯一相关的分歧是容器大于32位程序甚至可以表示的容器。64位序列化程序将丢弃"无效值"标记(
DWORD
)后接实际项目计数(DWORD_PTR
)2. 32位解串器将假定标记(0xFFFF'FFFF
)是真实的项目计数,并且在不查看 * 实际 * 项目计数的情况下使后续内存分配失败。在发生任何数据损坏之前,使用适当的异常处理从那里删除内容3。这并不是一个新的错误模式,也不是跨位互操作性所独有的。如果进程耗尽资源,在32位平台上串行化的
CArray
也可能无法在32位平台上反串行化。这可能比耗尽内存早得多,因为CArray
需要 * 连续 * 内存。1 * 上表第3行。*
2 * 上表第4行。*
3 * 这是假设调用堆栈中没有
catch(...)
一直忽略。*