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

Passing C++ classes across DCOM

0.00/5 (No votes)
15 Nov 2001 1  
Two classes that provide richer interfaces and easier semantics to pass classes via COM/DCOM

Sample Image

Introduction

Using COM technology to pass simple data like long, int, etc is easy. And what about structured data like C++ classes?

The most developers know the way to pass that. This way is based on passing data using VARIANT as SAFEARRAY. And what does VARIANT mean?

The VARIANT is perhaps the ultimate in general purpose functionality for passing data. But its range of low-level features can be daunting. I did not find a suitable library for the easy use of one in my projects.

To resolve this problem I built two classes to provide richer interfaces and easier semantics. The first class called CDComObj. This class is responsible for Reading/Writing data into/from VARIANT. The second class, CDcomObjArray, is responsible for passing collection of objects across DCOM. By using both of these classes it is easy to implement any C++ class which has ability to pass itself across DCOM.

An Example Using the Objects

Client part:

bool CServerAccessPoint::ServerConnect(CConnection& connObj)
{
   CComSafeArray conn;
   connObj.Write(conn); // Writing C++ class to VARIANT


   HRESULT hr = m_pDcomServer->Connect(&conn);
   if (FAILED(hr))
   {
     // DO something...

     return false;
   }
   return true;
}

Server part:

STDMETHODIMP CStreamingSrv::Connect(VARIANT *pConnection)
{
  Lock();
  CConnection conn;
  conn.Read(pConnection); // Reading C++ class from VARIANT


  m_connections.AddConnection(conn);
  Unlock();
  return S_OK;
}

Is not easy?

Classes

class CDcomObj  : public CSLObject
{
public:
  CDcomObj();
  virtual ~CDcomObj();

  virtual void Clear();
  virtual void Copy(const CSLObject& objectSrc);
          void Copy(const CDcomObj& objectSrc);
  virtual bool IsEqual(const CSLObject& objectSrc);

  virtual HRESULT WriteToStream(IStream* pStream);
  virtual HRESULT ReadFromStream(IStream* pStream);

  virtual long ElementSize();
          void Write(VARIANT* pSafeArray);
          void Write(CComSafeArray* safeArray);
  virtual void Write(CComSafeArray* safeArray,long& index);

          void Read(const CLSID& clsid);
          void Read(VARIANT* pSafeArray);
          void Read(CComSafeArray* safeArray);
  virtual void Read(CComSafeArray* safeArray,long& index);
          void ReadValue(const CComVariant& srcValue,CComVariant& destValue);
          void ReadValue(const CComVariant& srcValue,CComPtr& destValue);
          void ReadValue(const CComVariant& srcValue,IUnknown** destValue);
          void ReadValue(const CComVariant& srcValue,CString& destValue);
          void ReadValue(const CComVariant& srcValue,CComBSTR& destValue);
          void ReadValue(const CComVariant& srcValue,LONG& destValue);
          void ReadValue(const CComVariant& srcValue,CY& destValue);
          void ReadValue(const CComVariant& srcValue,bool& destValue);
          void ReadValue(const CComVariant& srcValue,UINT& destValue);
          void ReadValue(const CComVariant& srcValue,DWORD& destValue);
          void ReadValue(const CComVariant& srcValue,int& destValue);
          void ReadValue(const CComVariant& srcValue,double& destValue);
          void ReadValue(const CComVariant& srcValue,DATETIME_STRUCT& destValue);

          void ProgIDFromCLSID(const CLSID& clsid,CComBSTR& comBSTR);

          const	CLSID	 GetCLSID();
          const	CComBSTR GetLastError();
          const	bool IsModified() const;
          const	bool IsModified(UINT value) const;
          const	UINT GetModified() const;
                void AddModified(UINT uModified);
                void SetModified(UINT uModified);
                void RemoveModified(UINT uModified);
                void ShowError(CString lpszError);

public:
    
    CComBSTR	m_strCLSID;
    CComBSTR	m_strProgID;
    CComBSTR	m_strObjectName;
    
protected:
    
    // Specifics

    UINT      m_uModified;
    CComBSTR  m_bstrError;
};

Using the classes, step by step

The implementation of own DCOM class is very easy.

Step 1: Create your own class derived from CDComObj.

 class CConnection  : public CDcomObj

Step 2: Redefine the following virtual member functions :

// Shows how many elements contains your class

virtual long ElementSize()
// Writing class data to VARIANT (SAFEARRAY)

virtual void Write(CComSafeArray* safeArray,long& index);
// Reading class data from VARIANT (SAFEARRAY)

virtual void Read(CComSafeArray* safeArray,long& index);
// Copying data

virtual void Copy(const CSLObject& objectSrc);
// Clear data

virtual void Clear();

The DComObj contains the set of macros. So, redefinitions of mentioned functions is easy

An Example

To demonstrate this technique I built two classes: CConnection and CConnectionArray

class CConnection  : public CDcomObj
{
  DCL_DCOMOBJ(CConnection)
public:
  CConnection();
  virtual ~CConnection();

  virtual void Clear();
  virtual void Copy(const CSLObject& objectSrc);
  virtual bool IsEqual(const CSLObject& objectSrc);
  virtual long ElementSize();
  virtual void Write(CComSafeArray* safeArray,long& index);
  virtual void Read(CComSafeArray* safeArray,long& index);

protected:

  CString   m_strComputerName;
  CString   m_strApplicationName;
  CString   m_strUserName;
  CString   m_strPassword;
  CString   m_strServerName;
  CComBSTR  m_strConnectionHandle;
};

Here is the implementation of ElementSize() member function

long CConnection::ElementSize()			
{ 
  return 6 + CDcomObj::ElementSize();	
}

The implementation of Write and Read member function you can find here.

void CConnection::Write(CComSafeArray* safeArray,long& index)
{
  SA_BEGIN_WRITE(CDcomObj);
  SA_WRITE(m_strComputerName);
  SA_WRITE(m_strUserName);
  SA_WRITE(m_strPassword);
  SA_WRITE(m_strServerName);
  SA_WRITE(m_strApplicationName);
  SA_WRITE(m_strConnectionHandle);
}


void CConnection::Read(CComSafeArray* safeArray,long& index)
{
  SA_BEGIN_READ(CDcomObj);
  SA_READ(m_strComputerName);
  SA_READ(m_strUserName);
  SA_READ(m_strPassword);
  SA_READ(m_strServerName);
  SA_READ(m_strApplicationName);
  SA_READ(m_strConnectionHandle);
}

For streaming the collection of CConnection objects across DCOM it is enough to define class like here.

class CConnectionArray  : public CDcomObjArray
{
  DCL_DCOMOBJ_ARRAY(CConnectionArray,CConnection)
public:
  CConnectionArray(){}
  CConnectionArray( ccIndex aLimit, ccIndex aDelta , bool shouldDelete = true):
  CDcomObjArray(aLimit,aDelta ,shouldDelete )
  {
  }
  virtual ~CConnectionArray(){}

};

Notes

To avoid MFC at Server side I am using CString object from WTL V.3.1. So, you should download one from Microsoft site. In any case don't forget to setup path to this library in your Compiler: Tools/Options/Directories.

For easy manipulation of SafeArray's, I am using the CComSafeArray class from www.sellsbrothers.com

The Demo project is demonstrated as follows:

  • Passing C++ class / collection of classes across COM/DCOM.
  • Connection Point Technique

How can you see that?

  1. Register Server by starting StreamingServer.exe with option 'RegServer' : StreamingServer.exe -RegServer.
  2. Start first instance of Client: StreamingClient.exe
  3. Connect to the server as some user
  4. Start another one instance of Client
  5. Connect to server with different user name
  6. After establishing connection you can see list of connected users in any Client
  7. Start another one instance again and list of connected users will grow

What's going on?

  1. Under connection stage the client is sending to the server a C++ class CConnection.
  2. If the server accepted this request it sends a collection of CConnection classes to everything connected clients.

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