windows CArray MFC序列化多平台,16、32和64位

nqwrtyyt  于 2023-01-14  发布在  Windows
关注(0)|答案(2)|浏览(188)

我正在处理非常旧的遗留代码,我正在将它从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);

这里我的问题:ReadCountWriteCount使用不同平台之间大小不同的DWORD_PTR......这种串行化在32/64位兼容,还是由于大小变化,串行化数据分别仅在每个平台上工作?
我的意思是数据可以被32和64应用程序读取,没有错误-评论说它也适用于“16位”,我没有发现任何关于这个序列化的细节。
如果这不起作用,有一个变通方案,以这样的方式序列化CArray的数据是完全兼容的32和64应用程序?
编辑:两个答案都很好。我只是接受先到者为先的解决方案。非常感谢两个,希望能帮助到其他人!

mctunoxg

mctunoxg1#

正如您所写的,ReadCount返回一个DWORD_PTR,它是32位宽还是64位宽,这取决于代码是被编译为32位还是64位代码。
现在,只要实际的对象计数适合32位,那么由32位或64位程序编写的文件之间的互操作性就不存在问题。
另一方面,如果你的64位代码序列化一个CArray,它有超过4294967295个元素(这是不太可能发生的),那么你会遇到麻烦,如果你想从一个32位程序读取反序列化这个文件,但在一个32位程序中,CArray不能存储超过4294967295。
长话短说,您不需要做任何特殊的事情,只需要序列化/反序列化您的数据。

zphenhs4

zphenhs42#

CArray示例化的项目计数的存储和检索分别在CArchive::WriteCountCArchive::ReadCount中实现。
它们可以向流写入16位(WORD)、32位(DWORD)或64位(在64位平台上为DWORD_PTR)值,也可以从流读取这些值。

  • 如果项目计数小于0xFFFF,则将项目计数写入16位WORD
  • 否则,将"invalid value"标记((WORD)0xFFFF)转储到流中,后跟
  • 32位:项目计数为32位值(DWORD
  • 64位:如果项目计数小于0xFFFF'FFFF,则将项目计数写入32位DWORD
  • 否则,将"invalid value"标记((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(...)一直忽略。*

相关问题