Writing and Reading COM objects using CArchive | CodeGuru

Writing and Reading COM objects using CArchive

Imagine you have to work with your custom COM objects in a MFC application. These COM objects are stored in a container (client), which is a GUI intensive MFC application with quite sophisticated class hierarchy. In many C++ classes in the client, I have a situation where a classic C++ class possesses one or more […]

Written By
CodeGuru Staff
CodeGuru Staff
May 21, 1999
3 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

Imagine you have to work with your custom COM objects in a MFC application.
These COM objects are stored in a container (client), which is a GUI intensive
MFC application with quite sophisticated class hierarchy.
In many C++ classes in the client, I have a situation where a classic C++ class
possesses one or more COM objects. Additionally, these classes implement
standard Serialize() method to store their contents to the CArchive stream.
Below is a pseudo code of typical client C++ class:

class Dummy
{
   void Serialize(CArchive& ar);
private:
   double m_Double;
   IPersistStreamInit* m_ipCom1;        // raw interface pointer
   CComPtr&ltIPersistStreamInit&gt m_spCom2;// smart interface pointer defined in AtlBase.h
   IPersistStreamInitPtr m_spCom3;      // smart interface pointer based on _com_ptr_t
                                        // created by #import directive
};

The question here is: Is there a way to serialize COM objects within the


standard Serialize() method? To get the answer, I turned a few


ATL/MFC books upside-down, do some Internet browsing and

got zero hits.


This forced me to find my own solution. There is a chance that a


solution (better than presented below) exists, but I simply could not find it.

In order to store a COM class on the CArchive (using the solution below),
the COM class MUST implement the IPersistStreamInit interface. This
interface knows how to read/write via IStream interface, which is passed
as an argument of the IPersistStreamInit::Save/Load methods.

If you refer to the documentation about IStream you will find the
CreateStreamOnHGlobal() API function call that creates an IStream object on
the top of HGLOBAL memory block. I can take advantage of this and store
the initialized memory block into the CArchive. Below is a pseudo code
that does just that:

SaveComToArchive(CArchive& ar, IPersistStreamInit* pObject)
{
   // Note: This is a pseudo code – it will not compile!
   // Create IStream
   IStream* pStream = CreateStreamOnHGlobal();
   // Write COM object’s class UUID!
   pStream->WriteObjectClassUUID(pObject->GetClassUUID());
   // save the object
   pObject->Save(pStream);
   // Get the memory block maintained by the IStream
   HGLOBAL hMem;
   hMem = pStream->GetGlobalMemory();
   // Copy the block on the CArchive
   ar.Write(hMem,size_of_hMem_block);
   // Release the IStream
   pStream->Release;
}

The pseudo code for loading a COM object from the CArchive is very similar.
It turns out that a code like this works just fine, and I believe,
It should work for all COM objects that
properly implement IPersistStreamInit interface.
Since global memory is used as a buffer, it is obvious that we will
use this solution for COM that stores data of small and medium size.

Below you will find a link to the implemented code. There you will find two
functions and two operators, that have the following prototype:

HRESULT WriteComObjectToArchive(CArchive& ar, IPersistStreamInit* ipObject);
HRESULT ReadComObjectFromArchive(CArchive& ar, IPersistStreamInit** ippObject);
CArchive& operator << (CArchive& ar, IPersistStreamInit* ipObject);
CArchive& operator >> (CArchive& ar, IPersistStreamInit** ippObject);

Note: I deliberatelly put IPersistStreamInit* in each call rather than IUnknown*.


Namely, I wanted to emphasize that COM object must implement the


IPersistStreamInit interface.

Let me show how to use these functions. If you look at the implementation of the
ReadComObjectFromArchive() (please, see the source code), you will see,
that there are two options concerned with ippObject argument:


  • (a) If *ippObject==NULL, create a new COM object and load data for it,
  • (b) If *ippObject!=NULL load the data into existing COM object.

Both options are presented in sample call bellow. In most cases you will


be interested in the (a) case only.

void Dummy::Serialize(CArchive& ar)
{
   HRESULT hr;
   if(ar.IsStoring()) {
      ar << m_Double;
      hr = WriteComObjectToArchive(ar,m_ipCom1); // raw pointer
      // if(FAILED(hr)) ... check hr here to catch errors
      hr = WriteComObjectToArchive(ar,m_spCom2); // CComPtr<> pointer
      hr = WriteComObjectToArchive(ar,m_ipCom3); // _com_ptr_t<> pointer
   }
   // loading
   else {
      ar >> m_Double;
      // case a) – create new COM objects during the read
      m_ipCom1 = NULL; // it must be NULL in order to create a new object!
      hr = ReadComObjectFromArchive(ar,&m_ipCom1);
      // if the call is successful, the m_ipCom1 points to a new COM object,
      // with reference count set to 1!
      // The &operator of CComPtr<> will reset the pointer to NULL for us and
      // release any previous object.
      hr = ReadComObjectFromArchive(ar,&m_spCom2);
      // The &operator of _com_ptr_t<> will reset the pointer to NULL for us and
      // release any previous object.
      hr = ReadComObjectFromArchive(ar,&m_spCom3);
      // if the calls were successful, the m_spCom2 and m_spCom3 point to
      // a new COM objects, both with reference count set to 1!
      // case b) – use existing COM objects and simply load the data!
      // m_ipCom1 must point to IPersistStreamInit of a valid COM object.
      hr = ReadComObjectFromArchive(ar,&m_ipCom1);
      // if the call is successful, the m_ipCom1 was loaded with data from stream
      // and the reference count remained the same.
      IPersistStreamInit* pPSI;
      // The &operator of CComPtr<> will reset the pointer to NULL for us, so
      // we can’t use it in the case b).
      pPSI = m_spCom2;  // The pointer inside the m_spCom2 must be non NULL and valid.
      hr = ReadComObjectFromArchive(ar,&pPSI);
      pPSI = m_spCom3;  // The pointer inside the m_spCom3 must be non NULL and valid.
      hr = ReadComObjectFromArchive(ar,&pPSI);
      // if the calls were successful, the m_spCom2 and m_spCom3 now hold new data
   }
}

Now, I show you the same function as above, but using the operators and
omitting most comments. In this version you do not get the HRESULT.
Operators will throw _com_error exception in the case of error.

void Dummy::Serialize(CArchive& ar)
{
   if(ar.IsStoring()) {
      ar << m_Double;
      ar << m_ipCom1; // raw pointer
      ar << m_spCom2; // CComPtr<> pointer
      ar << m_spCom3; // _com_ptr_t<> pointer
   }
   // loading
   else {
      ar >> m_Double;
      // case a) – create new COM objects during the read
      m_ipCom1 = NULL; // it must be NULL in order to create a new object!
      ar >> &m_ipCom1;
      ar >> &m_spCom2;
      ar >> &m_spCom3;
      // case b) – use existing COM objects and simply load the data!
      ar >> &m_ipCom1;
      IPersistStreamInit* pPSI;
      pPSI = m_spCom2;
      ar >> &pPSI;
      pPSI = m_spCom3;
      ar >> &pPSI;
   }
}

Download source – 4 KB
The source code includes two short files ArchiveCom.h(.cpp). Add these files to your
MFC project and add the #include “ArchiveCom.h” line in appropriate source
implementation (CPP) files.

Download demo project – 29 KB
The demo project creates a simple COM named Dummy as an EXE server. This COM
implements IPersistentStreamInit interface and a few properties.
The demo also creates a client named Test, which has a simple CTestDummy C++ class
that uses the several Dummy COM objects and implements Serialize() method.
In the main() of the demo a CArchive is created,
the CTestDummy class object is initialized and written to the archive.
A second (empty) CTestDummy object is created is then loaded from the CArchive stream.

To see the implementation of the CTestDummy class, open the Test.cpp file.

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.