ATL Dispatch Sample

This sample demonstrates the implementatation of three kinds of dispatch-interfaces: duals, delegating dispinterfaces and raw dispinterfaces. Duals can be implemented using IDispatchImpl, but neither delegating nor raw dispinterfaces are supported by ATL as of version 3.0.

Because there are three ways of defining a dispatch-based interface, this sample also provides dispimpl2.h, a header file that provides three C++ classes, one for each method of defining a dispatch interface.

Dual Interfaces

A dual dispatch-based interface is defined like so:
[dual]
interface IFoo : IDispatch
{
    HRESULT DoFoo();
}
Implementing the IDispatch portion of a dual interface is usually managed using a typeinfo object. Given a typeinfo, all four methods of IDispatch are one liners, mostly letter the typeinfo object do the work.

ATL's IDispatchImpl requires dispatch-based interfaces to be defined as dual interfaces.

Delegating dispinterfaces

A delegating dispatch-based interface is defined like so:
[oleautomation]
interface IFoo : IUnknown
{
    HRESULT DoFoo();
}

dispinterface DFoo
{
    interface IFoo; // Logically delegate to another interface
}
Implementing DFoo is a matter of loading a typeinfo and having it forward to methods of IFoo (as this definition promises). Again, given a typeinfo object, the implementation of this kind of dispatch-based interface is easy.

Unfortunately, the implementation is different enough (the object must derive from both DFoo and IFoo), that IDispatchImpl doesn't do the trick. However, since the majority of the implementation of IDispatchImpl lies in CComTypeInfoHolder, a small class defined for delegating dispinterfaces could easily do the job. The dispimpl2.h header file provides such a class (IDelegatingDispImpl).

Raw dispinterfaces

A raw dispinterface is defined like so:
dispinterface DFoo
{
properties:
    BSTR Foo;

methods:
    void DoFoo();
}
Notice that no real interface is involved. DFoo will be generated in IDL as an interface that derives from IDispatch and provides no additional methods. However, a typeinfo object is still useful for implementing three out of four of the IDispatch member functions. However, Invoke is left up to the implementor. Yet another C++ class can be defined that does 3/4th of the work, leaving Invoke to the implementor. The dispimpl2.h header file provides such a class (IRawDispImpl).

dispimpl2.h

This sample includes the dispimpl2.h header file, which provides three class, IDualDispImpl, IDelegatingDispImpl and IRawDispImpl, i.e. one for each kind of dispatch-based interface.

IDualDispImpl

IDualDispImpl is really just IDispatchImpl, renamed for clarity. Using IDualDispImpl, assuming a dual interface defined as mentioned above, looks like this:
class CFoo : ..., public IDualDispImpl<IFoo>
{
...
BEGIN_COM_MAP(CFoo)
    COM_INTERFACE_ENTRY(IFoo)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
...
};
Of course, the custom methods of IFoo are up to the implementor as well.

IDelegatingDispImpl

IDelegatingDispImpl derives from both the midl-generated dispinterface and the non-dispatch delegatee interface, hence both are required template arguments:
class CFoo : ..., public IDelegatingDispImpl<DFoo, IFoo>
{
...
BEGIN_COM_MAP(CFoo)
    COM_INTERFACE_ENTRY(IFoo)
    COM_INTERFACE_ENTRY(DFoo)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
...
};
Again, the implementor is responsible for all of the custom IFoo methods.

IRawDispImpl

IRawDispImpl only derives from the midl-generated dispinterface and uses nothing for the forwarding of the methods, as there's no real interface to use. Instead, the implementator of DFoo is responsible for implementing Invoke by hand, although IRawDispImpl will implement the other three methods:
class CFoo : ..., public IRawDispImpl<DFoo>
{
...
BEGIN_COM_MAP(CFoo)
    COM_INTERFACE_ENTRY(DFoo)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
...
    STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
                        WORD wFlags, DISPPARAMS * pDispParams,
                        VARIANT * pVarResult, EXCEPINFO * pExcepInfo,
                        UINT * puArgErr);
};
Responsible C++ programmers will implement Invoke to validate, convert and forward arguments to a real member function, as the typeinfo object does in the case of the other two types of interfaces.

Copyright

Copyright (c) 1998 Chris Sells
All rights reserved.
NO WARRANTIES ARE EXTENDED. USE AT YOUR OWN RISK.

Contact the author with suggestions or comments at csells@sellsbrothers.com.