Passing C++ Object in ATL DLL | CodeGuru

Passing C++ Object in ATL DLL

Environment: ATL, COM, MFC Introduction A few weeks ago, I was desperately looking for a sample in which a C++ object can be sent across a COM interface but could not get any sample for the same. That’s why I decided to post this article here. Passing a C++ object across an ATL DLL is […]

Written By
CodeGuru Staff
CodeGuru Staff
Jun 27, 2002
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

Environment: ATL, COM, MFC

Introduction

A few weeks ago, I was desperately looking for a sample in which a C++ object can be sent across a COM interface but could not get any sample for the same. That’s why I decided to post this article here.

Passing a C++ object across an ATL DLL is not difficult, but, of course, it is a bit tricky and interesting too.

Before starting the project, make sure that your client and server are both be C++-compliant. Secondly, you must be aware of setting your COM client and server.

Limitations with the Interface

COM demands a high degree of separation between the client and server; this is done through interfaces but the problem is that interfaces offer only a limited number of data types in methods. If the interface is IDispatch-based, the choices are even more limited. Keeping these limitations in mind, C++ objects only can be passed across the interface under these situations:

  1. Both Client and Server are written in VC++.
  2. They must be able to share object definitions (such as header files).
  3. Passing objects simplifies the application design.
  4. Your application may need to run in a distributed environment. You want COM’s capabilities of remote activation, local/remote transparency, and security.

I would recommend that, before starting your work, you should also refresh the Serialization topic first.

Now, let’s continue with the sample and do the following:

  1. Create an ATL DLL server.
  2. Add MFC class derived from CObject.
  3. Use DECLARE_SERIAL macro in class header.
  4. Use IMPLEMENT_SERIAL macro in class body.
  5. Override the Serialize() method.
    // Your CSimpleObj class looks like this:
    class CSimpleObj : public CObject
    {
      DECLARE_SERIAL( CSimpleObj )
    public:
      // constructor and destructor
      CSimpleObj();
      virtual ~CSimpleObj();
      // Set internal string data
      void SetString( CString csData );
      // Used to serialize data into an archive
      virtual void Serialize(CArchive& ar);
      // Display the data string
      void Show();
    private:
      CString m_strData;// Internal data string
    };
    // Write this object to an archive
    void CSimpleObj::Serialize(CArchive& ar)
    {
      CObject::Serialize( ar );
      if (ar.IsLoading())
      {
      // extract data from archive
        ar
    >> m_strData;
      }
      else
      {
        // store data into archive
        ar << m_strData;
      }
    }
    // Method to display data in this object
    void CSimpleObj::Show()
    {
      AfxMessageBox(m_strData);
    }
    // save a string in data member
    void CSimpleObj::SetString(CString csData)
    {
      m_strData = csData;
    }
    
  6. Now, the next step is Serializing and De-Serializing (Loading and Storing Objects) with a CArchive. I’m using a different class called CBlob for it.
    class CBlob
    {
    public:
      CBlob() {};
      virtual ~CBlob() {};
      // Extract data from a CObject and load it into a SAFEARRAY.
      SAFEARRAY* Load( CObject *pObj );
      // Re-create an object from a SAFEARRAY
      BOOL Expand( CObject * &pObj, SAFEARRAY *pVar );
    private:
    };
    // Extract data from a CObject and use it to create a SAFEARRAY.
    SAFEARRAY* CBlob::Load( CObject *pObj)
    {
      CMemFile memfile;      // memory file
      // define the flag that tells the archive whether it should
      // load or store
      long lMode = CArchive::store | CArchive::bNoFlushOnDelete;
      // create the archive using the memory file
      CArchive ar(&memfile, lMode );
      // m_pDocument is not used
      ar.m_pDocument = NULL;
      // serialize the object into the archive
      ar.WriteObject(pObj);
      // close the archive — the data is now stored in memfile
      ar.Close();
      // get the length (in bytes) of the memory file
      long llen = memfile.GetLength();
      // detach the buffer and close the file
      unsigned char *pMemData = memfile.Detach();
      // set up safearray
      SAFEARRAY *psa;
      // create a safe array to store the stream data
      psa = SafeArrayCreateVector( VT_UI1, 0, llen );
      // pointers to byte arrays
      unsigned char *pData = NULL;
      // get a pointer to the safe array. Locks the array.
      SafeArrayAccessData( psa, (void**)&pData );
      // copy the memory file into the safearray
      memcpy( pData, pMemData, llen );
      // clean up buffer
      delete pMemData;
      // unlock access to safearray
      SafeArrayUnaccessData(psa);
      // return a pointer to a SAFEARRAY allocated here
      return psa;
    }
    // Re-create an object from a SAFEARRAY
    BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
    {
      CMemFile memfile;          // memory file for de-serailze
      long lLength;              // number of bytes
      char *pBuffer;             // buffer pointer
      // lock access to array data
      SafeArrayAccessData( psa, (void**)&pBuffer );
      // get number of elements in array. This is the number of bytes
      lLength = psa->rgsabound->cElements;
      // attach the buffer to the memory file
      memfile.Attach((unsigned char*)pBuffer, lLength);
      // start at beginning of buffer
      memfile.SeekToBegin();
      // create an archive with the attached memory file
      CArchive ar(&memfile, CArchive::load | CArchive::bNoFlushOnDelete);
      // document pointer is not used
      ar.m_pDocument = NULL;
      // inflate the object and get the pointer
      rpObj = ar.ReadObject(0);
      // close the archive
      ar.Close();
      // Note: pBuffer is freed when the SAFEARRAY is destroyed
      // Detach the buffer and close the file
      pBuffer = (char*) memfile.Detach();
      // release the safearray buffer
      SafeArrayUnaccessData( psa );
      return TRUE;
    }
    

