Click here to Skip to main content
15,886,258 members
Articles / Desktop Programming / ATL

IDispatchImplEx - Template classes for multi-interface dispatch

Rate me:
Please Sign up or sign in to vote.
4.65/5 (10 votes)
24 Jan 2009CPOL2 min read 36.4K   365   17  
C++ template classes for implementing COM objects that perform typeinfo-driven dispatch on multiple interfaces, both dual and pure.
#ifndef __BxComExt
#define __BxComExt
//***********************************************
// Author:  Kjell Tangen (kjarild@hotmail.com)
// Filename: BxComExt.h
// Part of: Outbacks of COM
// Copyright (C) Kjell Tangen, 1998-2009
// http://www.codeproject.com/kb/atl/idispatchimplex.aspx/
// Current Version: 4.02    Date: 2009-01-24
//-----------------------------------------------
// Description:
// This file contains implementation classes for COM
// CComTypeInfoHolderEx - Extends ATL::CComTypeInfoHolder for typeinfo-driven multi interface dispatch
// IDispatchImplEx      - Replaces ATL::IDispatchImpl
// PureDispatchImpl     - Used to implement a pure IDispatch interface that does typeinfo-driven 
//                        multi-interface dispatch
// CComClonableBase     - Base class for clonable COM objects
//
// The implementation uses ATL and STL.  
// This file should be included right after the ATL-includes 
// (normally in stdafx.h)
//
//-----------------------------------------------
// Created: 1998-03-06  
// 1.0  1998-05-07
// 2.0  1998-11-19: Converted to ATL 3.0
// 3.0  1999-07-10: Uses IProvideClassInfo if implemented.
//                  If typeinfo lookup of the CLSID fails (e.g. if the
//                  class and interface are defined in different typelibs
//                  the correct type library for the class will be inquired.
// 3.1  1999-11-18: Fixed a bug in CComTypeInfoHolderEx::Invoke when invoking a method on a referenced interface
//                  Now also uses IProvideClassInfo2 if IProvideClassInfo is not implemented
// 3.11 1999-12-10: Fixed a bug in CComTypeInfoHolderEx::_FindTIByReg which was present
//                  in Unicode builds.
// 3.12 2000-01-06: CComTypeInfoHolderEx no longer uses the critical section of the module,
//                  instead, it uses a private critical section.
// 3.13 2000-11-27: Fixed concurrency problem
// 3.14 2002-03-20: Fixed duplicate comdat problem related to IDispatchImplEx
// 4.0  2002-11-14: Support for implementation of a pure IDispatch implementation, added the PureDispatchImpl
//                  template. Has been tested with ATL 7.
// 4.01 2003-02-20: Fixed a bug related to dispatching on type libraries with version no. wMajor.wMinor > 1.0 
// 4.02 2009-01-24: Changed from using _stprintf to _stprintf_s in line 306
//***********************************************

#pragma warning( disable : 4786) // identifier truncated to 255 chars

#include <utility>
#include <vector>
#include <map>

// Required by STL:
bool operator < (const GUID g1, const GUID g2);

