Passing C++ Classes Across DCOM

Sample Image

Environment: VC++ 6.0, NT 4.0,Win2000, Win95/98

Using COM technology to pass simple data like long, int, etc is easy, but what about structured data like C++ classes? Most developers knows the way to pass that. The way is generally 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 more suitable library that could easily be used one in my projects. For this reason, I built two classes that provide richer interfaces and easier semantics.

The first class is called CDComObj. This class is responsible for reading and writing data into and from VARIANT. The second class, CDcomObjArray, is responsible for passing a collection of objects across DCOM. By using both of these classes it is easy to implement any C++ class with the 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 that 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<IUNKNOWN>& 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 the 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 a CString object from WTL V.3.1. You should download one from the Microsoft site. In any case don't forget to set up a path to this library in your Compiler: Tools/Options/Directories.

For easy manipulate with SafeArray, I am using the CComSafeArray class from www.sellsbrothers.com

The Demo project demonstrates the following:

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

How you can 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 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 instance and list of connected users will grow

What's going on?

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

Downloads



Comments

  • Passing structure to event in ActvieX control

    Posted by rajeshkapure on 08/29/2005 09:03am

    I have created ActvieX control having different event. now i want to pass use define structure to evnt . when we add new event it show other datatype. how can i do this. Regards Rajesh

    Reply
  • I think what DCOM do is to provide a new data type "remotable interface"

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: hua yuan

    to provide a program model to avoid what this kind of object passing.

    To implement this "remotable interface" (marshaling) DCOM does a lot of infrastructure code to support it.

    This object passing (if not MBV) seem is against the Dcom's idea.

    Reply
  • A classical example of how NOT to use COM

    Posted by Legacy on 11/15/2001 12:00am

    Originally posted by: Carsten Breum

    I have seen many different attempts to pass C++ object via
    
    DCOM and this one is not new. All of them are more or less
    hopeless I think. You have to go through the tedious work
    of mapping your data into a stream which in this case is a
    safearray. In some cases it is almost impossible to map the
    object because it has links in form of pointers to other
    objects and then you have to decide what to do with them.
    If these objects are linked to other objects you are really
    having problems. Even if you find a solution to this
    there's a great risk of spending a lot of time debugging
    because you did not make the mapping the right way!

    You should of course pass interface pointers to your
    objects instead. This means that you should implement your
    C++ objects as COM objects. By using ATL this is simple and
    the way you should follow if you want to pass data from
    one machine to another.

    If you have to transfer data via the internet you should
    consider writing web services in C# instead. This is even
    more simple than using ATL.


    Reply
  • What's the point...

    Posted by Legacy on 11/07/2001 12:00am

    Originally posted by: 2-Smart....

    Why not just wrapper the classes you want to pass via DCOM w/ IDL and make them COM classes so that you can pass them as an IUnknown as you would w/ any other COM object? dUh.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • You may already know about some of the benefits of Bluemix, IBM's open platform for developing and deploying mobile and web applications. Check out this webcast that focuses on building an Android application using the MobileData service, with a walk-through of the real process and workflow used to build and link the MobileData service within your application. Join IBM's subject matter experts as they show you the way to build a base application that will jumpstart you into building your own more complex app …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds