MFC MDI 文档的数据结构与COM实现

在探讨MFCMDI文档的数据结构时,首先需要理解其复杂性。在这种结构中,对象可能在结构内部被多次引用。这意味着,如果使用标准的序列化方法,可能会导致对象被多次创建,从而破坏了原有的引用关系。为了解决这个问题,需要采用一种特殊的序列化机制,以确保对象的引用关系得以正确维护。

COM接口的实现

MFC框架中,通常使用CCmdTarget类作为基类,并通过MFC提供的宏来实现COM接口。这种实现方式与MFC框架高度集成,因此是推荐的做法。具体实现细节可以参考MSDN文档。

序列化问题

在序列化对象时,不能简单地使用IPersistStream接口,因为这会导致复杂的序列化算法,这并不是想要的。相反,可以使用MFC提供的序列化机制,因为它能够很好地处理对象的多重引用问题。

引用计数问题

尽管有了COM接口和健壮的序列化机制,但问题依然存在。由于数据结构是通过COM规则访问的,因此对象的生命周期管理主要依赖于正确的引用计数。然而,MFC序列化机制在加载时并不对CCmdTarget派生对象进行引用计数。这意味着,如果对象被多次引用,代码可能会在Release()调用时崩溃。

解决方案

为了解决这个问题,需要修改CArchive的加载算法。一种方法是创建一个CArchiveEx类,继承自CArchive并重载必要的方法。但是,由于CArchive没有虚方法,这种方法并不可行,而且会导致MFC框架的重大修改。

另一种方法是修改DECLARE_SERIAL和IMPLEMENT_SERIAL宏,以实现正确的引用计数。以下是修改后的宏定义:

#define DECLARE_SERIAL_COM(class_name) \ _DECLARE_DYNCREATE(class_name) \ _AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb); \ BOOL _m_bAlreadyLoaded; #define IMPLEMENT_SERIAL_COM(class_name, base_class_name, wSchema) \ CObject* PASCAL class_name::CreateObject() \ { \ class_name *pOb = new class_name; \ pOb->_m_bAlreadyLoaded = FALSE; \ return pOb; \ } \ _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, class_name::CreateObject) \ _AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \ CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \ { \ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \ if (pOb != NULL) \ { \ if (pOb->_m_bAlreadyLoaded) \ pOb->ExternalAddRef(); \ else \ pOb->_m_bAlreadyLoaded = TRUE; \ } \ return ar; \ }

新的operator>>宏确保在对象被多次加载时进行正确的引用计数。注意,这里有一个布尔标志_m_bAlreadyLoaded,它告诉在第一次时不应该增加引用计数。

如何使用这段代码

要使用这段代码,只需将DECLARE_SERIAL和IMPLEMENT_SERIAL宏分别替换为DECLARE_SERIAL_COM和IMPLEMENT_SERIAL_COM即可。类至少需要继承自CCmdTarget。这些宏与CArchive序列化和参数完全兼容,并且与CCmdTarget的CreateObject()创建策略兼容(即引用计数默认设置为1)。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485