I’m using SAFEARRAY here because this is the best selection for our use. It can contain some complex multidimensional arrays, but for this example we are only using a very simple array. There’s one problem with SAFEARRAY data: MIDL doesn’t recognize this data type, but the easiest way is to use the VARIANT type that I will discuss in next article.

The next steps are as follows:

  1. Create a COM interface.
  2. Create a SAFEARRAY object.
  3. Define [helpstring(“method SetArray”)] HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring(“method GetArray”)] HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData); in an IDL file.
  4. Make a MFC-based client to test the application.

Your IDL file should look like this:

  interface IBolbData : IUnknown
  {
    [helpstring(“method SetArray”)] HRESULT SetArray([in]SAFEARRAY
      (unsigned char) pData);
    [helpstring(“method GetArray”)] HRESULT GetArray([out/*,retval*/]
      SAFEARRAY(unsigned char) *pData);
  };
// Sets object.
STDMETHODIMP CBolbData::SetArray(SAFEARRAY *pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())
  // create a dummy pointer of CSimpleObj
  CSimpleObj *dummy=NULL;
  // create blob obect to expand/deserialize
  CBlob blob;
  // Init dummy object using safe array through this function
  blob.Expand( (CObject*&)dummy, pData );
  dummy->Show();  // Call show function to test the object.
  delete dummy;      // Delete the pointer.
  return S_OK;
}
// Creates Object and sends to client.
STDMETHODIMP CBolbData::GetArray(SAFEARRAY **pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())
  // create object to send to server
  CSimpleObj *pMyOb = new CSimpleObj();
  // set the string data
  pMyOb->SetString( “A SAFEARRAY from the server!” );
  // create blob to serialize object
  CBlob blob;
  // load the object into the blob
  *pData = blob.Load( pMyOb );
  // delete the pMyOb pointer
  delete pMyOb;
  return S_OK;
}

And, finally, write a dialog-based MFC application having two buttons and add the following code:

void CClientDlg::OnOK()
{
// create COM smart pointer from CLSID string
  try
  {
    IBolbDataPtr pI( “Server.BolbData.1” );
    SAFEARRAY *psa ;
    // Get the safearray from the server
    pI->GetArray( &psa );
    // create a pointer to an object
    CSimpleObj *dummy=NULL;
    // blob object to expand
    CBlob blob;
    // use the blob to expand the safearray into an object
    blob.Expand( (CObject *&)dummy, psa );
    // call a method on the object to test it
    dummy->Show();
    // delete the object
    delete dummy;
  }
  // Handle any COM exceptions from smart pointers
  catch (_com_error e)
  {
    // display the message string from the error
    AfxMessageBox( e.ErrorMessage() );
  }
}
void CClientDlg::OnLoad()
{
  try
  {
    // create smart pointer from CLSID string
    IBolbDataPtr pI( “Server.BolbData.1” );
    SAFEARRAY *psa ;
    // create object to send to server
    CSimpleObj *pMyOb = new CSimpleObj();
    // set the string data
    pMyOb->SetString( “The client sent a SAFEARRAY!” );
    // create blob to serialize object
    CBlob blob;
    // load the object into the blob
    psa = blob.Load( pMyOb );
    // delete the object
    delete pMyOb;
    pI->SetArray( psa );
  }
  // Handle any COM exceptions from smart pointers
  catch (_com_error e)
  {
    // display the message string from the error
    AfxMessageBox( e.ErrorMessage() );
  }
}
Advertisement

Conclusion

This article covers a number of topics; for example, how to use Serialization, how to use SAFEARRAY, and how to pass a C++ object across the interface. I also would like to say thanks to William Rubin, whose article helped me quite a lot. I was planning to explain this topic in a bit more detail but due to shortage of time I couldn’t do so but will keep updating the document from time to time. In the meantime, please feel free to contact me.

Downloads

Download source files – 116 Kb

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.