Passing C++ classes across DCOM






4.64/5 (9 votes)
Nov 16, 2001
2 min read

218629

2494
Two classes that provide richer interfaces and easier semantics to pass classes via COM/DCOM
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?
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?
- Register Server by starting StreamingServer.exe with option 'RegServer' : StreamingServer.exe -RegServer.
- Start first instance of Client: StreamingClient.exe
- Connect to the server as some user
- Start another one instance of Client
- Connect to server with different user name
- After establishing connection you can see list of connected users in any Client
- Start another one instance again and list of connected users will grow
What's going on?
- Under connection stage the client is sending to the server a C++ class
CConnection
. - If the server accepted this request it sends a collection of
CConnection
classes to everything connected clients.