Using a BSTR to Transport C++ Objects Across the DCOM Interface

One problem that has frustrated me is the difficulty of building a
large object on the SQL/MTS server and then using DCOM to deliver all
that stuff to the client. The objective was to submit a query to the
server which would then process through the database , build a complex
object, that is an object with nested maps, lists and arrays of other
objects, and return the whole result in usable form to the client.

The semantics of using a SAFEARRAY or a large set of COM properties is
daunting to me. Since I don’t need to make these complex objects
available to VB or Java applications I can keep all the code in C++.

This approach takes advantage of the simple design of a BSTR which, as
I understand it, is just a long pointer. What it points to is the
address between a long int and a block of memory, actually the first
byte of the memory block. The long int preceding the string memory
contains the byte length of the memory block. Usually the memory block
contains a null terminated array of 2 byte characters, but it can
actually contain anything. Since COM/DCOM know how to marshall BSTRs
across the network and process boundaries, anything that can be packed
into a BSTR can be marshalled across.

The first step is to get the arbitrarily complex object packed into the
BSTR. I used a CMemFile to receive the serialized object, then stuff
the CMemFile into the BSTR.

The code looks like this:


BSTR CMyView::FetchObject(long lParam) // pseudo-server function
{
MyComplexClass *p_mCC; // a serializable class
BSTR bstrHoldThis;
CMemFile mfFile; // works just like a real
// file

CArchive ar((CFile*)&mfFile, CArchive::store);

// Create the complex object. I include this stub here to indicate
// that what is being passed is really an an object and not a pointer.
// It could be a megabyte or more in size.

p_mCC = new MyComplexObject(lParam);

// A small, simple object could have been created on the stack.That’s
// not best place for a REALLY big object, although it
// guarantees that it will go away when the function returns.
// I generally put objects on the heap with ‘new’ and
// delete them after serialization.

// Serialize the object to the memory file
p_mCC->Serialize(ar);

// You need to flush the archive and file buffers
ar.Flush(); // flush the archive first
mfFile.Flush(); // then the file

// The step that creates the BSTR needs the length of the file

ULONG lArchiveLength = mfFile.GetLength();

// Stuff the archive into the BSTR with SysAllocStringByteLen()
// CMemFile.Detach() returns a pointer to the memory block of the file
// which is used to create the BSTR.

bstrHoldThis = SysAllocStringByteLen((const char*)mfFile.Detach(),
lArchiveLength);

// Free the object. The CMemFile will clean up after itself.

delete p_mCC;

// Return the BSTR result;

return bstrHoldThis; // send the whole thing into the aether
}

Now the caller needs to be able to unpack the BSTR and re-create the
object. This is just the reverse of the steps above and I’d leave it as
an exercise for the reader but I actually got it to work so here it is.


Void CMyView::OnButton1()
{
BSTR bstrA;
MyComplexClass mRC;
CMemFile mfFile;

// error checking omitted for clarity – but necessary

// go get the BSTR wrapped object from the pseudo-server
BstrA = FetchObject(m_lTestVal);

// here’s the ugly part. I back into the length of the BSTR.

ULONG *p_lLength = (ULONG*) bstrA; // points at the byte AFTER the
// BSTR length

–p_lLength; // now it points at the length of the
// BSTR. Like Foster’s – crude but
// effective.

// attach the memory part of the BSTR to the CMemFile memory block

mfFile.Attach((unsigned char*) bstrA, *p_lLength);

// now the object is in the memfile and it can be loaded
CArchive ar(&mfFile, CArchive::load);

mRC.Serialize(ar);

// The object is now fully instantiated on the stack.
// In this sample code it would be destroyed when this
// function returned so you’d really want to build it with ‘new’
// on the heap and assign it to a member variable, or a
// Document data member.

// We still need to free the memory used by the BSTR and the
// CMemFile. They both point to the same memory. The CMemFile
// won’t automatically free the memory that was attached when it
// is destroyed, so freeing the BSTR memory takes care of that for us.

SysFreeString(bstrA);
}

One could combine this method with others on a more general COM
interface and agree to limit its use to MFC C++ clients (more
non-portable ugliness). The challenge is to get all the serialization
parts right for the array, maps, lists etc.

More by Author

Must Read