July 18, 2001 tools

Implementing Marshal-by-Value using ATL

July 18, 2001

Not too long ago, Jonathan Borden, jborden@MEDIAONE.NET, posted a nifty class called IMarshalByValueImpl that implemented IMarshal for objects interested in being marshaled by value . It was built in the ATL style, i.e. it used fun template tricks, and depended on the COM class also implementing either IPersistStream or IPersistStreamImpl. And, in fact, ATL provides an implementation of IPersistStreamInit called IPersistStreamInitImpl. However, IPersistStreamInitImpl has one fatal flaw: it implements GetSizeMax by returning E_NOTIMPL. This breaks Jonathan’s IMarshalByValueImpl, which depends on a sane implementation of GetSizeMax from the class.

IPersistStreamInitImpl2 leverages the property map of ATLs IPersistStreamInitImpl to build GetSizeMax. The code is not pretty. It’s just pieced together from ATLs implementation of IPersistStreamInit::Save and CComVariant::WriteToStream, but it seems to work (thanks to Dharma Shukla, v-dharsh@microsoft.com, for testing it!).

Here’s the usage for giving a class MBV:

class PassByValue :
  public CComObjectRootEx<CComMultiThreadModel>,
  public CComCoClass<PassByValue, &CLSID_PassByValue>,
  public IDispatchImpl<IPassByValue, &IID_IPassByValue>,
  public IMarshalByValueImpl<PassByValue>,
  public IPersistStreamInitImpl2<PassByValue>
{
public:
BEGIN_COM_MAP(PassByValue)
  COM_INTERFACE_ENTRY(IDispatch)
  COM_INTERFACE_ENTRY(IPassByValue)
  COM_INTERFACE_ENTRY(IMarshal)
  // Don't need to expose these to the world
  //COM_INTERFACE_ENTRY(IPersist)
  //COM_INTERFACE_ENTRY  
  (IPersistStreamInit)
END_COM_MAP()

BEGIN_PROP_MAP(PassByValue)
  // List of properties to marshal-by-value
  ...
END_PROP_MAP()

  // IPassByValue methods
  ...

private:
  // Whatever data members you wish to marshal
  ...

public:
  BOOL m_bRequiresSave; // Required by IPersistStreamInitImpl2
};