viksoe.dk

atldyndispatch.h

Written by Bjarke Viksoe.
This article was submitted .


Did you know that you can create IDispatch interfaces on the fly? They don't have to be generated from the IDL or typelib files like ATL requires you to do.

The following classes allow you to generate IDispatch name tables dynamically instead of loading method information from the typelib. There are two classes: the IDispDynImpl allows you to turn any C++ object into an automation COM object - without the need of the ATL framework! The other class presented here achieves the same goal, but this class integrates into the ATL architecture.

The classes allow you to create one IDispatch interface for one instance of your C++ class, while handing out a completely different IDispatch interface for other clients.

You may have heard of IDispatchEx and think that this code is violating COM rules. IDispatch interfaces are not allowed to change at runtime - but it is perfectly acceptable to generate a new (and even a different) name table for every instance of an object!

The Dispatch Map

Both classes use a macro map to describe the name table. You use these macros to generate the methods and properties in your IDispatch object.

The MFC library has something called Dispatch Maps. It is absent in ATL, so let's define one...
Here is an example of a dispatch map:

  BEGIN_DISPATCH_MAP(CMyObject)
    DISP_METHOD(FooManyArgs, VT_EMPTY, 2, VTS_DISPATCH VTS_I4)
    DISP_METHOD0(FooNoArgs,  VT_EMPTY)
    DISP_METHOD1(FooOneArg,  VT_I2, VT_BOOL)
  END_DISPATCH_MAP()
The DISP_METHOD macro defines a method with return code and arguments. The first parameter is the name of the C++ function and the automation name. The 2nd parameter is the type of the return code. This parameter uses the VARIANT type identifiers (or VARTYPE). The identifier VT_EMPTY means "no value". The 3rd parameter in DISP_METHOD is the number of arguments the function takes. And the last argument is a space-separated list of one or more constants specifying the functionís parameter list.
The DISP_METHOD0 and DISP_METHOD1 are short-hand macros for defining methods with one or no arguments.

The implemented C++ functions should look like this...

  void __stdcall FooManyArgs(IDispatch *pDisp, LONG i);
  void __stdcall FooNoArgs();
  SHORT __stdcall FooOneArg(VARIANT_BOOL b);

Properties are supported using the following macros:

  BEGIN_DISPATCH_MAP(CMyObject)
    DISP_PROP(PropReadWrite, VT_I2)
    DISP_PROPGET(PropReadOnly, VT_BSTR)
    DISP_PROPPUT(PropAssignOnly, VT_I4)
  END_DISPATCH_MAP()
where DISP_PROP defines both a read and write C++ function for accessing the property, here through the C++ functions...
  SHORT __stdcall get_PropReadWrite();
  void __stdcall put_PropReadWrite(SHORT value);
  BSTR __stdcall get_PropReadOnly();
  void __stdcall put_PropAssignOnly(LONG value);

IDispDynImpl

The IDispDynImpl class allows you to take any C++ object and turn it into an IDispatch object. No ATL initialisation code is needed. The class is for use when you know exactly who is calling you and how. The class implements IUnknown and the basic IDispatch interfaces using stubs. There is no real reference counting, so you had better watch out how your object is used.

I find this class particular useful when I need to pass automation objects e.g. between a web browser and the hosting application. There is no real need to create class factories, typelibs and those sorts of things in that situation. VTable (early) binding is not useful because scripting code cannot use them anyhow.

CComDynTypeInfoHolder

If you dive into the architecture of ATL, you can fish out a class called CComTypeInfoHolder, which manages the type information of the ATL IDispatch implementation. This class is templated on superclasses like IDispatchImpl, so clearly the intention has always been that ATL should be able to handle other means of instantiating the IDispatch interface than using typelibs.

The CComDynTypeInfoHolder class does exactly that. It uses the above macro map to create the IDispatch name table.
It also uses an ancient OLE Automation method called ::CreateDispTypeInfo() to generate Type Info for the macro map. This info can then be used by the other ATL classes. In addition, it implements the IDispatch code needed to be used in your object.

How useful is this

You may argue that the dispatch map shown here doesn't really change much. You still have to hardcode a list of your methods, just like in the IDL file.
This is not entirely true. Just like all the standard ATL maps, it allows you to override the default behaviour. The internal function _GetDispMap() is created by the BEGIN_DISPATCH_MAP macro. You can provide your own function instead - allowing truly dynamic creation of the name table.

Performance... how about performance?
Let's not talk speed here. If performance were a concern, you wouldn't be using IDispatch in the first place. Actually, because this is a very custom implementation, it is slightly faster than the standard ATL implementation of IDispatch!

Is validation and safety the same? The class does standard checking for invalid arguments and return values.

How to use them

Here is a simple sample of using IDispDynImpl. Note how the methods must have stdcall calling convention.
class CMyObject :
  public IDispDynImpl<CMyObject>
{
  BEGIN_DISPATCH_MAP(CMyObject)
    DISP_METHOD1(Foo, VT_BOOL, VT_I2)
  END_DISPATCH_MAP()

  VARIANT_BOOL __stdcall Foo(SHORT in)
  {
    return VARIANT_TRUE;
  }
};

The CComDynTypeInfoHolder class is used like this...

class ATL_NO_VTABLE CTestObj : 
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass< CTestObj, &CLSID_TestObj >,
   public IDispatchImpl< IDispatch, NULL, NULL, 1, 0, 
     CComDynTypeInfoHolder<CTestObj> >
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_TESTOBJ)

BEGIN_COM_MAP(CTestObj)
   COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

BEGIN_DISPATCH_MAP(CTestObj)
   DISP_METHOD(Foo, VT_I2, 2, VTS_BSTR VTS_BSTR)
END_DISPATCH_MAP()

   SHORT __stdcall Foo(BSTR bstr1, BSTR bstr2)
   {
      ATLTRACE("Foo %ls %ls\n", bstr1, bstr2);
      return 0;
   }
};
This is a stripped ATL object generated with the ATL Wizard.

With both classes there seems to be a few quirks. First, I haven't figured out how to add COM exceptions and error handling, not without introducing the penalty of C++ exceptions into the project anyway. Also Named Arguments are not supported.

Source Code Dependencies

Microsoft ATL Library

Useful Links

Oops, Valery Pryamikov did a similar class
Chris Sells also has a few ATL IDispatch modifications

Download Files

DownloadSource Code (5 Kb)

To the top