Supporting IDispatch

[Previous] [Next]

IDispatch enables an object to expose properties and methods to clients that don't have access to type information at compile time or that perhaps aren't compiled languages. The client might be a scripting language such as Microsoft VBScript, or it might be a control container that must support any control that is inserted at run time. Scripting languages lack the ability to include a description of the interfaces the object supports. Containers can't predict which controls the user will want to insert at run time. IDispatch solves this problem by providing a predefined interface with a mechanism for invoking methods by integer ID, commonly called a dispatch ID, or dispid. If the ID for a method isn't known, it can be discovered by name. You can create an object that exposes its properties and methods through IDispatch only or through a dual interface. Dual interfaces allow either vtable binding or dynamic binding by inheriting from IDispatch. ATL objects are generated with a dual interface by default. The implementation for IDispatch on a dual interface is provided in the ATL IDispatchImpl base class, which your object derives from, and is defined as here:

 class ATL_NO_VTABLE CAtlSimple :      public CComObjectRootEx<CComSingleThreadModel>,     public CComCoClass<CAtlSimple, &CLSID_AtlSimple>,     public IDispatchImpl<IAtlSimple, &IID_IAtlSimple,         &LIBID_ATLSIMPLESVRLib> 

IDispatchImpl implements the four IDispatch methods, which are described in Table 6-3.

Table 6-3 The Four IDispatch Methods

Method Description
GetTypeInfoCount Retrieves the number of type information interfaces the dual interface supports (0 or 1)
GetTypeInfo Gets the type information that describes the dual interface
GetIDsOfNames Converts a string method name to an integer ID to be used in Invoke
Invoke Calls a method on the dual interface

Implementing IDispatch on a dual interface relies on the type library that describes the object. IDispatchImpl receives the GUID for the type library as a template parameter, so it can load the type library and delegate two of the four IDispatch methods to the type library parser. In particular, the type library parser COM provides in oleaut32.DLL implements Invoke and GetIDsOfNames, which are accessed via an ITypeInfo pointer. IDispatchImpl maintains a contained CComTypeInfoHolder object to interface with the type library parser and procure an ITypeInfo pointer. IDispatchImpl delegates to CComTypeInfoHolder for all methods except GetTypeInfoCount, which simply returns 1. The CComTypeInfoHolder class name is actually a default template parameter to IDispatchImpl. Although this scenario is unlikely, if necessary, you can provide a type description through some other mechanism by specifying a different class name. The IDispatchImpl class is shown here:

 template <class T, const IID* piid, const GUID* plibid =     &CComModule::m_libid, WORD wMajor = 1,     WORD wMinor = 0, class tihclass = CComTypeInfoHolder> class ATL_NO_VTABLE IDispatchImpl : public T { public:     typedef tihclass _tihclass; // IDispatch     STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)     {         *pctinfo = 1;         return S_OK;     }     STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid,         ITypeInfo** pptinfo)     {         return _tih.GetTypeInfo(itinfo, lcid, pptinfo);     }     STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames,         UINT cNames, LCID lcid, DISPID* rgdispid)     {         return _tih.GetIDsOfNames(riid, rgszNames, cNames,             lcid, rgdispid);     }     STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,         LCID lcid, WORD wFlags, DISPPARAMS* pdispparams,         VARIANT* pvarResult, EXCEPINFO* pexcepinfo,         UINT* puArgErr)     {         return _tih.Invoke((IDispatch*)this, dispidMember,             riid, lcid, wFlags, pdispparams, pvarResult,             pexcepinfo, puArgErr);     } protected:     static _tihclass _tih;     static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)     {         return _tih.GetTI(lcid, ppInfo);     } }; 

As you can see, the static CComTypeInfoHolder member _tih does the brunt of the work. Before CComTypeInfoHolder can do anything, it must load the type library and get the ITypeInfo pointer from the parser for the dual interface. This is accomplished in CComTypeInfoHolder::GetTI, part of which is shown here:

 ITypeLib* pTypeLib; hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor,     lcid, &pTypeLib); if(SUCCEEDED(hRes)) {     CComPtr<ITypeInfo> spTypeInfo;     hRes = pTypeLib->GetTypeInfoOfGuid(*m_pguid, &spTypeInfo);      } 

The member variables m_pguid, m_plibid, m_wMajor, and m_wMinor are initialized statically from the IDispatchImpl template parameters. The variable m_pguid is the interface ID for the dual interface. The variable m_plibid is the type library ID. Although not obvious, major and minor library version information is also specified in the IDispatchImpl template. The version numbers default to 0 and 1, respectively. Because type libraries can contain information about several interfaces, enumerations, and coclasses, GetTypeInfoOfGuid is used to single out the information for the dual interface specified in the template. The resulting ITypeInfo pointer is cached in CComTypeInfoHolder::m_pInfo, where it can be retrieved by an IDispatchImpl::GetTypeInfo request or used internally to access the parser. GetTI is called to load the library and cache the pointer only once: the first time IDispatchImpl delegates a call to CComTypeInfoHolder. That means the locale ID specified in GetTypeInfo, GetIDsOfNames, or Invoke is ignored after the first call because the type info has already been loaded. GetTI also caches an array of method names and their corresponding dispatch IDs to speed up GetIDsOfNames access. Finally, when an Invoke call is received, CComTypeInfoHolder can simply delegate to the type library parser, as shown here:

 HRESULT Invoke(IDispatch* p, DISPID dispidMember,     REFIID /* riid */, LCID lcid, WORD wFlags,     DISPPARAMS* pdispparams, VARIANT* pvarResult,     EXCEPINFO* pexcepinfo, UINT* puArgErr) {     HRESULT hRes = EnsureTI(lcid);     if(m_pInfo != NULL)         hRes = m_pInfo->Invoke(p, dispidMember, wFlags,             pdispparams, pvarResult, pexcepinfo, puArgErr);     return hRes; } 

In the preceding code segment, EnsureTI loads the type library (using GetTI) if it hasn't been loaded already.

We're done talking about IDispatch. That's the last foundation topic we'll cover except for the debugging support we get for free by using the ATL framework.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: