Nested Dispatch Implementation

Imagine the following IDL:

[ oleautomation ]
interface ICowboy : IUnknown
{
    HRESULT Draw([in] long nSpeed); // Conflicts with IArtist::Draw
    HRESULT Shoot();                 // Conflicts with ICurse::Shoot
}

[ oleautomation ]
interface IArtist : IUnknown
{
    HRESULT Draw([in] long nStrokes); // Conflicts with ICowboy::Draw
    HRESULT Paint();
}

[ oleautomation ]
interface ICurse : IUnknown
{
    HRESULT Shoot(); // Conflicts with ICowboy::Shoot
    HRESULT Darn();
}

library TURNOFTHECENTURYLib
{
    coclass AcePowell
    {
        interface ICowboy;
        [default] interface IArtist;
        interface ICurse;
    }
}

The client would like to write the following code:

<script language=vbscript>

    ace.paint    ' top level object has all methods of
                 ' [default] interface

    dim artist
    set artist = ace.interface("iartist")

    if not artist is nothing then artist.draw 100

    dim curse
    set curse = artist.interface("icurse")
    if not curse is nothing then curse.darn

    dim cowboy
    set cowboy = curse.interface("icowboy")
    if not cowboy is nothing then cowboy.shoot

   if not curse is nothing then
        curse.shoot
        curse.darn

    end if

    dim ace2
    set ace2 = curse.interface("default") ' Get back top-level itf
    set ace2 = curse.interface("object")  ' Get back top-level itf

</script>

As you can see, we're exposing a collection of objects where the top level object has all of the methods and properties of the [default] interface as well a property to obtain an interface pointer on a sub-object for each of the oleautomation interfaces. The sub-objects are separate COM identities whose implementation of IDispatch simply forwards to the main identity via the specific interface in question. Each of the sub-objects also have one property per interface to get to the other interfaces, thus simulated the rules of COM identity as exposed via QI. This allows us to build an invocation model very similar to VB's where QI is an assignment and references of type class mean references to the [default] interface.

Usage

To use this implementation of IDispatch, publish all interface you'd like to expose via the interface property in the coclass statement, e.g.

library TESTOBJLib
{
    coclass DogCat
    {
        [default] interface IDog;
        interface ICat;
    }
}

All interfaces must be oleautomation compliant to be exposed via IDispatch (although they don't have to be dual or even marked oleautomation).

Include multidisp.h as needed (stdafx.h is a good place) and multidisp_i.c in exactly one place (stdafx.cpp is a good place).

Implement the interfaces as normal, but expose IDispatch as a aggregate, using CLSID_NestedDispatch as the implementation of IDispatch, e.g.

class ATL_NO_VTABLE CDogCat :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CDogCat, &CLSID_DogCat>,

    public IProvideClassInfoImpl<&CLSID_DogCat, &LIBID_TESTOBJLib>,
    public ICat,
    public IDog
{
public:

DECLARE_REGISTRY_RESOURCEID(IDR_DOGCAT)
DECLARE_GET_CONTROLLING_UNKNOWN()

BEGIN_COM_MAP(CDogCat)
    COM_INTERFACE_ENTRY(IDog)
    COM_INTERFACE_ENTRY(ICat)
    COM_INTERFACE_ENTRY(IProvideClassInfo)
    COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IDispatch,

                                      m_spDispatch.p,
                                      CLSID_NestedDispatch)
END_COM_MAP()

// IDogCat
public:
    STDMETHOD(Bark)();
    STDMETHOD(Purr)();

public:
    CComPtr<IUnknown> m_spDispatch;
};

For the Nested Dispatch implementation to find the interfaces exposed by your class, expose IProvideClassInfo. An implementation class, IProvideClassInfoImpl, has been provided in multidisp.h for this purpose.

Limitations

If you're building a control, VB requires that the default interface of your coclass be a dual interface. And, since VB uses the typelib to show only the properties of the default interface, any properties of the non-default interfaces won't be shown in the property browser.

Revision History

Version 1.0.0.2, 12 September 2002 by Serge Sandler ssandler@insureware.com.

Incompatibility of multidisp.dll with Windows 9x platform is fixed by replacing lstrcmpiW() with _wcsicmp().

Note,

1. Statically linked C run-time library adds around 25K to the executable, so if size is a concern, undefine _ATL_MIN_CRT and reimplement _wcsicmp() yourself or link to CRT dynamically.

2. Use "Win32 Release MinDependency" configuration for release, as Unicode configuration will not load in Windows 9x (unless Microsoft layer for Unicode is installed).

Version 1.0.0.3, 17 September 2002 by Serge Sandler ssandler@insureware.com.

Fixed analysis of wFlags in Invoke()

Copyright

CNestedDispatch and CFlyweightDispatch, version 1.0
Copyright (c) 1998 by Chris Sells and Don Box.

All rights reserved.
NO WARRANTIES ARE EXTENDED. USE AT YOUR OWN RISK.

To contact the author with suggestions or comments, send mail to csells@sellsbrothers.com.