#ifndef _stdDispIdBase // Override this to have another default
#define _stdDispIdBase -0x10F0
#endif
//***************************************************************************
// Class CComTypeInfoHolderEx
// Kjell Tangen, 1998-2009 (kjarild@hotmail.com)
// 
// CComTypeInfoHolderEx is used by the IDispatchImplEx and PureDispatchImpl template classes to access
// the object's  type information in order to support dispatch clients.
// These classes use type info to dispatch to any ole automation compatible 
// interface on the class (declared with dual or oleautomation IDL directives). 
// If you have an ATL-based implementation,
// use the IDispatchImplEx class instead of the standard ATL::IDispatchImpl
// class if your COM-class has multiple dual interfaces and you want your 
// class to be scriptable. CComTypeInfoHolderEx will search through the type 
// info on each of the interfaces on the class and try to bind to the 
// dispatch member requested by the client.
// Even if your implementation is not based on ATL, this class can be used by
// your dual interface implementation, just look in the IDispatchImplEx
// class for details on how to use it.
// If the type info for an interface is in another type library than the class
// implementing it, the class should implement the IProvideClassInfo interface.
// Using ATL, this can be done using the IProvideClassInfo2Impl template.
//
// Alternatively, if you don't want to use dual interfaces and want to have a single IDispatch 
// interface on your object, you may use PureDispatchImpl to implement the single IDispatch interface
// that dispatches on multiple oleautomation-compatiple IUnknown-derived interfaces.
// This template also uses CComTypeInfoHolderEx to walk through type information when dispatching.
// Therefore PureDispatchImpl has basically the same capabilities as IDispatchImplEx. Note that it is 
// not possible to mix both templates in the same class definition. This is by design. PureDispatchImpl
// has been designed for use when a class supports no dual interfaces. If one or more dual interfaces
// are implemented, then IDispatchImplEx must be used.
//
// New in version 4.0:
// -------------------
// Added the PureDispatchImpl template. This template reuses the CComTypeInfoHolderEx template
// and therefore has the same functionality as IDispatchImplEx. However, it can be used to support
// designs where no dual interfaces are used, and instead the class has a single IDispatch interface and
// one or more IUnknown-derived, oleautomation-compatible interfaces.
// 
// New in version 3.0:
// -------------------
// CComTypeInfoHolderEx now supports multi interface dispatch in cases where a
// class implements interfaces defined by other another type library than the 
// module the class belongs to. Requirements for multiinterface dispatch to work
// in this more general case are:
// * The class implements IProvideClassInfo
// or
// * The class has a registered type library, i.e. a "TypeLib" subkey in the
// registry with a REG_SZ value equal to the LIBID of the module which the class belongs to 
//  
// Intented usage:
// ---------------
// If you have an ATL-based COM implementation, you will not use
// CComTypeInfoHolderEx directly. Instead you will just implement your dual interfaces
// by deriving your class from IDispatchImplEx instead of IDispatchImpl:
// class ATL_NO_VTABLE CMyClass : 
// public IProvideClassInfo2Impl<&CLSID_MyClass,&GUID_NULL, &LIBID_MyModule>,
//	public IDispatchImplEx<IMyInterface, &IID_IMyInterface, &CLSID_MyClass, &LIBID_MyModule>,
//
// If the interface is defined in an imported type library, this will still work:
// class ATL_NO_VTABLE CMyClass : 
// public IProvideClassInfo2Impl<&CLSID_MyClass,&GUID_NULL, &LIBID_MyModule>,
//	public IDispatchImplEx<IMyInterface, &IID_IMyInterface, &CLSID_MyClass, &LIBID_ImportedLib>,
// 
// If your class supports events, replace GUID_NULL with the IID of your event interface in the
// IProvideClassInfo2Impl template
//***************************************************************************
/* Example:

IDL:
----
   importlib("ImportedLib.tlb"); // Interface IC is defined here
   interface IA : IDispatch
   {
      HRESULT MethodOnIA();
   }:
   interface IB : IDispatch
   {
      HRESULT MethodOnIB();
   };
   interface IPure1 : IUnknown
   {
      HRESULT DispMe();
   };
   interface IPure2 : IUnknown
   {
      HRESULT DispMeAgain();
   };
   coclass MyClass
   {
		[default] interface IA;
      interface IB;
      interface IC; // Defined in ImportedLib.tlb
   };
   coclass MyClassWithNoDuals
   {
	   [default] interface IPure1;
      interface IPure2;
      interface ID; // Defined in ImportedLib.tlb
   };
C++ implementations:
---------------------
   class ATL_NO_VTABLE CMyClass : 
      public CComObjectRootEx<CComSingleThreadModel>,
      public IProvideClassInfo2Impl<&CLSID_MyClass,&GUID_NULL, &LIBID_MyLib>, 
	   public IDispatchImplEx<IA, &IID_IA, &CLSID_MyClass, &LIBID_MyLib>,
	   public IDispatchImplEx<IB, &IID_IB, &CLSID_MyClass, &LIBID_MyLib>,
	   public IDispatchImplEx<IC, &IID_IC, &CLSID_MyClass, &LIBID_ImportedLib>,
	   public CComCoClass<CMyClass, &CLSID_MyClass>,
	   public ISupportErrorInfo
   {
   ..standard ATL implementation

   class ATL_NO_VTABLE CMyClassWithNoDuals : 
      public CComObjectRootEx<CComSingleThreadModel>,
	   public CComCoClass<CMyClassWithNoDuals, &CLSID_MyClassWithNoDuals>,
      public PureDispatchImpl<&CLSID_MyClassWithNoDuals, &LIBID_MyLib>,
      public IPure1,
      public IPure2,
      public ID,
	   public ISupportErrorInfo,
   {
   ..standard ATL implementation
      BEGIN_COM_MAP(CMyClassWithNoDuals)
  	      COM_INTERFACE_ENTRY(IPure1)
  	      COM_INTERFACE_ENTRY(IPure2)
  	      COM_INTERFACE_ENTRY(ID)
	      COM_INTERFACE_ENTRY(IDispatch)
	      COM_INTERFACE_ENTRY(ISupportErrorInfo)
      END_COM_MAP()
   ...

VB script client:
-----------------

  Dim instanceOfMyClass
  ... got instanceOfMyClass from somewhere
  ' Regardless of which interface I got hold of, 
  ' I can script towards all methods on the class:
  instanceOfMyClass.MethodOnIA
  instanceOfMyClass.MethodOnIB
  instanceOfMyClass.MethodOnIC

  Dim instanceOfMyClassWithNoDuals
  ... got instanceOfMyClassWithNoDuals from somewhere
  ' Regardless of which interface I got hold of, 
  ' I can script towards all methods on the class:
  instanceOfMyClassWithNoDuals.DispMe
  instanceOfMyClassWithNoDuals.DispMeAgain
  instanceOfMyClassWithNoDuals.MethodOnID
*/

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase=_stdDispIdBase,WORD wMajor = 1,WORD wMinor = 0>
class CComTypeInfoHolderEx
: public CComTypeInfoHolder
{
   typedef std::pair<long, IID> _dispidEntry;
   typedef std::pair<_dispidEntry, HREFTYPE> _typeinfoEntry;
   typedef std::vector<_typeinfoEntry> _idispidContainer;
   typedef std::map< _dispidEntry, long> _cdispidContainer;
   typedef _cdispidContainer::value_type  _cdispValue;
   typedef _idispidContainer::value_type  _idispValue;

   long m_nextIdx;
   _idispidContainer m_idispCont; // Contains Interface-specific dispids
   _cdispidContainer m_cdispCont; // Maps pairs of IID+dispid to class-specific dispids
   const GUID* m_pclsid;
   //static CComAutoCriticalSection m_csec;
   static CComAutoCriticalSection& _GetCSEC()
   {
      static CComAutoCriticalSection csec;
      return csec;
   }
public:
   CComTypeInfoHolderEx(const IID* pIID = piid, const CLSID* pCLSID = pclsid, const GUID* pLIBID = plibid) 
   : m_idispCont() 
   {
      m_nextIdx = 0;
      m_pguid = pIID;
      m_pclsid = pCLSID;
      m_plibid = pLIBID;
      m_wMajor = wMajor;// 1;
      m_wMinor = wMinor;// 0;
      m_pInfo = NULL;
      m_dwRef = 0;
   }
   long GetCDIDFromIdx(long idx) {return (DispIdBase + idx);}
   long GetIdxFromCDID(long cdid){return (cdid - DispIdBase);}
   long AddEntry(long lDID, IID, HREFTYPE hrt = 0);
   long GetDIDFromIdx(long idx){return m_idispCont[idx].first.first;}
   IID& GetIIDFromIdx(long idx) {return m_idispCont[idx].first.second;}
   HREFTYPE GetHREFTYPEFromIdx(long idx) {return m_idispCont[idx].second;}
   HRESULT _FindName(ITypeInfo* pInfo, LPOLESTR* rgszNames, UINT cNames, DISPID* rgdispid);
   HRESULT _FindNameByReg(LPOLESTR* rgszNames, UINT cNames, DISPID* rgdispid);
   HRESULT _FindTIByReg(ITypeInfo** ppifTI);
   HRESULT _GetIProvideClassInfo(IUnknown* pifUnk, IProvideClassInfo** ppifCI)
   {
      HRESULT hr;
      *ppifCI = NULL;
      hr = pifUnk->QueryInterface(IID_IProvideClassInfo, (void**)ppifCI);
      if(FAILED(hr))
      {
         hr = pifUnk->QueryInterface(IID_IProvideClassInfo2, (void**)ppifCI);
      }
      return hr;
   }
	HRESULT GetIDsOfNames(IDispatch* pDisp, REFIID riid, LPOLESTR* rgszNames, UINT cNames,
		LCID lcid, DISPID* rgdispid);
	HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID riid,
		LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
		EXCEPINFO* pexcepinfo, UINT* puArgErr);
};

/*template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase>
CComAutoCriticalSection CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase>::m_csec;*/

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline long CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::AddEntry(long lDID, IID riid, HREFTYPE hrt)
{
   long lNewId;
   _dispidEntry e(lDID,riid);
   _typeinfoEntry ti(e, hrt);
   lNewId = m_nextIdx;
   // Check if the entry exist
   _cdispidContainer::iterator iter = m_cdispCont.find(e);
   if(iter != m_cdispCont.end())
   {
      lNewId = (*iter).second;
   }
   else
   {
      m_idispCont.insert(m_idispCont.end(), ti);
      m_cdispCont.insert(_cdispValue(e,lNewId));
      InterlockedIncrement(&m_nextIdx);
   }
   return lNewId;
}

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline HRESULT CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::_FindTIByReg(ITypeInfo** ppifTI)
{
   HRESULT hRes;
   // Search for the type library registered for the class
   LPOLESTR psClass = NULL;
   *ppifTI = NULL;
   hRes = StringFromCLSID(*pclsid,&psClass);
   if(SUCCEEDED(hRes))
   {
      TCHAR sRegKey[64];
      USES_CONVERSION;
	   _stprintf_s(sRegKey,64,_T("CLSID\\%s\\TypeLib"), OLE2T(psClass));
      HKEY hKey;
      hRes = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, sRegKey,0, KEY_READ, &hKey);
      if(hRes == ERROR_SUCCESS)
      {
         TCHAR sLibId[40];
         DWORD dwSize = 40 * sizeof(TCHAR); // Buffersize, not number of characters!
         DWORD dwType = REG_SZ;
         hRes = ::RegQueryValueEx(hKey, _T(""), NULL, &dwType, (LPBYTE)sLibId, &dwSize);
         if(SUCCEEDED(hRes))
         {
            GUID libid;
            hRes = ::CLSIDFromString(T2OLE(sLibId),&libid);
            if(SUCCEEDED(hRes))
            {
		         ITypeLib* pTypeLib;
               // I know, I know - this is a hack, but it will work in most cases.
               // If it doesn't, tough luck. We have gone far enough!
               // If it fails here, you should try implementing IProvideClassInfo
               // on your class.
		         hRes = LoadRegTypeLib(libid, m_wMajor, m_wMinor, LOCALE_USER_DEFAULT, &pTypeLib);
		         if (SUCCEEDED(hRes))
		         {
			         hRes = pTypeLib->GetTypeInfoOfGuid(*pclsid, ppifTI);
			         pTypeLib->Release();
               }
            }
         }
         ::RegCloseKey(hKey);
      }
      CoTaskMemFree(psClass);
   }
   return hRes;
}

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline HRESULT CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::_FindNameByReg(LPOLESTR* rgszNames, UINT cNames, DISPID* rgdispid)
{
   HRESULT hRes;
   // Search for the type library registered for the class
   ITypeInfo* pTypeInfo = NULL;
   hRes = _FindTIByReg(&pTypeInfo);
	if (SUCCEEDED(hRes))
	{
      hRes = _FindName(pTypeInfo, rgszNames, cNames, rgdispid);
      pTypeInfo->Release();
	}
   return hRes;
}

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline HRESULT CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::_FindName(ITypeInfo* pClassTI, LPOLESTR* rgszNames, UINT cNames, DISPID* rgdispid)
{
   HRESULT hRes;
   // Search through the class type info
   TYPEATTR* pTAttr = NULL;
   hRes = pClassTI->GetTypeAttr(&pTAttr);
   if(SUCCEEDED(hRes) && pTAttr != NULL)
   {
      HREFTYPE hRefType;
      unsigned int i;
      bool bComplete = false;
      for(i = 0; !bComplete && i < pTAttr->cImplTypes; ++i)
      {
         hRes = pClassTI->GetRefTypeOfImplType(i,&hRefType);
         if(SUCCEEDED(hRes))
         {
            ITypeInfo* pifTypeInfo2;
            hRes = pClassTI->GetRefTypeInfo(hRefType, &pifTypeInfo2);
            if(SUCCEEDED(hRes) && pifTypeInfo2 != NULL)
            {
               long lDispId;
		         hRes = pifTypeInfo2->GetIDsOfNames(rgszNames, cNames, &lDispId);
               if(SUCCEEDED(hRes))
               {
                  TYPEATTR* pTAttr2 = NULL;
                  hRes = pifTypeInfo2->GetTypeAttr(&pTAttr2);
                  if(SUCCEEDED(hRes) && pTAttr2 != NULL)
                  {
                     bComplete = true;
                     _GetCSEC().Lock();
                     try
                     {
                        long idx = AddEntry(lDispId, (IID&)pTAttr2->guid, hRefType);
                        *rgdispid = GetCDIDFromIdx(idx);
                     }
                     catch(...)
                     {
                        hRes = E_FAIL;
                     }
                     _GetCSEC().Unlock();
                     pifTypeInfo2->ReleaseTypeAttr(pTAttr2);
                  }
               }
               pifTypeInfo2->Release();
            }
         }
      }
      pClassTI->ReleaseTypeAttr(pTAttr);
   }
   return hRes;
}

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline HRESULT CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::GetIDsOfNames(IDispatch* pDisp, REFIID riid, LPOLESTR* rgszNames,
	UINT cNames, LCID lcid, DISPID* rgdispid)
{
   HRESULT hRes = DISP_E_UNKNOWNNAME;
   if(piid)
   {
      hRes = CComTypeInfoHolder::GetIDsOfNames(riid,rgszNames,cNames,
                                             lcid,rgdispid);
   }
   if(SUCCEEDED(hRes))
      return hRes;
   else if(hRes != DISP_E_UNKNOWNNAME)
      return hRes;
   LPOLESTR psName = rgszNames[0];
	ITypeInfo* pInfo = NULL;
   // First, check for IProvideClassInfo
   if(!pDisp)
      return E_POINTER;
   IProvideClassInfo* pClassInfo = NULL;
   hRes = _GetIProvideClassInfo(pDisp, &pClassInfo);
   if(SUCCEEDED(hRes))
   {
      hRes = pClassInfo->GetClassInfo(&pInfo);
      pClassInfo->Release();
      if(SUCCEEDED(hRes))
      {
         long lDispId;
         hRes = _FindName(pInfo, rgszNames, cNames, &lDispId);
         if(SUCCEEDED(hRes))
         {
            *rgdispid = lDispId;
            return S_OK;
         }
      }
   }
   if(piid)
   {
	   hRes = GetTI(lcid, &pInfo);
   }

   unsigned int idx;
   ITypeLib* pifTypeLib = NULL;
   if(pInfo != NULL)
   {
      hRes = pInfo->GetContainingTypeLib(&pifTypeLib, &idx);
   }
   else
   {
		hRes = LoadRegTypeLib(*plibid, m_wMajor, m_wMinor, lcid, &pifTypeLib);
   }
   if(SUCCEEDED(hRes) && pifTypeLib != NULL)
   {
      ITypeInfo* pClassTI = NULL;
      hRes = pifTypeLib->GetTypeInfoOfGuid(*pclsid,&pClassTI);
      if(SUCCEEDED(hRes) && pClassTI != NULL)
      {
         ITypeComp* pifTypeComp = NULL;
         if(pInfo)
            hRes = pInfo->GetTypeComp(&pifTypeComp);
         if(pifTypeComp != NULL)
         {
            ITypeInfo* pifTypeInfo2 = NULL;
            DESCKIND dc;
            BINDPTR bp;
            long lHashId = LHashValOfName(lcid,psName);
            hRes = pifTypeComp->Bind(psName,lHashId, 0,
                                    &pifTypeInfo2, &dc, &bp);
            if(SUCCEEDED(hRes) && pifTypeInfo2 != NULL)
            {
               long lDispId;
		         hRes = pifTypeInfo2->GetIDsOfNames(rgszNames, cNames, &lDispId);
               if(SUCCEEDED(hRes))
               {
                  TYPEATTR* pTAttr = NULL;
                  hRes = pifTypeInfo2->GetTypeAttr(&pTAttr);
                  if(SUCCEEDED(hRes) && pTAttr != NULL)
                  {
                     long idx;
                     _GetCSEC().Lock();
                     try
                     {
                        idx = AddEntry(lDispId, (IID&)pTAttr->guid);
                        *rgdispid = GetCDIDFromIdx(idx);
                     }
                     catch(...)
                     {
                        hRes = E_FAIL;
                     }
                     _GetCSEC().Unlock();
                     pifTypeInfo2->ReleaseTypeAttr(pTAttr);
                  }
               }
               pifTypeInfo2->Release();
            }
            else if(SUCCEEDED(hRes))
            {
               // Search through the class type info
               hRes = _FindName(pClassTI, rgszNames, cNames, rgdispid);
            }
            pifTypeComp->Release();
         }
         else
         {
            // Search through the class type info
            hRes = _FindName(pClassTI, rgszNames, cNames, rgdispid);
         }
         pClassTI->Release();
      }
      pifTypeLib->Release();
   }

	if(pInfo)
      pInfo->Release();
	
   if(FAILED(hRes) && !pClassInfo)
   {
      // One last shot: Search for another, registered type library
      hRes = _FindNameByReg(rgszNames, cNames, rgdispid);
   }
	return hRes;
}

template<const IID* piid, const CLSID* pclsid, const GUID* plibid, long DispIdBase, WORD wMajor, WORD wMinor>
inline HRESULT CComTypeInfoHolderEx<piid,pclsid,plibid,DispIdBase,wMajor,wMinor>::Invoke(IDispatch* p, DISPID dispidMember, REFIID riid,
	LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
	EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
   HRESULT hRes = DISP_E_MEMBERNOTFOUND;
   if(piid)
   {
      hRes = CComTypeInfoHolder::Invoke(p, dispidMember, riid, lcid, wFlags, 
                              pdispparams, pvarResult,pexcepinfo, puArgErr);
   }
   if(SUCCEEDED(hRes))
      return hRes;
   else if(hRes != DISP_E_MEMBERNOTFOUND)
      return hRes;
   
	SetErrorInfo(0, NULL);
   ITypeInfo* pInfo = NULL;
   if(piid)
   {
	   hRes = GetTI(lcid, &pInfo);
   }

   long ldid;
   unsigned int uTypeIndex;
   long idx;
   _typeinfoEntry ti;
   _dispidEntry e;
   IID riid2;
   hRes = S_FALSE;
   _GetCSEC().Lock();
   try
   {
      idx = GetIdxFromCDID(dispidMember);
      ti = m_idispCont.at(idx);
      e = ti.first;
      ldid = e.first;
      riid2 = e.second;
   }
   catch(...)
   {
      hRes = DISP_E_MEMBERNOTFOUND;
   }
   _GetCSEC().Unlock();
   
   if(SUCCEEDED(hRes))
   {
      ITypeLib* pifTypeLib = NULL;
      if(pInfo)
      {
         hRes = pInfo->GetContainingTypeLib(&pifTypeLib, &uTypeIndex);
      }
      else
      {
		   hRes = LoadRegTypeLib(*plibid, m_wMajor, m_wMinor, lcid, &pifTypeLib);
      }
      if(pifTypeLib != NULL)
      {
         ITypeInfo* pifTypeInfo2 = NULL;
         hRes = pifTypeLib->GetTypeInfoOfGuid(riid2,&pifTypeInfo2);
         if(SUCCEEDED(hRes) && pifTypeInfo2 != NULL)
         {
            IDispatch* p2 = NULL;
            hRes = p->QueryInterface(riid2,(void**)&p2);
            if(p2 != NULL)
            {
		         hRes = pifTypeInfo2->Invoke(p2, ldid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
               p2->Release();
            }
            pifTypeInfo2->Release();
         }
         else
         {
            // Try to search for a referenced type, i.e. a type not defined by this type library.
            IProvideClassInfo* pClassInfo = NULL;
            hRes = _GetIProvideClassInfo(p, &pClassInfo);
            if(SUCCEEDED(hRes))
            {
               ITypeInfo* pClassTI = NULL;
               hRes = pClassInfo->GetClassInfo(&pClassTI);
               pClassInfo->Release();
               if(SUCCEEDED(hRes) && pClassTI)
               {
                  hRes = pClassTI->GetRefTypeInfo(ti.second, &pifTypeInfo2);
                  if(SUCCEEDED(hRes) && pifTypeInfo2 != NULL)
                  {
                     IDispatch* p2 = NULL;
                     hRes = p->QueryInterface(riid2,(void**)&p2);
                     if(p2 != NULL)
                     {
		                  hRes = pifTypeInfo2->Invoke(p2, ldid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
                        p2->Release();
                     }
                     pifTypeInfo2->Release();
                  }
                  pClassTI->Release();
               }
            }
            else
            {
               // Search the registry for type info
               ITypeInfo* pClassTI = NULL;
               hRes = _FindTIByReg(&pClassTI);
               if(SUCCEEDED(hRes) && pClassTI)
               {
                  hRes = pClassTI->GetRefTypeInfo(ti.second, &pifTypeInfo2);
                  if(SUCCEEDED(hRes) && pifTypeInfo2 != NULL)
                  {
                     IDispatch* p2 = NULL;
                     hRes = p->QueryInterface(riid2,(void**)&p2);
                     if(p2 != NULL)
                     {
		                  hRes = pifTypeInfo2->Invoke(p2, ldid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
                        p2->Release();
                     }
                     pifTypeInfo2->Release();
                  }
                  pClassTI->Release();
               }
            }
         }
         pifTypeLib->Release();
      }
   }
   if(pInfo)
		pInfo->Release();

	return hRes;
}

template <class T, const IID* piid, const CLSID* pclsid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolderEx<piid,pclsid,plibid, _stdDispIdBase, wMajor,wMinor> >
class ATL_NO_VTABLE IDispatchImplEx : public T
{
public:
	//typedef tihclass _tihclass;
   IDispatchImplEx() 
   {
#ifdef _ATL2 // AddRef/Release is used on the typeinfo holder in ATL2.1, so we must protect it
      _GetTIH().AddRef(); 
#endif
   }
	~IDispatchImplEx() 
   {
#ifdef _ATL2 // AddRef/Release is used on the typeinfo holder in ATL2.1, so we must protect it
      _GetTIH().Release();
#endif
   }

	STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
	{*pctinfo = 1; return S_OK;}

	STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
	{return _GetTIH().GetTypeInfo(itinfo, lcid, pptinfo);}

	STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
		LCID lcid, DISPID* rgdispid)
	{return _GetTIH().GetIDsOfNames((IDispatch*)this, riid, rgszNames, cNames, lcid, rgdispid);}

	STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
		LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
		EXCEPINFO* pexcepinfo, UINT* puArgErr)
	{return _GetTIH().Invoke((IDispatch*)this, dispidMember, riid, lcid,
		wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);}
protected:
   static tihclass& _GetTIH()
   {
      static tihclass _stih;
      return _stih;
   }
	//static _tihclass _tih;
	static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
	{return _GetTIH().GetTI(lcid, ppInfo);}
};

/*template <class T, const IID* piid, const CLSID* pclsid, const GUID* plibid, WORD wMajor,
WORD wMinor, class tihclass>
IDispatchImplEx<T, piid, pclsid, plibid, wMajor, wMinor, tihclass>::_tihclass
IDispatchImplEx<T, piid, pclsid, plibid, wMajor, wMinor, tihclass>::_tih;*/

template <const CLSID* pclsid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolderEx<NULL,pclsid,plibid,wMajor,wMinor> >
class ATL_NO_VTABLE PureDispatchImpl : public IDispatch
{
public:
	//typedef tihclass _tihclass;
   //typedef IDispatch T;
   PureDispatchImpl() 
   {
#ifdef _ATL2 // AddRef/Release is used on the typeinfo holder in ATL2.1, so we must protect it
      _GetTIH().AddRef(); 
#endif
   }
	~PureDispatchImpl() 
   {
#ifdef _ATL2 // AddRef/Release is used on the typeinfo holder in ATL2.1, so we must protect it
      _GetTIH().Release();
#endif
   }

	STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
	{*pctinfo = 1; return S_OK;}

	STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
	{return _GetTIH().GetTypeInfo(itinfo, lcid, pptinfo);}

	STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
		LCID lcid, DISPID* rgdispid)
	{return _GetTIH().GetIDsOfNames((IDispatch*)this, riid, rgszNames, cNames, lcid, rgdispid);}

	STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
		LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
		EXCEPINFO* pexcepinfo, UINT* puArgErr)
	{return _GetTIH().Invoke((IDispatch*)this, dispidMember, riid, lcid,
		wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);}
protected:
   static tihclass& _GetTIH()
   {
      static tihclass _stih;
      return _stih;
   }
	//static _tihclass _tih;
	static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
	{return _GetTIH().GetTI(lcid, ppInfo);}
};

// Required by STL
inline bool operator < (const GUID g1, const GUID g2)
{
   union
   {
      GUID g;
      struct
      {
         ULONG l1;
         ULONG l2;
         ULONG l3;
         ULONG l4;
      } l;
   } u1, u2;
   u1.g = g1;
   u2.g = g2;
   if(g1 == g2)
      return false;
   if(u1.l.l1 > u2.l.l1)
      return false;
   else if(u1.l.l1 < u2.l.l1)
      return true;
   else 
   {
      if(u1.l.l2 > u2.l.l2)
         return false;
      else if(u1.l.l2 < u2.l.l2)
         return true;
      else
      {
         if(u1.l.l3 > u2.l.l3)
            return false;
         else if(u1.l.l3 < u2.l.l3)
            return true;
         else
         {
            if(u1.l.l4 < u2.l.l4)
               return true;
            else
               return false;
         }
      }
   }
}


//***************************************************************************
// Class CComClonableBase
// Kjell Tangen, 1998 (kat@computas.no)
// Computas, Norway
//
// Implements the IClassFactory interface for cloning. The class in particular
// targets cloning of aggregates. Note that this class assumes that your object
// is implemented using ATL.
// 
// Intented usage:
// 1. A clonable class (CMyClass) should inherit from CComClonableBase<CMyClass>
// 2. Declare and implement HRESULT Copy(CMyClass& source) in CMyClass
// 3. Add COM_INTERFACE_ENTRY(IClassFactory) to your interface map. 
//    Important notice: This interface entry MUST BE PRIOR TO ANY AGGREGATE
//    DECLARATIONS where the aggregated object is also clonable.
//***************************************************************************
/*
Example:
class ATL_NO_VTABLE COuter : 
	public CComCoClass<COuter, &CLSID_Outer>,
	public IMyObject,
   public CComClonableBase<COuter>,
	public CComObjectRootEx<CComSingleThreadModel>
{
   CComBSTR    m_bsExpression;
public:
   IUnknown*   m_pifInner; // Aggregated object

****Boiler-plate ATL-code removed for clarity****

BEGIN_COM_MAP(COuter)
	COM_INTERFACE_ENTRY(IMyObject)
   COM_INTERFACE_ENTRY(IClassFactory)
	COM_INTERFACE_ENTRY_AGGREGATE_BLIND( m_pifInner)
END_COM_MAP()

};

// Implementation:
STDMETHODIMP COuter::Copy(COuter& source)
{
   // Copy class members
   m_bsExpression = source.m_bsExpression;

   // Clone the aggregated object
   return = _CopyAggObject(source.m_pifInner, m_pifInner);
}
*/

