//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 1999 // // File: tiedobj.h // // History: 08/28/1999 VivekJ Created //-------------------------------------------------------------------------- #ifndef TIEDOBJ_H #define TIEDOBJ_H #pragma once #include "conuistr.h" // for MMC_E_OBJECT_IS_GONE /************************************************************************ * This file provides base and template class support to encapsulate the * relationship between an object and a COM tearoff object that holds a pointer * to that object. * The object is referred to as the Tied object, since the COM object is * "tied" to it, and cannot do anything useful apart from it. * The COM object disables itself when the tied object is deleted. This * allows for automatic detection of dead states. An object can also "orphan" * all COM objects tied to it by calling the UnadviseAll method. * * An example of a tied object relationship is that of CAMCDoc and CMMCDocument. * CMMCDocument is derived from CTiedComObject, and CAMCDoc derives * from CTiedObject. CAMCDoc thus keeps a list of all COM object that are tied * to it, and notifies them when it is destroyed. Similarly, the COM objects notify * the tied object when they are destroyed, so that they are removed from the list. * * NOTE: the tied object does NOT addref the COM objects, it just keeps a list of them. * If it was to addref them, there would be all sorts of circular lifetime problems. * By ensuring that the tied object and the COM objects notify each other of their * own destruction, lifetime management is correctly handled. * * Use CTiedComObjectCreator::ScCreateAndConnect to create an instance of the * COM object and tie it to the tied object. * ************************************************************************/ class CTiedObject; class CTiedComObjectRoot; /*+-------------------------------------------------------------------------* * class CTiedComObjectRoot * * * PURPOSE: Base class for CTiedComObject * *+-------------------------------------------------------------------------*/ class CTiedComObjectRoot { public: virtual void Unadvise() = 0; // so that the tied object can inform that it is being deleted. }; /*+-------------------------------------------------------------------------* * class CTiedObject * * * PURPOSE: The base class that any object which has COM objects tied to * it should derive from. Provides methods to add a new COM object * to its list of tied objects, to remove a COM object from its * list, and to * *+-------------------------------------------------------------------------*/ class CTiedObject { typedef CTiedComObjectRoot * PTIEDCOMOBJECTROOT; typedef std::list CTiedComObjects; CTiedComObjects m_TiedComObjects; public: SC ScAddToList(CTiedComObjectRoot *p); void RemoveFromList(CTiedComObjectRoot *p); virtual ~CTiedObject(); protected: void UnadviseAll(); }; /*+-------------------------------------------------------------------------* * * CTiedObject::ScAddToList * * PURPOSE: Adds the COM object to the list of objects. Usually called soon * after constructing the COM object. * * PARAMETERS: * CTiedComObjectRoot * p : * * RETURNS: * inline SC * *+-------------------------------------------------------------------------*/ inline SC CTiedObject::ScAddToList(CTiedComObjectRoot *p) { DECLARE_SC (sc, _T("CTiedObject::ScAddToList")); if(!p) return (sc = E_INVALIDARG); m_TiedComObjects.push_back(p); return sc; } /*+-------------------------------------------------------------------------* * * CTiedObject::RemoveFromList * * PURPOSE: Removes the specfied COM object from the list of COM objects. * Usually called from the destructor of the COM object. * * PARAMETERS: * CTiedComObjectRoot * p : The COM object. * * RETURNS: * inline void * *+-------------------------------------------------------------------------*/ inline void CTiedObject::RemoveFromList(CTiedComObjectRoot *p) { CTiedComObjects::iterator iter; iter = std::find (m_TiedComObjects.begin(), m_TiedComObjects.end(), p); ASSERT(iter != m_TiedComObjects.end()); if(iter != m_TiedComObjects.end()) m_TiedComObjects.erase(iter); } inline void CTiedObject::UnadviseAll() { CTiedComObjects::iterator iter; for(iter = m_TiedComObjects.begin(); iter != m_TiedComObjects.end(); iter++) { (*iter)->Unadvise(); } } inline CTiedObject::~CTiedObject() { UnadviseAll(); } /*+-------------------------------------------------------------------------* * template class CTiedComObject * * * PURPOSE: The base class for COM objects that are tied to non-COM objects * for instance, CMMCDocument is tied to CAMCDoc - it delegates * all its methods to the tied object. * *+-------------------------------------------------------------------------*/ template class CTiedComObject : public CTiedComObjectRoot { friend class TiedObjectClass; public: CTiedComObject() : m_pT(NULL) {} virtual ~CTiedComObject(); void SetTiedObject(TiedObjectClass *pT); protected: // called by the COM methods to make sure that the tied object exists. SC ScGetTiedObject(TiedObjectClass*&pT); bool IsTied() { return m_pT != NULL; } void Unadvise(); private: TiedObjectClass *m_pT; }; /*+-------------------------------------------------------------------------* * * ~CTiedComObject * * PURPOSE: Destructor. Tells the tied object to remove this one from * its list of tied COM objects. * *+-------------------------------------------------------------------------*/ template CTiedComObject::~CTiedComObject() { if(m_pT != NULL) { m_pT->RemoveFromList(this); } } template void CTiedComObject::SetTiedObject(TiedObjectClass *pT) { ASSERT(pT != NULL); m_pT = pT; } template inline void CTiedComObject::Unadvise() { m_pT = NULL; } /*+-------------------------------------------------------------------------* * * CTiedComObject::ScGetTiedObject * * PURPOSE: Checks that a valid tied object pointer exists, and returns it. * * PARAMETERS: * TiedObjectClass** ppT : [OUT]: The object pointer * * RETURNS: * SC: MMC_E_OBJECT_IS_GONE if no valid pointer exists. *+-------------------------------------------------------------------------*/ template inline SC CTiedComObject::ScGetTiedObject(TiedObjectClass*&pT) { DECLARE_SC (sc, _T("CTiedComObject::ScGetTiedObject")); pT = m_pT; if(NULL == m_pT) return (sc = ScFromMMC(MMC_E_OBJECT_IS_GONE)); return (sc); } /*+-------------------------------------------------------------------------* * * ScCreateConnection * * PURPOSE: Creates a two-way connection between a COM object and its tied * object. * * PARAMETERS: * TiedComObjClass comObj : * TiedObjClass obj : * * RETURNS: * SC * *+-------------------------------------------------------------------------*/ template SC ScCreateConnection(TiedComObjClass &comObj, TiedObjClass &obj) { DECLARE_SC (sc, _T("ScCreateConnection")); sc = obj.ScAddToList(&comObj); if(sc) return (sc); comObj.SetTiedObject(&obj); return (sc); } /*+-------------------------------------------------------------------------* * CTiedComObjectCreator * * * PURPOSE: has a single static function, ScCreateAndConnect, which creates * an instance of the COM object (if the smart pointer supplied is * NULL) and connects it to the Tied object supplied. * *+-------------------------------------------------------------------------*/ template class CTiedComObjectCreator { public: template static SC ScCreateAndConnect(TiedObjClass &obj, SmartPointerClass &smartPointer) { DECLARE_SC(sc, TEXT("CTiedComObjectCreator::ScCreateAndConnect") ); // if the object has not yet been created, create it. if(smartPointer == NULL) { CComObject *pTiedComObject = NULL; sc = CComObject::CreateInstance(&pTiedComObject); if (sc) return (sc); if(!pTiedComObject) return (sc = E_UNEXPECTED); sc = ScCreateConnection(*pTiedComObject, obj); // create a link between the tied obj and tied com obj. if(sc) return sc; smartPointer = pTiedComObject; // This AddRef's it once. need to addref it for the client as well. } return sc; } }; #endif // TIEDOBJ_H