Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Serialization of COM objects using MFC

0.00/5 (No votes)
17 Jan 2000 1  
A neat way to use MFCs built in serialization to store COM objects
  • Download source files - 9 Kb
  • Introduction

    Some months ago, I had to create objects which constitute the data structure of a MFC MDI document. This structure is fairly complex, meaning the objects in the structure are referenced more than one time within the structure. The objects are seen from the plug-ins of the application as COM objects; plug-ins manipulate COM objects through the COM interface of the document only. The objects also have to be serialized into the document file.

    COM implementation

    The retained solution was to implement them with the MFC (with a derivation from CCmdTarget and then using the macros supplied with the MFC) as the framework was done with MFC and this mechanism is well integrated. See the MSDN for more information on how to provide COM implementation with MFC.

    Serialization

    The serialization of the object does not use IPersistStream as you might expect because of the multiple references inside the data structure; this would lead to the development of a serialization algorithm which is not desirable.

    In fact, I simply use the serialization provided with the MFC. Why? well, the structure of the data involves multiple references of objects within the data structure and the MFC provides a neat way to serialize such structure - and more, since the objects are implemented with MFC then the serialization is provided for free (as the object inherits from CObject).

    The problem

    Well, we have COM interfaces, we have a robust serialization, so where is the problem?

    In fact, the data structure is accessed using COM rules and so relies on a correct reference counting mainly for the life time management of the objects. The problem is that the MFC serialization does not reference count CCmdTarget derived objects when loading (CCmdTarget initialized the ref count to 1 which is not correct in case of multiple references). So your code will break if the object is referenced more than one time (this will appear when Release()'ing COM objects) !

    A solution

    Obvously, we have to modify the loading algorithm of CArchive, but how?

    The first idea is to create a CArchiveEx classes which inherits from CArchive and then overloading the necessary methods but as there is no virtual method, it is not possible and anyway, it would lead to important modification to the MFC framework (you will have to modify CDocument::OnOpenDocument() in order to use the new class).

    So, are we stuck? no!

    In fact, if you look at the DECLARE_SERIAL and IMPLEMENT_SERIAL macros, definition you will notice that these macros define the operator >> in order to load an object from a CArchive stream. That's it ! Just modify the operator >> and do the correct reference counting.

    So here are macros replacement for IMPLEMENT_SERIAL and DECLARE_SERIAL (the bold lines indicates the difference from the original one) :

    #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; }
    
    The new operator >> takes care of doing the correct reference counting when the object is loaded more than one time. Notice that there is a boolean flag (_m_bAlreadyLoaded) which tells that we should not increment the ref count the first time. You might say that I could have set the ref count to zero (0) just after allocating a new object and just ExternalAddRef() inside the operator >> but it would change the allocation policy for CreateObject() (it creates by default an object with a ref count of one) so I think this way is better for compatibility.

    The overhead of these macros is that it adds the BOOL variable _m_bAlreadyLoaded to your class.

    How to use this code

    Just replace the macros DECLARE_SERIAL and IMPLEMENT_SERIAL respectively with DECLARE_SERIAL_COM and IMPLEMENT_SERIAL_COM and it should work (your class must inherit from CCmdTarget at least !).

    The provided macros are fully compatible both with CArchive serialization and in terms of parameters.
    The macros are also compatible with the creation policy of CreateObject() of CCmdTarget (I mean the ref count is set to one (1) as expected).

    About the code

    The sources given only test the correctness of the solution with a small data set provided by a dummy class which implements a COM interface.

    The test just creates some date in memory and do some serialization on it; it also dumps the objects of the data structure to show that the reference count is correct.

    Look at the source comments for more details on the way the test is done.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here