template <class T>
class ATL_NO_VTABLE CComClonableBase :
   public IClassFactory
{
public:
   // IClassFactory methods:
   STDMETHOD(CreateInstance)(IUnknown* pifOuterUnk,
                                         REFIID riid,
                                         void** ppvObj)
   {
      HRESULT hr = E_UNEXPECTED;
      *ppvObj = NULL;
      if(pifOuterUnk)
      { 
         IUnknown* pifInnerUnk = NULL;
         // Clone should be part of an aggregate
         hr = CComCreator< CComAggObject<T> >::CreateInstance(pifOuterUnk,
                                                              riid,
                                                              (void**)&pifInnerUnk);
         if(pifInnerUnk)
         {
            CComAggObject<T>* pObject = static_cast<CComAggObject<T>*>(pifInnerUnk);
            if(pObject)
            {
               hr = pObject->m_contained.Copy(static_cast<T&>(*this));
               *ppvObj = (void*)pifInnerUnk;
            }
         }
      }
      else
      {
         CComObject<T>* pObject = NULL;
         hr = CComObject<T>::CreateInstance(&pObject);
         if(pObject)
         {
            pObject->AddRef();
            hr = pObject->Copy(static_cast<T&>(*this));
            hr = pObject->QueryInterface(riid, ppvObj);
            pObject->Release();
         }
      }

      return hr;
   }
   STDMETHOD(LockServer)(BOOL fLock){return E_FAIL;}
protected:
   HRESULT _CopyAggObject(IUnknown* pifSourceUnk, IUnknown*& pifTargetUnk)
   {
      // This method is a utility to aggregate a clone of an object
      CComQIPtr<IClassFactory, &IID_IClassFactory> ifElementClonable;
      HRESULT hr;
      ifElementClonable = pifSourceUnk;
      // Release the target unknown first
      if(pifTargetUnk)
         pifTargetUnk->Release();
      hr = ifElementClonable->CreateInstance(static_cast<T&>(*this).GetControllingUnknown(),
                                             IID_IUnknown,
                                             (void**)&pifTargetUnk);
      return hr;
   }
   virtual HRESULT Copy(T& source) = 0;
};


#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Norway Norway
ATL/COM, C#/.NET, IL, WCF

Comments and Discussions