views:

495

answers:

3

The "ATL simple object" wizard doesn't provide a way to specify that a new class is derived from an existing coclass and its interface. In Visual Studio 2008, how do I make a new ATL COM class derived from an existing one (i.e. Base implements IBase, and I want to make a new Derived class derived from Base that implements IDerived, where IDerived is derived from IBase.)

Update: it sounds simple, but a wizard-generated ATL class has up to six base classes, a COM map and a connection point map. Which of these base classes and maps should be repeated in the derived class? If maps are repeated in the derived class should they contain the contents of the base class map or just the additional items? Does the order of base classes matter? What about FinalConstruct() and FinalRelease()? Should DECLARE_PROTECT_FINAL_CONSTRUCT and DECLARE_REGISTRY_RESOURCEID be repeated in the derived class?

Here's a sample base class that is empty except for all the boilerplate. Now what should the derived class look like?

class ATL_NO_VTABLE CBase :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<CBase, &CLSID_Base>,
 public ISupportErrorInfo,
 public IConnectionPointContainerImpl<CBase>,
 public CProxy_IBaseEvents<CBase>,
 public IDispatchImpl<IBase, &IID_IBase, &LIBID_ExampleLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
 CBase()
 {
 }

DECLARE_REGISTRY_RESOURCEID(IDR_Base)


BEGIN_COM_MAP(CBase)
 COM_INTERFACE_ENTRY(IBase)
 COM_INTERFACE_ENTRY(IDispatch)
 COM_INTERFACE_ENTRY(ISupportErrorInfo)
 COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(CBase)
 CONNECTION_POINT_ENTRY(__uuidof(_IBaseEvents))
END_CONNECTION_POINT_MAP()
// ISupportsErrorInfo
 STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);


 DECLARE_PROTECT_FINAL_CONSTRUCT()

 HRESULT FinalConstruct()
 {
  return S_OK;
 }

 void FinalRelease()
 {
 }
};

OBJECT_ENTRY_AUTO(__uuidof(Base), CBase)
A: 

Edit the code that the wizards generate. If you want an object to derive from additional interfaces, add these base classes to the resulting class declaration.

John Dibling
A wizard-generated ATL class has up to six base classes, a COM map and a connection point map. Which of these base classes and maps should be repeated in the derived class, and does the order of base classes matter? What about FinalConstruct() and FinalRelease()?
Qwertie
+1  A: 

Well, I received some links on the subject from elsewhere.

http://vcfaq.mvps.org/com/7.htm

http://vcfaq.mvps.org/com/8.htm

Qwertie
It's not an especially easy solution but it's all I could find.
Qwertie
A: 

Just a suggestion - if your COM object does not need to do anything special with COM related stuff then you can implement code such that the real logic that your base COM class does is encapsulated in another plain old C++ class say CBaseLogic.

CBaseLogic : IBase

class ATL_NO_VTABLE CBase :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBase, &CLSID_Base>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CBase>,
    public CProxy_IBaseEvents<CBase>,
    public IDispatchImpl<IBase, &IID_IBase, &LIBID_ExampleLib
{
CBaseLogic m_LogicObj; /* Method calls are simply forwarded to this member */
};


CDerivedLogic : public CBaseLogic

class ATL_NO_VTABLE CDerived :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CDerived, &CLSID_Base>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CDerived>,
    public CProxy_IBaseEvents<CDerived>,
    public IDispatchImpl<IBase, &IID_IBase, &LIBID_ExampleLib
{
CDerivedLogic m_LogicObj;
};

This achieves what you are trying to do with the added advantage of

  1. Keeps your real program logic separate from the infrastructure / packaging (COM)
  2. Makes the real logic platform independent.
  3. Future maintainer need not understand your clever COM hack
  4. Keeps your program logic clean and away from the COM syntax, improving readability
  5. Makes re-use of real logic easier in other forms of packaging eg as a C DLL
Just saw the above posted links from vcfaq - they contain more sophisticated implementations of basically the same approach. I suggest you go over those above and take up the ones which are best suited for your situation
This approach also means you have to write a set of forwarding functions, manually. I ended up using the approach described at http://vcfaq.mvps.org/com/8.htm but I don't like it very much.
Qwertie