Implementing Marshal-by-Value using ATL
July 18, 2001Not 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 ATL’s IPersistStreamInitImpl to build GetSizeMax. The code is not pretty. It’s just pieced together from ATL’s 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 };