Thursday, 15 June 2017

ATL Notes 2 - Only Inherit from IDispatch Once

Continuing on ATL (Active Template Library) ...

So when writing an object with ATL for use in Excel VBA I recommend selecting dual interface from the Simple Object Wizard this gives both a binary vtable interface for early-binding clients as well as an IDispatch implementation for late-binding clients. Note, when I say 'late binding clients' I'm not talking about VBScript clients but VBA developers who'd prefer to call into a COM library with flexibility.

But there is a fly in the ointment here. COM is all about interface based programming where one is supposed to factor out the functionality of a class into separate interfaces, multiple dual interfaces will not compile in ATL because every Com class must only inherit from IDispatch once.

It isn't just the use case of factoring out functionality into separate interfaces but also the use case of shipping an additional interface to supplement, update or enhance the functionality of a class. In both use cases ATL will only allow one of these interfaces to be dual. To resolve the ATL compilation errors you will need designate one interface as being prime and only allow late-binding to that interface.

This pretty much scuppers the factoring out use-case. Developers coming from VBA will probably not grieve over this as they are used to using one interface only (VBA does have the Implements keyword but is rarely used). For the shipping updates use-case you should ship a new Com class with a new expanded all encompassing interface.

I could give some sample code but actually I have found some internet pages that illustrate the problem.

com problem inheriting twice from IDispatch
ATL inheritance mistake
They also give the solution which is to make one interface the goto interface when QueryInterface is called for IDispatch by using the COM_INTERFACE_ENTRY2 macro. So the following non-compiling code

BEGIN_COM_MAP(CAudioRecorder) 
        COM_INTERFACE_ENTRY(IAudioDevice) 
        COM_INTERFACE_ENTRY(IAudioRecorder)   <<<< ERROR 1 
        COM_INTERFACE_ENTRY(IDispatch)        <<<< ERROR 2 
END_COM_MAP()

should be changed to the following to direct QueryInterface calls for IDispatch to IAudioDevice

BEGIN_COM_MAP(CAudioRecorder) 
        COM_INTERFACE_ENTRY(IAudioDevice) 
        COM_INTERFACE_ENTRY(IAudioRecorder)   
        COM_INTERFACE_ENTRY2(IDispatch,IAudioDevice)        
END_COM_MAP()


No comments:

Post a Comment