//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996. // // File: deflink.h // // Contents: Implementation of the standard link object // // Classes: CDefLink // // Functions: // // Author: // Craig Wittenberg (craigwi) 8/12/92 // // History: dd-mmm-yy Author Comment // 20-Feb-95 KentCe Buffered stream i/o. // 01-Feb-95 t-ScottH added Dump method to CDefLink // added DumpCDefLink API // added DLFlag to indicate if aggregated // (_DEBUG only) // 09-Jan-95 t-scotth changed VDATETHREAD to accept a pointer // 09-Jan-95 alexgo fixed a ton of link tracking bugs from // 16bit OLE. // 21-Nov-94 alexgo memory optimization // 28-Aug-94 alexgo added IsReallyRunning // 02-Aug-94 alexgo added object stabilization // 30-Jun-94 alexgo handles re-entrant shutdowns better // 31-May-94 alexgo now recovers from crashed servers // 06-May-94 alexgo made IsRunning work properly // 07-Mar-94 alexgo added call tracing // 03-Feb-94 alexgo fixed errors with SendOnLinkSrcChange // 11-Jan-94 alexgo added VDATEHEAP macros to every function // and method. Also fixed an aggregation bug, // allowing linking to work. // 22-Nov-93 alexgo removed overloaded GUID == // 15-Nov-93 alexgo 32bit port // // ChrisWe 11/09/93 Changed COleCache::Update to COleCache::UpdateCache, // which does the same thing without an indirect fuction call // srinik 09/11/92 Removed IOleCache implementation, as a result of // removing voncache.cpp, and moving IViewObject // implementation into olecache.cpp. // // SriniK 06/04/92 Fixed problems in IPersistStorage methods //-------------------------------------------------------------------------- #include #include #include #include "deflink.h" #include "defutil.h" #ifdef _DEBUG #include #endif // _DEBUG #ifdef _TRACKLINK_ #include #endif ASSERTDATA /* * IMPLEMENTATION of CDefLink */ //+------------------------------------------------------------------------- // // Member: CDefLink::Create // // Synopsis: Static function to create an instance of a link object // // Arguments: [pUnkOuter] -- Controlling unknown // // Returns: Pointer to IUnkown interface on DefLink // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- IUnknown *CDefLink::Create(IUnknown *pUnkOuter) { LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::Create(%p)\n", NULL /* this */, pUnkOuter)); // Validation check VDATEHEAP(); // Local variable CDefLink *pDefLink = NULL; IUnknown *pUnk = NULL; // Create DefLink pDefLink = new CDefLink(pUnkOuter); if(pDefLink) { // Make the ref count equal to 1 pDefLink->m_Unknown.AddRef(); // Create Ole Cache pDefLink->m_pCOleCache = new COleCache(pDefLink->m_pUnkOuter, CLSID_NULL); if(pDefLink->m_pCOleCache) { // Create Data Advise Cache if(CDataAdviseCache::CreateDataAdviseCache(&pDefLink->m_pDataAdvCache) == NOERROR) { pUnk = &pDefLink->m_Unknown; } } } if(pUnk == NULL) { // Something has gone wrong. Cleanup if(pDefLink) pDefLink->m_Unknown.Release(); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::Create(%p)\n", NULL /* this */, pUnk )); return pUnk; } //+------------------------------------------------------------------------- // // Member: CDefLink::CDefLink // // Synopsis: Constructor // // Arguments: [pUnkOuter] -- Controlling IUnknown // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten to use CRefExportCount //-------------------------------------------------------------------------- CDefLink::CDefLink(IUnknown *pUnkOuter) : CRefExportCount(pUnkOuter) { // Validation check VDATEHEAP(); // Initialize the controlling unknown if(!pUnkOuter) pUnkOuter = &m_Unknown; // Initialize member variables m_pUnkOuter = pUnkOuter; m_clsid = CLSID_NULL; m_dwUpdateOpt = OLEUPDATE_ALWAYS; m_pStg = NULL; m_flags = 0; m_dwObjFlags = 0; // Initialize sub objects m_pCOleCache = NULL; m_pCOAHolder = NULL; m_dwConnOle = 0; m_pDataAdvCache = NULL; m_dwConnTime = 0; // Initialize client site m_pAppClientSite = NULL; // Intialize delegates m_pUnkDelegate = NULL; m_pDataDelegate = NULL; m_pOleDelegate = NULL; m_pRODelegate = NULL; m_pOleItemContainerDelegate = NULL; // Initialize monikers m_pMonikerAbs = NULL; m_pMonikerRel = NULL; // zero out times memset(&m_ltChangeOfUpdate, 0, sizeof(m_ltChangeOfUpdate)); memset(&m_ltKnownUpToDate, 0, sizeof(m_ltKnownUpToDate)); memset(&m_rtUpdate, 0, sizeof(m_rtUpdate)); // Initialize member variables used for caching MiscStatus bits m_ContentSRVMSHResult = 0xFFFFFFFF; m_ContentSRVMSBits = 0; m_ContentREGMSHResult = 0xFFFFFFFF; m_ContentREGMSBits = 0; #ifdef _DEBUG if(pUnkOuter != &m_Unknown) m_flags |= DL_AGGREGATED; #endif // _DEBUG } //+------------------------------------------------------------------------- // // Member: CDefObject::CleanupFn, private, virtual // // Synopsis: This function is called by CRefExportCount when the object // enters zombie state // // Arguments: None // // History: dd-mmm-yy Author Comment // 28-Jan-07 Gopalk Creation //-------------------------------------------------------------------------- void CDefLink::CleanupFn(void) { LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::CleanupFn()\n", this)); // Validation check VDATEHEAP(); // Unbind source if neccessary UnbindSource(); // Release monikers if(m_pMonikerAbs) { m_pMonikerAbs->Release(); m_pMonikerAbs = NULL; } if(m_pMonikerRel) { m_pMonikerRel->Release(); m_pMonikerRel = NULL; } // Release sub objects if(m_pCOleCache) { m_pCOleCache->m_UnkPrivate.Release(); m_pCOleCache = NULL; } if(m_pCOAHolder) { m_pCOAHolder->Release(); m_pCOAHolder = NULL; } if(m_pDataAdvCache) { delete m_pDataAdvCache; m_pDataAdvCache = NULL; } // Release container side objects Win4Assert(!(m_flags & DL_LOCKED_CONTAINER)); if(m_pAppClientSite) { m_pAppClientSite->Release(); m_pAppClientSite = NULL; } if(m_pStg) { m_pStg->Release(); m_pStg = NULL; } // Update flags m_flags &= ~(DL_DIRTY_LINK); m_flags |= DL_CLEANEDUP; LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::CleanupFn()\n", this )); } //+------------------------------------------------------------------------- // // Function: DumpSzTime // // Synopsis: Prints the time in the FILETIME strucutre // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: NYI for 32bit // //-------------------------------------------------------------------------- #ifdef LINK_DEBUG INTERNAL_(void) DumpSzTime( LPOLESTR szMsg, FILETIME ft ) { VDATEHEAP(); WORD wDate, wTime; XCHAR szBuffer[24]; CoFileTimeToDosDateTime(&ft, &wDate, &wTime); int Day = ( wDate & 0x001F); int Month = ( (wDate>>5) & 0x000F); int Year = 1980 + ((wDate>>9) & 0x007F); int Sec = ( wTime & 0x001F); int Min = ( (wTime>>5) & 0x003F); int Hour = ( (wTime>>11) & 0x001F); wsprintf((LPOLESTR)szBuffer, " %02d:%02d:%02d on %02d/%02d/%04d\n", Hour, Min, Sec, Month, Day, Year); OutputDebugString(szMsg); OutputDebugString(szBuffer); } #else #define DumpSzTime(a,b) #endif //+------------------------------------------------------------------------- // // Member: CDefLink::SetUpdateTimes // // Synopsis: Internal function to save local and remote times for // link->IsUpToDate calculations // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: See notes below // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // The basic problem in calculating link IsUpToDate is that // the local clock may be different than the remote clock. // The solution is to keep track of both times on *both* // clocks (i.e. time now and time of change on both the local // and remote clocks). IsUpToDate is calculated by comparing // the differences between the times on the two clocks. This, // of course, assumes that both clocks equivalently measure // a second. // //-------------------------------------------------------------------------- INTERNAL CDefLink::SetUpdateTimes( void ) { VDATEHEAP(); FILETIME rtNewUpdate; LPMONIKER pmkAbs = NULL; HRESULT hresult; LPBINDCTX pbc = NULL; LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::SetUpdateTimes ( )\n", this )); //use the relative moniker if it exists and if the container has a //moniker if (NOERROR != GetAbsMkFromRel(&pmkAbs, NULL)) { //otherwise use the absolute moniker pmkAbs = m_pMonikerAbs; if (pmkAbs) { pmkAbs->AddRef(); } } if (pmkAbs == NULL) { hresult = E_UNSPEC; goto errRet; } hresult = CreateBindCtx( 0, &pbc ); if (hresult != NOERROR) { goto errRet; } //debugging aids DumpSzTime("SetUpdateTimes (going in): rtUpdate = ",m_rtUpdate); DumpSzTime("SetUpdateTimes (going in): ltKnownUpToDate = ", m_ltKnownUpToDate); DumpSzTime("SetUpdateTimes (going in): ltChangeOfUpdate = ", m_ltChangeOfUpdate); //get the current local time. CoFileTimeNow(&m_ltKnownUpToDate); //debugging aids DumpSzTime("SetUpdateTimes: time now is ",m_ltKnownUpToDate); //get the time of last change on the remote machine hresult = pmkAbs->GetTimeOfLastChange(pbc, NULL, &rtNewUpdate); if (hresult == NOERROR) { //if the remote time of last change is different than //what we previously stored as the remote time of last change, //then we update the remote time of last change and update //our local time of last change. //Since the IsUpToDate algorithm relies on taking the //differences between times on the same clock and comparing //those differences between machines, it is important that //the two times (local and remote) are *set* simulataneously. if ((rtNewUpdate.dwLowDateTime != m_rtUpdate.dwLowDateTime)|| (rtNewUpdate.dwHighDateTime != m_rtUpdate.dwHighDateTime)) { // rtUpdate value is changing m_rtUpdate = rtNewUpdate; //debugging aid DumpSzTime("rtUpdate changing to ", m_rtUpdate); m_ltChangeOfUpdate = m_ltKnownUpToDate; //debugging aid DumpSzTime("ltChangeOfUpdate changing to ", m_ltChangeOfUpdate); m_flags |= DL_DIRTY_LINK; } } errRet: //debugging aids DumpSzTime("SetUpdateTimes (going out): rtUpdate = ",m_rtUpdate); DumpSzTime("SetUpdateTimes (going out): ltKnownUpToDate = ", m_ltKnownUpToDate); DumpSzTime("SetUpdateTimes (going out): ltChangeOfUpdate = ", m_ltChangeOfUpdate); if (pmkAbs) { pmkAbs->Release(); } if (pbc) { pbc->Release(); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::SetUpdateTimes ( %lx )\n", this, hresult)); return(hresult); } //+------------------------------------------------------------------------- // // Member: CDefLink::UpdateUserClassID // // Synopsis: Grabs the class ID from the remote server (our delegate) // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // // update clsid from server if running; necessary because link source in // treatas case may decide to change the clsid (e.g., if features are used // which aren't supported by the old clsid). //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::UpdateUserClassID(void) { VDATEHEAP(); CLSID clsid; IOleObject *pOleDelegate; LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::UpdateUserClass ( )\n", this)); if( (pOleDelegate = GetOleDelegate()) != NULL && pOleDelegate->GetUserClassID(&clsid) == NOERROR) { m_clsid = clsid; } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::UpdateUserClass ( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::~CDefLink // // Synopsis: Destructor // // Arguments: None // // History: dd-mmm-yy Author Comment // 10-Jan-07 Gopalk Rewritten //-------------------------------------------------------------------------- CDefLink::~CDefLink (void) { VDATEHEAP(); Win4Assert(m_flags & DL_CLEANEDUP); Win4Assert(!(m_flags & DL_LOCKED_CONTAINER) ); Win4Assert(!(m_flags & DL_DIRTY_LINK)); Win4Assert(m_pMonikerAbs == NULL); Win4Assert(m_pMonikerRel == NULL); Win4Assert(m_pUnkDelegate == NULL); Win4Assert(m_pStg == NULL); Win4Assert(m_pCOleCache == NULL); Win4Assert(m_pCOAHolder == NULL); Win4Assert(m_pAppClientSite == NULL); } //+---------------------------------------------------------------------------- // // Member: // CDefLink::CPrivUnknown::AddRef, private // // Synopsis: // implements IUnknown::AddRef // // Arguments: // none // // Returns: // the parent object's reference count // // History: // Gopalk Rewritten Jan 28, 97 //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::CPrivUnknown::AddRef( void ) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CPrivUnknown::AddRef()\n", this)); // Validation check VDATEHEAP(); // Local variables CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_Unknown); ULONG cRefs; // Addref the parent object cRefs = pDefLink->SafeAddRef(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CPrivUnknown::AddRef(%lu)\n", this, cRefs)); return cRefs; } //+---------------------------------------------------------------------------- // // Member: // CDefLink::CPrivUnknown::Release, private // // Synopsis: // implements IUnknown::Release // // Arguments: // none // // Returns: // the parent object's reference count // // History: // Gopalk Rewritten Jan 28, 97 //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::CPrivUnknown::Release( void ) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CPrivUnknown::Release()\n", this)); // Validation check VDATEHEAP(); // Local variables CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_Unknown); ULONG cRefs; // Release parent object cRefs = pDefLink->SafeRelease(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CPrivUnknown::Release(%lu)\n", this, cRefs)); return cRefs; } //+------------------------------------------------------------------------- // // Member: CDefLink::CPrivUnknown::QueryInterface // // Synopsis: The link's private QI implementation // // Effects: // // Arguments: [iid] -- the requested interface ID // [ppv] -- where to put the pointer to the interface // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IUnknown // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 11-Jan-94 alexgo QI to the cache now queries to the cache's // private IUnknown implementation // 22-Nov-93 alexgo removed overloaded GUID == // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::CPrivUnknown::QueryInterface(REFIID iid, LPLPVOID ppv) { HRESULT hresult = NOERROR; CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_Unknown); VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CPrivUnknown::QueryInterface" " ( %p , %p )\n", pDefLink, iid, ppv)); if (IsEqualIID(iid, IID_IUnknown)) { *ppv = (void FAR *)&pDefLink->m_Unknown; //AddRef this object (not the aggregate) AddRef(); // hresult already set to NOERROR; goto errRtn; } else if (IsEqualIID(iid, IID_IOleObject)) { *ppv = (void FAR *) (IOleObject *)pDefLink; } else if (IsEqualIID(iid, IID_IDataObject)) { *ppv = (void FAR *) (IDataObject *)pDefLink; } else if (IsEqualIID(iid, IID_IOleLink)) { *ppv = (void FAR *) (IOleLink *)pDefLink; } else if (IsEqualIID(iid, IID_IRunnableObject)) { *ppv = (void FAR *) (IRunnableObject *)pDefLink; } else if (IsEqualIID(iid, IID_IViewObject) || IsEqualIID(iid, IID_IOleCache) || IsEqualIID(iid, IID_IViewObject2) || IsEqualIID(iid, IID_IOleCache2) ) { hresult = pDefLink->m_pCOleCache->m_UnkPrivate.QueryInterface(iid,ppv); goto errRtn; } else if (IsEqualIID(iid, IID_IPersistStorage) || IsEqualIID(iid, IID_IPersist)) { *ppv = (void FAR *) (IPersistStorage *)pDefLink; } else { *ppv = NULL; hresult = E_NOINTERFACE; goto errRtn; } pDefLink->m_pUnkOuter->AddRef(); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CPrivUnknown::QueryInterface" " ( %lx ) [ %p ]\n", pDefLink, hresult, *ppv)); return(hresult); } /* * IMPLEMENTATION of IUnknown methods */ //+------------------------------------------------------------------------- // // Member: CDefLink::QueryInterface // // Synopsis: QI's to the controlling IUnknown // // Effects: // // Arguments: [riid] -- the interface ID // [ppv] -- where to put it // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IUnknown // // Algorithm: // // History: dd-mmm-yy Author Comment // 15-Nov-94 alexgo author // // Notes: We do *not* need to stabilize this method as only // one outgoing call is made and we do not use the // 'this' pointer afterwards // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::QueryInterface( REFIID riid, void **ppv ) { HRESULT hresult; VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::QueryInterface ( %lx , " "%p )\n", this, riid, ppv)); Assert(m_pUnkOuter); hresult = m_pUnkOuter->QueryInterface(riid, ppv); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::QueryInterface ( %lx ) " "[ %p ]\n", this, hresult, *ppv)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::AddRef // // Synopsis: delegates AddRef to the controlling IUnknown // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IUnknown // // Algorithm: // // History: dd-mmm-yy Author Comment // 15-Nov-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::AddRef( void ) { ULONG crefs;; VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::AddRef ( )\n", this)); Assert(m_pUnkOuter); crefs = m_pUnkOuter->AddRef(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::AddRef ( %ld ) ", this, crefs)); return crefs; } //+------------------------------------------------------------------------- // // Member: CDefLink::Release // // Synopsis: delegates Release to the controlling IUnknown // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IUnknown // // Algorithm: // // History: dd-mmm-yy Author Comment // 15-Nov-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::Release( void ) { ULONG crefs;; VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Release ( )\n", this)); Assert(m_pUnkOuter); crefs = m_pUnkOuter->Release(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Release ( %ld )\n", this, crefs)); return crefs; } /* * IMPLEMENTATION of CDataObjectImpl methods */ //+------------------------------------------------------------------------- // // Member: CDefLink::GetDataDelegate // // Synopsis: Private method to get the IDataObject interface on // the server delegate for the link // // Effects: // // Arguments: void // // Requires: // // Returns: IDataObject * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // This function may return misleading information if the // server has died (i.e., you'll return a pointer to a cached // interface proxy). It is the responsibility of the caller // to handler server crashes. // //-------------------------------------------------------------------------- INTERNAL_(IDataObject *) CDefLink::GetDataDelegate(void) { VDATEHEAP(); IDataObject *pDataDelegate; LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::GetDataDelegate ( )\n", this )); if( !IsZombie() ) { DuCacheDelegate(&m_pUnkDelegate, IID_IDataObject, (LPLPVOID)&m_pDataDelegate, NULL); pDataDelegate = m_pDataDelegate; #if DBG == 1 if( m_pDataDelegate ) { Assert(m_pUnkDelegate); } #endif // DBG == 1 } else { pDataDelegate = NULL; } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::GetData" "Delegate ( %p )\n", this, pDataDelegate)); return pDataDelegate; } //+------------------------------------------------------------------------- // // Member: CDefLink::ReleaseDataDelegate // // Synopsis: Private method to release the IDataObject pointer on the // server to which we are linked // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::ReleaseDataDelegate(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::ReleaseDataDelegate ( )\n", this)); if (m_pDataDelegate) { SafeReleaseAndNULL((IUnknown **)&m_pDataDelegate); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::ReleaseData" "Delegate ( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::GetData // // Synopsis: Gets data from the server // // Effects: // // Arguments: [pfromatetcIn] -- the requested data format // [pmedium] -- where to put the data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Tries the cache first, then asks the server // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetData(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetData" " ( %p , %p )\n", this, pformatetcIn, pmedium)); VDATEPTROUT( pmedium, STGMEDIUM ); VDATEREADPTRIN( pformatetcIn, FORMATETC ); CRefStabilize stabilize(this); if( !HasValidLINDEX(pformatetcIn) ) { return DV_E_LINDEX; } pmedium->tymed = TYMED_NULL; pmedium->pUnkForRelease = NULL; Assert(m_pCOleCache != NULL); if( m_pCOleCache->m_Data.GetData(pformatetcIn, pmedium) != NOERROR) { if( GetDataDelegate() ) { hresult = m_pDataDelegate->GetData(pformatetcIn, pmedium); AssertOutStgmedium(hresult, pmedium); } else { hresult = OLE_E_NOTRUNNING; } } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetData" " ( %lx )\n", this, hresult)); return(hresult); } //+------------------------------------------------------------------------- // // Member: CDefLink::GetDataHere // // Synopsis: Retrieves data into the specified pmedium // // Effects: // // Arguments: [pformatetcIn] -- the requested format // [pmedium] -- where to put the data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Asks the cache first, then the server delegate // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetDataHere( LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetDataHere" " ( %p , %p )\n", this, pformatetcIn, pmedium)); VDATEREADPTRIN( pformatetcIn, FORMATETC ); VDATEREADPTRIN( pmedium, STGMEDIUM ); CRefStabilize stabilize(this); if( !HasValidLINDEX(pformatetcIn) ) { return DV_E_LINDEX; } Assert(m_pCOleCache != NULL); if( m_pCOleCache->m_Data.GetDataHere(pformatetcIn, pmedium) != NOERROR ) { if ( GetDataDelegate() ) { hresult = m_pDataDelegate->GetDataHere(pformatetcIn, pmedium); } else { hresult = OLE_E_NOTRUNNING; } } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetDataHere" " ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::QueryGetData // // Synopsis: Returns whether or not a GetData call for the requested // format would succeed. // // Effects: // // Arguments: [pformatetcIn] -- the requested data format // // Requires: // // Returns: HRESULT (NOERROR == GetData would succeed) // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Asks the cache first, then the server delegate (if the // cache call fails) // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::QueryGetData(LPFORMATETC pformatetcIn ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::QueryGetData" " ( %p )\n", this, pformatetcIn)); VDATEREADPTRIN( pformatetcIn, FORMATETC ); CRefStabilize stabilize(this); if( !HasValidLINDEX(pformatetcIn) ) { hresult = DV_E_LINDEX; goto errRtn; } Assert(m_pCOleCache != NULL); if( m_pCOleCache->m_Data.QueryGetData(pformatetcIn) != NOERROR ) { if ( GetDataDelegate() ) { hresult = m_pDataDelegate->QueryGetData(pformatetcIn); } else { hresult = OLE_E_NOTRUNNING; } } errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::QueryGetData" " ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetCanonicalFormatEtc // // Synopsis: Gets the cannonical (or preferred) data format for the // object (choosing from the given formats) // // Effects: // // Arguments: [pformatetc] -- the requested formats // [pformatetcOut] -- where to to put the canonical format // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Delegates to the server (if running) // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetCanonicalFormatEtc( LPFORMATETC pformatetc, LPFORMATETC pformatetcOut) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Get" "CanonicalFormatetc ( %p , %p )\n", this, pformatetc, pformatetcOut)); VDATEPTROUT( pformatetcOut, FORMATETC ); VDATEREADPTRIN( pformatetc, FORMATETC ); CRefStabilize stabilize(this); pformatetcOut->ptd = NULL; pformatetcOut->tymed = TYMED_NULL; if (!HasValidLINDEX(pformatetc)) { return DV_E_LINDEX; } if( GetDataDelegate() ) { hresult = m_pDataDelegate->GetCanonicalFormatEtc(pformatetc, pformatetcOut); } else { hresult = OLE_E_NOTRUNNING; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Get" "CanonicalFormatetc ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetData // // Synopsis: Stuffs data into an object (such as an icon) // // Effects: // // Arguments: [pformatetc] -- the format of the data // [pmedium] -- the data // [fRelease] -- if TRUE, then the data should be free'd // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Delegates to the server // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: The cache gets updated via a OnDataChange advise // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetData( LPFORMATETC pformatetc, LPSTGMEDIUM pmedium, BOOL fRelease) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetData" " ( %p , %p , %lu )\n", this, pformatetc, pmedium, fRelease)); VDATEREADPTRIN( pformatetc, FORMATETC ); VDATEREADPTRIN( pmedium, STGMEDIUM ); CRefStabilize stabilize(this); if( !HasValidLINDEX(pformatetc) ) { hresult = DV_E_LINDEX; goto errRtn; } if( GetDataDelegate() ) { hresult = m_pDataDelegate->SetData(pformatetc, pmedium, fRelease); } else { hresult = OLE_E_NOTRUNNING; } errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetData " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::EnumFormatEtc // // Synopsis: Enumerates the formats accepted for either GetData or SetData // // Effects: // // Arguments: [dwDirection] -- which formats (1 == GetData or // 2 == SetData) // [ppenumFormatEtc] -- where to put the enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Delegates to the server, if not available or the server // returns OLE_E_USEREG // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 30-May-94 alexgo now handles crashed servers // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::EnumFormatEtc( DWORD dwDirection, LPENUMFORMATETC *ppenumFormatEtc) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); VDATEPTROUT(ppenumFormatEtc, LPENUMFORMATETC); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::EnumFormat" "Etc ( %lu , %p )\n", this, dwDirection, ppenumFormatEtc)); CRefStabilize stabilize(this); if( GetDataDelegate() ) { hresult=m_pDataDelegate->EnumFormatEtc (dwDirection, ppenumFormatEtc); if( !GET_FROM_REGDB(hresult) ) { if( SUCCEEDED(hresult) || IsReallyRunning() ) { // if we failed, but the server is still // running, then go ahead and propogate the // error to the caller. // Note that IsReallyRunning will clean up our // state if the server had crashed. goto errRtn; } // FALL-THROUGH!! This is deliberate. If // the call failed and the server is no longer // running, then we assume the server has crashed. // We want to go ahead and fetch the information // from the registry. } } // Not running or object wants to use reg db anyway hresult = OleRegEnumFormatEtc(m_clsid, dwDirection, ppenumFormatEtc); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::EnumFormat" "Etc ( %lx ) [ %p ]\n", this, hresult, *ppenumFormatEtc)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::DAdvise // // Synopsis: Sets up a data advise connection // // Effects: // // Arguments: [pFormatetc] -- the data format to advise on // [advf] -- advise flags // [pAdvSink] -- whom to notify // [pdwConnection] -- where to put the advise connection ID // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Delegates to the advise cache // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::DAdvise(FORMATETC *pFormatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { HRESULT hresult; IDataObject * pDataDelegate; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::DAdvise " "( %p , %lu , %p , %p )\n", this, pFormatetc, advf, pAdvSink, pdwConnection )); VDATEREADPTRIN( pFormatetc, FORMATETC ); VDATEIFACE( pAdvSink ); if (pdwConnection) { VDATEPTROUT( pdwConnection, DWORD ); *pdwConnection = NULL; } CRefStabilize stabilize(this); if (!HasValidLINDEX(pFormatetc)) { hresult = DV_E_LINDEX; goto errRtn; } pDataDelegate = GetDataDelegate(); // NULL if not running hresult = m_pDataAdvCache->Advise(pDataDelegate, pFormatetc, advf, pAdvSink, pdwConnection); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::DAdvise " "( %lx ) [ %p ]\n", this, hresult, (pdwConnection) ? *pdwConnection : 0 )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::DUnadvise // // Synopsis: Destroys a data advise connection // // Effects: // // Arguments: [dwConnection] -- the connection to dismantle // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: delegates to the data advise cache // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::DUnadvise(DWORD dwConnection) { HRESULT hresult; IDataObject FAR* pDataDelegate; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::DUnadvise" " ( %lu )\n", this, dwConnection )); CRefStabilize stabilize(this); pDataDelegate = GetDataDelegate();// NULL if not running hresult = m_pDataAdvCache->Unadvise(pDataDelegate, dwConnection); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::DUnadvise ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::EnumDAdvise // // Synopsis: Enumerates the data advise connections to the object // // Effects: // // Arguments: [ppenumAdvise] -- where to put the enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: delegates to the data advise cache // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: This method does NOT have to be stabilized as we are // only going to be allocating memory for the enumerator // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::EnumDAdvise( LPENUMSTATDATA *ppenumAdvise ) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::EnumDAdvise" " ( %p )\n", this, ppenumAdvise)); hresult = m_pDataAdvCache->EnumAdvise (ppenumAdvise); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::EnumDAdvise" " ( %lx ) [ %p ]\n", this, hresult, *ppenumAdvise)); return hresult; } /* * IMPLEMENTATION of COleObjectImpl methods * */ //+------------------------------------------------------------------------- // // Member: CDefLink::GetOleDelegate // // Synopsis: Gets the IOleObject interface from the server, private method // // Effects: // // Arguments: [void] // // Requires: // // Returns: IOleObject * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handled the zombie state // 18-Nov-93 alexgo 32bit port // // Notes: // This function may return misleading information if the // server has died (i.e., you'll return a pointer to a cached // interface proxy). It is the responsibility of the caller // to handler server crashes. // // //-------------------------------------------------------------------------- INTERNAL_(IOleObject *) CDefLink::GetOleDelegate(void) { IOleObject *pOleDelegate; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::GetOle" "Delegate ( )\n", this )); if( !IsZombie() ) { DuCacheDelegate(&m_pUnkDelegate, IID_IOleObject, (LPLPVOID)&m_pOleDelegate, NULL); pOleDelegate = m_pOleDelegate; #if DBG == 1 if( m_pOleDelegate ) { Assert(m_pUnkDelegate); } #endif // DBG == 1 } else { pOleDelegate = NULL; } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::GetOle" "Delegate ( %p )\n", this, pOleDelegate)); return pOleDelegate; } //+------------------------------------------------------------------------- // // Member: CDefLink::ReleaseOleDelegate (private) // // Synopsis: Releases the IOleObject pointer from the server // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::ReleaseOleDelegate(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::ReleaseOle" "Delegate ( )\n", this )); if (m_pOleDelegate) { SafeReleaseAndNULL((IUnknown **)&m_pOleDelegate); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::ReleaseOle" "Delegate ( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::SetClientSite // // Synopsis: Sets the client site for the object // // Effects: // // Arguments: [pClientSite] -- the client site // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Stores the pointer; if the link is running, then // the LockContainer is called via the client site by // the DuSetClientSite helper function // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handled zombie state // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetClientSite( IOleClientSite *pClientSite ) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetClientSite" " ( %p )\n", this, pClientSite)); CRefStabilize stabilize(this); if( IsZombie() ) { // we don't want to change our state (i.e. reset the // the client site) if we're zombied, because it's possible // that we'd never be able to release the client site again // resulting in memory leaks or faults. hresult = CO_E_RELEASED; } else { BOOL fLockedContainer = (m_flags & DL_LOCKED_CONTAINER); // here we use whether or not we've been bound to the server // as the test for whether or not we're running (even though // the server may have crashed since we last bound). We do // this because DuSetClientSite will Unlock the old container // and lock the new if we're running. Thus, if we've ever been // running, we need to unlock the old container (even though // we may not currently be running). hresult = DuSetClientSite( m_pUnkDelegate ? TRUE : FALSE, pClientSite, &m_pAppClientSite, &fLockedContainer); if(fLockedContainer) { m_flags |= DL_LOCKED_CONTAINER; } else { m_flags &= ~(DL_LOCKED_CONTAINER); } } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetClientSite" " ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetClientSite // // Synopsis: Retrieves the stored client site pointer // // Effects: // // Arguments: [ppClientSite] -- where to put the client site pointer // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 22-Nov-93 alexgo inlined DuGetClientSite // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetClientSite( IOleClientSite **ppClientSite ) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetClientSite ( %p )\n", this, ppClientSite)); VDATEPTROUT(ppClientSite, IOleClientSite *); CRefStabilize stabilize(this); *ppClientSite = m_pAppClientSite; if( *ppClientSite ) { (*ppClientSite)->AddRef(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetClientSite" " ( %lx ) [ %p ] \n", this, NOERROR, *ppClientSite)); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetHostNames // // Synopsis: In principal, should set the names to be drawn for // the server object. Not relevant for links (link servers // are not a part of the document being edited). // // Effects: // // Arguments: [szContainerApp] -- the name of the container // [szContainerObj] -- the container's name for the object // // Requires: // // Returns: HRESULT (NOERROR currently) // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetHostNames (LPCOLESTR szContainerApp, LPCOLESTR szContainerObj) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetHostNames" " ( %p , %p )\n", this, szContainerApp, szContainerObj)); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetHostNames" " ( %lx )\n", this, NOERROR)); return NOERROR; // makes the embedded/link case more the same } //+------------------------------------------------------------------------- // // Member: CDefLink::Close // // Synopsis: Closes the object (in this case, just saves and unbinds the // link) // // Effects: // // Arguments: [dwFlags] -- clising flags (such as SAVEIFDIRTY) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Close( DWORD dwFlags ) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Close " "( %lu )\n", this, dwFlags)); CRefStabilize stabilize(this); if (dwFlags != OLECLOSE_NOSAVE) { AssertSz(dwFlags == OLECLOSE_SAVEIFDIRTY, "OLECLOSE_PROMPTSAVE is inappropriate\n"); if( IsDirty() == NOERROR ) { if( m_pAppClientSite ) { m_pAppClientSite->SaveObject(); } } } // just unbind. UnbindSource(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Close " "( %lx )\n", this, NOERROR)); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetMoniker // // Synopsis: Sets the moniker to the link object // // Effects: // // Arguments: [dwWhichMoniker] -- which moniker // [pmk] -- the new moniker // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: calls utility method UpdateRelMkFromAbsMk // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // // The moniker of the container is changing. // The next time we bind, we will try using the container moniker // composed with the relative moniker, and then, if that fails, // the absolute moniker, so there is no real need for us to // change these monikers. // // However, there are two cases when we know the absolute moniker // is the correct one, and we can take this opportunity to // recompute the relative moniker (which is changing because // the container moniker is changing). The advantage of this is // that GetDisplayName can return a better result in the interim // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetMoniker( DWORD dwWhichMoniker, LPMONIKER pmk ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetMoniker " "( %lx , %p )\n", this, dwWhichMoniker, pmk)); CRefStabilize stabilize(this); if( IsZombie() ) { hresult = CO_E_RELEASED; } else if (dwWhichMoniker == OLEWHICHMK_CONTAINER || dwWhichMoniker == OLEWHICHMK_OBJFULL) { if( m_pMonikerRel == NULL || m_pUnkDelegate) { UpdateRelMkFromAbsMk(pmk); } } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetMoniker" " ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetMoniker // // Synopsis: Retrieves the moniker for the object // // Effects: // // Arguments: [dwAssign] -- flags (such as wether a moniker should // be assigned to the object if none currently // exits) // [dwWhichMoniker]-- which moniker to get (relative/absolute/etc) // [ppmk] -- where to put a pointer to the moniker // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: asks the client site for the moniker // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetMoniker( DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppmk) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetMoniker " "( %lx , %lx , %p )\n", this, dwAssign, dwWhichMoniker, ppmk)); CRefStabilize stabilize(this); if( m_pAppClientSite ) { hresult = m_pAppClientSite->GetMoniker(dwAssign, dwWhichMoniker, ppmk); } else { // no client site *ppmk = NULL; hresult = E_UNSPEC; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetMoniker " "( %lx ) [ %p ]\n", this, hresult, *ppmk )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::InitFromData // // Synopsis: Initializes the object from the given data // // Effects: // // Arguments: [pDataObject] -- the data object to initialize from // [fCreation] -- TRUE indicates the object is being // created, FALSE a data transfer // [dwReserved] -- unused // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Delegates to the server // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::InitFromData( LPDATAOBJECT pDataObject, BOOL fCreation, DWORD dwReserved) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::InitFromData " "( %p , %lu , %lx )\n", this, pDataObject, fCreation, dwReserved)); CRefStabilize stabilize(this); if( GetOleDelegate() ) { hresult = m_pOleDelegate->InitFromData(pDataObject, fCreation, dwReserved); } else { hresult = OLE_E_NOTRUNNING; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::InitFromData " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetClipboardData // // Synopsis: Retrieves a data object that could be put on the clipboard // // Effects: // // Arguments: [dwReserved] -- unused // [ppDataObject] -- where to put the pointer to the data // object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Delegates to the server object // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetClipboardData( DWORD dwReserved, LPDATAOBJECT *ppDataObject) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetClipboard" "Data ( %lx , %p )\n", this, dwReserved, ppDataObject)); CRefStabilize stabilize(this); if ( GetOleDelegate() ) { hresult = m_pOleDelegate->GetClipboardData (dwReserved, ppDataObject); } else { hresult = OLE_E_NOTRUNNING; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetClipboard" "Data ( %lx ) [ %p ]\n", this, hresult, *ppDataObject)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::DoVerb // // Synopsis: Sends a verb to the object (such as Open) // // Effects: // // Arguments: [iVerb] -- the verb // [lpmsg] -- the window's message that caused the verb // [pActiveSite] -- the site where the object was activated // [lindex] -- unused currently // [hwndParent] -- the parent window of the container // [lprcPosRect] -- the rectange bounding the object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Binds to the server and then delegates the DoVerb call // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: If we had bound to the server and it crashed, we pretend it // was still running anyway for DoVerb (our call to BindToSource // will re-run it). Essentially, this algorithm "fixes" the // crash and restores the link's state. // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::DoVerb (LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect) { HRESULT hresult; BOOL bStartedNow = !m_pUnkDelegate; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::DoVerb " "( %ld , %ld , %p , %ld , %lx , %p )\n", this, iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect)); if( lpmsg ) { VDATEPTRIN( lpmsg, MSG ); } if( pActiveSite ) { VDATEIFACE( pActiveSite ); } if( lprcPosRect ) { VDATEPTRIN(lprcPosRect, RECT); } CRefStabilize stabilize(this); if( lindex != 0 && lindex != -1 ) { hresult = DV_E_LINDEX; goto errRtn; } // if we had crashed, BindToSource will reconnect us if ( FAILED(hresult = BindToSource(0, NULL)) ) { goto errRtn; } // we don't propogate hide to server; this (and other behavior) // favors the link object as serving an OLE container rather than // a general programmability client. This leave the link running, // possibly invisible. if (iVerb == OLEIVERB_HIDE) { hresult = NOERROR; goto errRtn; } if( GetOleDelegate() ) { hresult = m_pOleDelegate->DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect); } else { hresult = E_NOINTERFACE; } if (bStartedNow && FAILED(hresult)) { UnbindSource(); } errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::DoVerb " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::EnumVerbs // // Synopsis: Enumerate the verbs accepted by this object // // Effects: // // Arguments: [ppenumOleVerb] -- where to put the pointer to the enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: askes the server delegate. If not there or it returns // OLE_E_USEREG, then we get the info from the registration // database // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 30-May-94 alexgo now handles crashed servers // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::EnumVerbs( IEnumOLEVERB **ppenumOleVerb ) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::EnumVerbs " "( %p )\n", this, ppenumOleVerb)); CRefStabilize stabilize(this); if( GetOleDelegate() ) { hresult = m_pOleDelegate->EnumVerbs(ppenumOleVerb); if( !GET_FROM_REGDB(hresult) ) { if( SUCCEEDED(hresult) || IsReallyRunning() ) { // if we failed, but the server is still // running, then go ahead and propogate the // error to the caller. // Note that IsReallyRunning will clean up our // state if the server had crashed. goto errRtn; } // FALL-THROUGH!! This is deliberate. If // the call failed and the server is no longer // running, then we assume the server has crashed. // We want to go ahead and fetch the information // from the registry. } } // Not running or object wants to use reg db anyway hresult = OleRegEnumVerbs(m_clsid, ppenumOleVerb); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::EnumVerbs " "( %lx ) [ %p ]\n", this, hresult, *ppenumOleVerb)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetUserClassID // // Synopsis: Retrieves the class id of the linked object // // Effects: // // Arguments: [pClassID] -- where to put the class ID // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 18-Nov-93 alexgo 32bit port // // Notes: No need to stabilize as we make no outgoing calls // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetUserClassID(CLSID *pClassID) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetUserClass" "ID ( %p )\n", this, pClassID)); VDATEPTROUT(pClassID, CLSID); *pClassID = m_clsid; LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetUserClass" "ID ( %lx )\n", this, NOERROR )); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetUserType // // Synopsis: Retrieves a descriptive string about the server type // // Effects: // // Arguments: [dwFormOfType] -- indicates whether a short or long string // description is desired // [pszUserType] -- where to put the string // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Asks the server delegate, if that fails or the server // returns OLE_E_USEREG, then get the info from the registration // database // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 30-May-94 alexgo now handles crashed servers // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetUserType(DWORD dwFormOfType, LPOLESTR *ppszUserType) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetUserType " "( %lu , %p )\n", this, dwFormOfType, ppszUserType)); VDATEPTROUT(ppszUserType, LPOLESTR); *ppszUserType = NULL; CRefStabilize stabilize(this); if( GetOleDelegate() ) { hresult = m_pOleDelegate->GetUserType (dwFormOfType, ppszUserType); if( !GET_FROM_REGDB(hresult) ) { if( SUCCEEDED(hresult) || IsReallyRunning() ) { // if we failed, but the server is still // running, then go ahead and propogate the // error to the caller. // Note that IsReallyRunning will clean up our // state if the server had crashed. goto errRtn; } // FALL-THROUGH!! This is deliberate. If // the call failed and the server is no longer // running, then we assume the server has crashed. // We want to go ahead and fetch the information // from the registry. } } // Not running, or object wants to use reg db anyway // Consult reg db hresult = OleRegGetUserType(m_clsid, dwFormOfType, ppszUserType); // it is not appropriate to read from the stg since the storage is // owned by the link, not the link source (thus, the link source // never has the opportunity to call WriteFmtUserTypeStg on the // link object's storage). // We also do not need to bother storing the last known user // type because if we can get it for one particular clsid, we // should always be able to get it. If we can't get the user type, // then either we have never gotten a user type (and thus don't // have a "last known") or we've changed clsid's (in which case, // the last known user type would be wrong). errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetUserType " "( %lx ) [ %p ]\n", this, hresult, *ppszUserType)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::Update // // Synopsis: Updates the link (by calling IOleLink->Update) // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 18-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Update(void) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Update ( )\n", this )); CRefStabilize stabilize(this); hresult = Update(NULL); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Update ( " "%lx )\n", this, hresult)); return hresult; } //fudge value #define TwoSeconds 0x01312D00 //+------------------------------------------------------------------------- // // Member: CDefLink::IsUpToDate // // Synopsis: Determines whether or not a link is up-to-date // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT -- NOERROR == IsUpToDate, S_FALSE == out of date // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: The current time is compared with the last time known // up-to-date on *both* machines (the process of the container // and the process of the link). These time differences are // compared to determine whether the link is out-of-date. // See the UpdateTimes method. // // History: dd-mmm-yy Author Comment // 09-Jan-95 alexgo correctly answer IsUpToDate now; also // fixup monikers if needed. // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: The arithmetic calculations in this method assume // two's complement arithmetic and a high order sign bit // (true for most current machines) // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::IsUpToDate(void) { FILETIME rtTimeOfLastChange; LPMONIKER pmkAbs = NULL; IMoniker * pmkContainer = NULL; HRESULT hresult = NOERROR; LPBINDCTX pbc = NULL; FILETIME rtDiff; FILETIME ltDiff; FILETIME ftTemp; FILETIME ftNow; BOOL fHighBitSet; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::IsUpToDate " "( )\n", this )); CRefStabilize stabilize(this); if (m_dwUpdateOpt == OLEUPDATE_ALWAYS && IsRunning()) { // hresult == NOERROR from default initializer goto errRet; } // use the relative moniker if it exists and if the container // has a moniker if (NOERROR != GetAbsMkFromRel(&pmkAbs, &pmkContainer)) { // otherwise use the absolute moniker if (pmkAbs = m_pMonikerAbs) { pmkAbs->AddRef(); } } if (pmkAbs == NULL) { hresult = MK_E_UNAVAILABLE; goto errRet; } hresult = CreateBindCtx( 0, &pbc ); if (hresult != NOERROR) { goto errRet; } //get the remote time of last change hresult = pmkAbs->GetTimeOfLastChange(pbc, NULL, &rtTimeOfLastChange); if (hresult != NOERROR) { // if GetTimeOfLastChange failed, it's possible that the moniker // we constructed is bogus. Try again using the *real* absolute // moniker. We do this to mimic bind behaviour. The moniker // we use above is constructed from the relative moniker. In binding, // if the relative moniker fails, then we fall back to the last // known real absolute moniker. BOOL fSuccess = FALSE; if( m_pMonikerAbs ) { if (pmkAbs != m_pMonikerAbs) { // do this if we did the bind on the relative one.above hresult = m_pMonikerAbs->GetTimeOfLastChange(pbc, NULL, &rtTimeOfLastChange); if( hresult == NOERROR ) { fSuccess = TRUE; // hang onto the better absolute moniker pmkAbs->Release(); // releases the one we contructed // in GetAbsMkFromRel pmkAbs = m_pMonikerAbs; pmkAbs->AddRef(); // so the Release() down below } // doesn't hose us. } #ifdef _TRACKLINK_ if (!fSuccess) { // at this point we have tried either: relative then absolute OR // just absolute. We now should try the reduced absolute one. IMoniker *pmkReduced; EnableTracking(m_pMonikerAbs, OT_ENABLEREDUCE); hresult = m_pMonikerAbs->Reduce(pbc, MKRREDUCE_ALL, NULL, &pmkReduced); EnableTracking(m_pMonikerAbs, OT_DISABLEREDUCE); if (hresult == NOERROR) { hresult = pmkReduced->GetTimeOfLastChange(pbc, NULL, &rtTimeOfLastChange); if (hresult != NOERROR) { pmkReduced->Release(); } else { fSuccess = TRUE; pmkAbs->Release(); pmkAbs = pmkReduced; } } } #endif } if (!fSuccess) { hresult = MK_E_UNAVAILABLE; goto errRet; } } // once we get this far, we know that either 1. the relative moniker // is good, or 2. the absolute moniker is good. In any event, pmkAbs // now points to a (semi)-reasonable spot. (I say 'semi', because // even though GetTimeOfLastChange succeeded, we aren't guaranteed that // a Bind would be successful. // // check to see if we need to update the relative moniker (if we don't // already have one, don't bother.) It is also possible that the // absolute moniker is now bad. Use the known 'good' one and update // both monikers // we ignore the return code here; if this call fails, it is not // serious. // pmkContainer may be NULL if our container doesn't offer us one. if( pmkContainer ) { UpdateMksFromAbs(pmkContainer, pmkAbs); pmkContainer->Release(); } // compute rtDiff = max(0, rtTimeOfLastChange - rtUpdate) // possibly optimize with _fmemcopy // debugging aid DumpSzTime("IsUpToDate: rtTimeOfLastChange = ", rtTimeOfLastChange); // start rtDiff calculation rtDiff = rtTimeOfLastChange; // debugging aid DumpSzTime("IsUpToDate: rtUpdate = ", m_rtUpdate); // the following subtractions rely on two's complement if (m_rtUpdate.dwLowDateTime > rtDiff.dwLowDateTime) { //handle the carry rtDiff.dwHighDateTime = (DWORD)((LONG)rtDiff.dwHighDateTime - 1); } rtDiff.dwLowDateTime = (DWORD)((LONG)rtDiff.dwLowDateTime - (LONG)m_rtUpdate.dwLowDateTime); rtDiff.dwHighDateTime = (DWORD)((LONG)rtDiff.dwHighDateTime - (LONG)m_rtUpdate.dwHighDateTime); // if rtDiff < 0, say we are out of date. if ((LONG)rtDiff.dwHighDateTime < 0) { hresult = S_FALSE; goto errRet; } if (rtDiff.dwHighDateTime == 0 && rtDiff.dwLowDateTime == 0) { // no time difference. could be due to large clock ticks, // so we say we are up to date only if several seconds have // elapsed since last known update time. CoFileTimeNow( &ftNow ); ftTemp = m_ltKnownUpToDate; // This bit of logic may seem strange. All we want is // is to test the high bit in a portable fashion // between 32/64bit machines (so a constant isn't good) // As long as the sign bit is the high order bit, then // this trick will do fHighBitSet = ((LONG)ftTemp.dwLowDateTime < 0); ftTemp.dwLowDateTime += TwoSeconds; // if the high bit was set, and now it's zero, then we // had a carry if (fHighBitSet && ((LONG)ftTemp.dwLowDateTime >= 0)) { ftTemp.dwHighDateTime++; // handle the carry. } // compare times if ((ftNow.dwHighDateTime > ftTemp.dwHighDateTime) || ((ftNow.dwHighDateTime == ftTemp.dwHighDateTime) && (ftNow.dwLowDateTime > ftTemp.dwLowDateTime))) { hresult = NOERROR; } else { hresult = S_FALSE; } } else { // there was a time difference // compute ltDiff = max(0, m_ltKnownUpToDate - // m_ltChangeOfUpdate); // Actually, by this time we know rtDiff >= 0, so we can // simply compare ltDiff with rtDiff -- no need to compute // the max. ltDiff = m_ltKnownUpToDate; // debugging aid DumpSzTime("IsUpToDate: ltKnownUpToDate = ",ltDiff); DumpSzTime("IsUpToDate: ltChangeOfUpdate = ", m_ltChangeOfUpdate); // these calc's rely on two's complement. if (m_ltChangeOfUpdate.dwLowDateTime > ltDiff.dwLowDateTime) { // handle carry ltDiff.dwHighDateTime = (DWORD)((LONG)ltDiff.dwHighDateTime - 1); } ltDiff.dwLowDateTime = (DWORD)((LONG)ltDiff.dwLowDateTime - (LONG)m_ltChangeOfUpdate.dwLowDateTime); ltDiff.dwHighDateTime = (DWORD)((LONG)ltDiff.dwHighDateTime - (LONG)m_ltChangeOfUpdate.dwHighDateTime); // Now determine if rtDiff < ltDiff if (ltDiff.dwHighDateTime > rtDiff.dwHighDateTime) { hresult = NOERROR; } else if (ltDiff.dwHighDateTime == rtDiff.dwHighDateTime) { if (ltDiff.dwLowDateTime > rtDiff.dwLowDateTime) { hresult = NOERROR; } else { hresult = S_FALSE; } } else { hresult = S_FALSE; } } // all cases should have been handled by this point. Release // any resources grabbed. errRet: if (pmkAbs) { pmkAbs->Release(); } if (pbc) { pbc->Release(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::IsUpToDate " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetExtent // // Synopsis: Sets the drawing extents, not allowed for links // // Effects: // // Arguments: [dwDrawAspect] -- the drawing aspect // [lpsizel] -- the new extents // // Requires: // // Returns: E_UNSPEC (not allowed) // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32 bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetExtent(DWORD dwDrawAspect, LPSIZEL lpsizel) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetExtent " "( %lx , %p )\n", this, dwDrawAspect, lpsizel)); LEDebugOut((DEB_WARN, "Set Extent called for links, E_UNSPEC \n")); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetExtent " "( %lx )\n", this, E_UNSPEC)); return E_UNSPEC; // can't call this for a link } //+------------------------------------------------------------------------- // // Member: CDefLink::GetExtent // // Synopsis: Get's the size (extents) of the object // // Effects: // // Arguments: [dwDrawAspect] -- the drawing aspect // [lpsizel] -- where to put the extents // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Asks the server first, if not running or an error // then delegate to the cache // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetExtent( DWORD dwDrawAspect, LPSIZEL lpsizel) { HRESULT error = E_FAIL; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetExtent " "( %lx , %p )\n", this, dwDrawAspect, lpsizel)); VDATEPTROUT(lpsizel, SIZEL); CRefStabilize stabilize(this); lpsizel->cx = 0; lpsizel->cy = 0; // if server is running try to get extents from the server if( GetOleDelegate() ) { error = m_pOleDelegate->GetExtent(dwDrawAspect, lpsizel); } // if there is error or object is not running get extents from Cache if( error != NOERROR ) { Assert(m_pCOleCache != NULL); error = m_pCOleCache->GetExtent(dwDrawAspect, lpsizel); } // WordArt2.0 is giving negative extents!! if (SUCCEEDED(error)) { lpsizel->cx = LONG_ABS(lpsizel->cx); lpsizel->cy = LONG_ABS(lpsizel->cy); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetExtent " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::Advise // // Synopsis: Sets up an advise connection to the object for things like // Close, Save, etc. // // Effects: // // Arguments: [pAdvSink] -- whom to notify // [pdwConnection] -- where to put the connection ID // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Creates an OleAdvise holder (if one not already present // and then delegates to it) // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Advise " "( %p , %p )\n", this, pAdvSink, pdwConnection)); CRefStabilize stabilize(this); if( IsZombie() ) { hresult = CO_E_RELEASED; goto errRtn; } // if we haven't got an advise holder yet, allocate one if (m_pCOAHolder == NULL) { // allocate the advise holder m_pCOAHolder = new FAR COAHolder; // check to make sure we got one if (m_pCOAHolder == NULL) { hresult = E_OUTOFMEMORY; goto errRtn; } } // delegate the call to the advise holder hresult = m_pCOAHolder->Advise(pAdvSink, pdwConnection); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Advise " "( %lx ) [ %lu ]\n", this, hresult, (pdwConnection) ? *pdwConnection : 0 )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::Unadvise // // Synopsis: Removes an advise connection to the object // // Effects: // // Arguments: [dwConnection] -- the connection ID to remove // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Delegates to the OleAdvise holder (which was created // during the Advise--if it wasn't, then we are in a strange // state). // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Unadvise(DWORD dwConnection) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Unadvise " "( %lu )\n", this, dwConnection )); CRefStabilize stabilize(this); if (m_pCOAHolder == NULL) { // no one registered hresult = E_UNEXPECTED; } else { hresult = m_pCOAHolder->Unadvise(dwConnection); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Unadvise " "( %lx )\n", this, hresult )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::EnumAdvise // // Synopsis: Enumerates the advise connections on the object // // Effects: // // Arguments: [ppenumAdvise] -- where to put the pointer to the advise // enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Delegates to the advise holder // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: We do not need to stabilize this method as we only allocate // memory for hte advise enumerator // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::EnumAdvise( LPENUMSTATDATA *ppenumAdvise ) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::EnumAdvise " "( %p )\n", this, ppenumAdvise )); if (m_pCOAHolder == NULL) { // no one registered hresult = E_UNSPEC; } else { hresult = m_pCOAHolder->EnumAdvise(ppenumAdvise); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::EnumAdvise " "( %lx ) [ %p ]\n", this, hresult, *ppenumAdvise )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetMiscStatus // // Synopsis: Gets the miscellaneous status bits (such as // OLEMISC_ONLYICONIC) // // Effects: // // Arguments: [dwAspect] -- the drawing aspect // [pdwStatus] -- where to put the status bits // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: Asks the server first, if not running or if it returns // OLE_E_USEREG, then get the info from the registration // database. We always add link-specific bits regardless // of error conditions or what the server or regdb says. // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 30-May-94 alexgo now handles crashed servers // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetMiscStatus( DWORD dwAspect, DWORD *pdwStatus) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetMiscStatus(%lx, %p)\n", this, dwAspect, pdwStatus )); // Validation checks VDATEHEAP(); VDATETHREAD(this); VDATEPTROUT(pdwStatus, DWORD); // Local variables HRESULT hresult; // Stabilize CRefStabilize stabilize(this); // Initialize *pdwStatus = 0; hresult = OLE_S_USEREG; if(GetOleDelegate()) { // Check if MiscStatus bits have been cached for this instance // of server for DVASPECT_CONTENT if(m_ContentSRVMSHResult != 0xFFFFFFFF && dwAspect == DVASPECT_CONTENT) { *pdwStatus = m_ContentSRVMSBits; hresult = m_ContentSRVMSHResult; } else { // Ask the running server hresult = m_pOleDelegate->GetMiscStatus (dwAspect, pdwStatus); // Cache the server MiscStatus bits for DVASPECT_CONTENT if(dwAspect == DVASPECT_CONTENT) { m_ContentSRVMSBits = *pdwStatus; m_ContentSRVMSHResult = hresult; } } if(FAILED(hresult) && !GET_FROM_REGDB(hresult)) { // Check if server is really running BOOL fRunning = FALSE; // Note that IsReallyRunning will cleanup if the // server had crashed fRunning = IsReallyRunning(); Win4Assert(fRunning); // Hit the registry if the server crashed if(!fRunning) hresult = OLE_S_USEREG; } } // Check if we have to obtain MiscStatus bits from the registry if (GET_FROM_REGDB(hresult)) { // Check if registry MiscStatus bits have been cached for DVASPECT_CONTENT if(m_ContentREGMSHResult != 0xFFFFFFFF && dwAspect == DVASPECT_CONTENT) { *pdwStatus = m_ContentREGMSBits; hresult = m_ContentREGMSHResult; } else { // Hit the registry hresult = OleRegGetMiscStatus (m_clsid, dwAspect, pdwStatus); // Cache the registry MiscStatus bits for DVASPECT_CONTENT if(hresult == NOERROR && dwAspect == DVASPECT_CONTENT) { m_ContentREGMSBits = *pdwStatus; m_ContentREGMSHResult = hresult; } } } // Add link-specific bits (even if error) and return. // we add them even if an error because in order to get here, we // have to have instantiated this link object; thus, it is always // valid to say OLEMISC_ISLINKOBJECT, etc. (*pdwStatus) |= OLEMISC_CANTLINKINSIDE | OLEMISC_ISLINKOBJECT; LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetMiscStatus (%lx)[%lx]\n", this, hresult, *pdwStatus )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetColorScheme // // Synopsis: Sets the palette for the object; unused for links // // Effects: // // Arguments: [lpLogpal] -- the palette // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IOleObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetColorScheme(LPLOGPALETTE lpLogpal) { VDATEHEAP(); VDATETHREAD(this); // we ignore this always LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetColor" "Scheme ( %p )\n", this, lpLogpal)); LEDebugOut((DEB_WARN, "Link IOO:SetColorScheme called on a link\n")); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetColor" "Scheme ( %lx )\n", this, NOERROR)); return NOERROR; } /* * IMPLEMENTATION of CLinkImpl methods * */ //+------------------------------------------------------------------------- // // Member: CDefLink::BeginUpdates // // Synopsis: Private method to update the caches and then set the update // times // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::BeginUpdates(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::BeginUpdates ( )\n", this)); IDataObject FAR* pDataDelegate; if( pDataDelegate = GetDataDelegate() ) { // inform cache that we are running Assert(m_pCOleCache != NULL); m_pCOleCache->OnRun(pDataDelegate); // update only the automatic local caches from the newly // running src m_pCOleCache->UpdateCache(pDataDelegate, UPDFCACHE_NORMALCACHE, NULL); // we are an automatic link which is now up to date SetUpdateTimes(); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::BeginUpdates ( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::EndUpdates // // Synopsis: Calls OnStop on the cache // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::EndUpdates(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::EndUpdates ( )\n", this)); Assert(m_pCOleCache != NULL); m_pCOleCache->OnStop(); LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::EndUpdates ( )\n", this)); } //+------------------------------------------------------------------------- // // Member: CDefLink::UpdateAutoOnSave // // Synopsis: Updates caches that have been set with ADVFCACHE_ONSAVE // and sets the update times. Private method // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::UpdateAutoOnSave(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::UpdateAutoOnSave ( )\n", this)); // if m_pUnkDelegate is non-NULL, assume we are running // (we only want to take the hit of the rpc-call IsRunning // on external entry points. if (m_pUnkDelegate && m_dwUpdateOpt == OLEUPDATE_ALWAYS) { // update any cache which has ADVFCACHE_ONSAVE Assert(m_pCOleCache != NULL); //REVIEW32: I think SetUpdateTimes ought to be called //*after* the cache has been updated (that's what //BeginUpdates does as well) SetUpdateTimes(); m_pCOleCache->UpdateCache(GetDataDelegate(), UPDFCACHE_IFBLANKORONSAVECACHE, NULL); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::UpdateAutoOnSaves ( )\n", this)); } //+------------------------------------------------------------------------- // // Member: CDefLink::UpdateRelMkFromAbsMk (private) // // Synopsis: Creates a new relative moniker from the absolute moniker // // Effects: // // Arguments: [pmkContainer] -- the moniker to the container (may be NULL) // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Feb-94 alexgo check for NULL before SendOnLinkSrcChange // 19-Nov-93 alexgo 32bit port // // Notes: // // update relative moniker from abs; always release relative moniker; // may leave relative moniker NULL; doesn't return an error (because // no caller wanted it); dirties the link when we get rid of an // existing relative moniker or get a new one. // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::UpdateRelMkFromAbsMk(IMoniker *pmkContainer) { LPMONIKER pmkTemp = NULL; BOOL fNeedToAdvise = FALSE; HRESULT hresult; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::UpdateRelMkFromAbsMk ( %p )\n", this, pmkContainer )); if (m_pMonikerRel) { m_pMonikerRel->Release(); m_pMonikerRel = NULL; m_flags |= DL_DIRTY_LINK; // got rid on an existing moniker, now dirty fNeedToAdvise = TRUE; } // NOTE: m_pMonikerRel is now NULL and only set when if we get a // new one if (m_pMonikerAbs == NULL) { // no abs mk thus no relative one goto errRtn; } if (pmkContainer) { pmkTemp = pmkContainer; } else { hresult = GetMoniker( OLEGETMONIKER_ONLYIFTHERE, // it will be OLEWHICHMK_CONTAINER, &pmkTemp ); AssertOutPtrIface(hresult, pmkTemp); if (hresult != NOERROR) { // no container moniker, thus no relative one to it goto errRtn; } Assert(pmkTemp != NULL); } hresult = pmkTemp->RelativePathTo(m_pMonikerAbs, &m_pMonikerRel); AssertOutPtrIface(hresult, m_pMonikerRel); if (hresult != NOERROR) { // no relationship between container and absolute, thus no // relative if (m_pMonikerRel) { m_pMonikerRel->Release(); m_pMonikerRel = NULL; } } if (pmkContainer == NULL) { // new moniker was allocated and needs to be released pmkTemp->Release(); } if (m_pMonikerRel != NULL) { m_flags |= DL_DIRTY_LINK; // have new relative moniker; dirty fNeedToAdvise = TRUE; } // if there's an advise holder and we need to advise, send out // the change notification. if (fNeedToAdvise && m_pCOAHolder) { m_pCOAHolder->SendOnLinkSrcChange(m_pMonikerAbs); } errRtn: LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::UpdateRelMkFromAbsMk ( %p )\n", this, pmkContainer )); } //+------------------------------------------------------------------------- // // Member: CDefLink::UpdateMksFromAbs // // Synopsis: make a reasonable attempt to get valid rel && absolute // monikers // // Effects: // // Arguments: [pmkContainer] -- the moniker to the container // [pmkAbs] -- 'good' absolute moniker // // Requires: // // Returns: S_OK // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 09-Jan-95 alexgo author // // Notes: This function should only be used when we aren't 100% sure // of the validity of the moniker (i.e. after an IMoniker:: // TimeOfLastChange call). We do not do any error // recovery. Basically, the idea is 'try' to put us in a // more consistent state, but it that fails, it's OK (because // we'd basically have OLE 16bit behaviour). // //-------------------------------------------------------------------------- INTERNAL CDefLink::UpdateMksFromAbs( IMoniker *pmkContainer, IMoniker *pmkAbs ) { HRESULT hresult; IMoniker *pmktempRel; BOOL fNeedToUpdate = FALSE; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::UpdateMksFromAbs ( %p , %p )\n", this, pmkContainer, pmkAbs)); // try updating the relative moniker (if one exists). Basically, we // see if the relative moniker between pmkContainer and pmkAbs is // any different than the moniker we currently have. if( m_pMonikerRel ) { hresult = pmkContainer->RelativePathTo(pmkAbs, &pmktempRel); if( hresult == NOERROR ) { if( pmktempRel->IsEqual(m_pMonikerRel) == S_FALSE ) { // need to update the relative moniker. m_pMonikerRel->Release(); m_pMonikerRel = pmktempRel; m_pMonikerRel->AddRef(); // updated relative moniker, now dirty m_flags |= DL_DIRTY_LINK; fNeedToUpdate = TRUE; } } } // it is also possible that the absolute moniker is now bad. Use the // known 'good' one. if( m_pMonikerAbs && m_pMonikerAbs->IsEqual(pmkAbs) == S_FALSE ) { m_pMonikerAbs->Release(); m_pMonikerAbs = pmkAbs; m_pMonikerAbs->AddRef(); #ifdef _TRACKLINK_ EnableTracking(m_pMonikerAbs, OT_READTRACKINGINFO); #endif m_flags |= DL_DIRTY_LINK; fNeedToUpdate = TRUE; } // send out an advise to any interested parties if we changed our // monikers. Note that we do this even if just the relative moniker // changed because the moniker we give apps via GetSourceMoniker is // computed from the relative. if( fNeedToUpdate && m_pCOAHolder ) { m_pCOAHolder->SendOnLinkSrcChange(m_pMonikerAbs); } hresult = NOERROR; LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::UpdateMksFromAbs ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetAbsMkFromRel (private) // // Synopsis: Gets the absolute moniker from the relative moniker // stored in the link // // Effects: // // Arguments: [ppmkAbs] -- where to put the pointer to the moniker // [ppmkCont] -- where to put the container moniker // (may be NULL) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: calls IMoniker->ComposeWith on the moniker to the container // // History: dd-mmm-yy Author Comment // 09-Jan-95 alexgo added ppmkCont parameter // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL CDefLink::GetAbsMkFromRel(IMoniker **ppmkAbs, IMoniker **ppmkCont ) { LPMONIKER pmkContainer = NULL; HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::GetAbsMkFromRel ( %p , %p )\n", this, ppmkAbs, ppmkCont)); *ppmkAbs = NULL; if (m_pMonikerRel == NULL) { hresult = E_FAIL; goto errRtn; } hresult = GetMoniker( OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_CONTAINER, &pmkContainer ); AssertOutPtrIface(hresult, pmkContainer); if (hresult != NOERROR) { goto errRtn; } Assert(pmkContainer != NULL); hresult = pmkContainer->ComposeWith( m_pMonikerRel, FALSE, ppmkAbs ); if (pmkContainer) { if( ppmkCont ) { *ppmkCont = pmkContainer; // no need to AddRef, just implicitly // transfer ownership from pmkContainer } else { pmkContainer->Release(); } } errRtn: LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::GetAbsMkFromRel ( %lx ) " "[ %p ]\n", this, hresult, *ppmkAbs)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetUpdateOptions // // Synopsis: Sets the update options for the link (such as always or // manual) // // Effects: // // Arguments: [dwUpdateOpt] -- update options // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: If UPDATE_ALWAYS, then update the caches, otherwise // call OnStop (via EndUpdates) // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetUpdateOptions(DWORD dwUpdateOpt) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetUpdateOptions " "( %lx )\n", this, dwUpdateOpt)); CRefStabilize stabilize(this); if( IsZombie() ) { hresult = CO_E_RELEASED; goto errRtn; } switch (dwUpdateOpt) { case OLEUPDATE_ALWAYS: // make sure we are connected if running BindIfRunning(); // if we've already are in UPDATE_ALWAYS mode, // we don't need to reenter if (m_pUnkDelegate && m_dwUpdateOpt != OLEUPDATE_ALWAYS) { BeginUpdates(); } break; case OLEUPDATE_ONCALL: // if we aren't already in UPDATE_ONCALL mode, then // enter it. if (m_dwUpdateOpt != OLEUPDATE_ONCALL) { // inform cache that we are not running // (even if not running) EndUpdates(); } break; default: hresult = E_INVALIDARG; goto errRtn; } m_dwUpdateOpt = dwUpdateOpt; m_flags |= DL_DIRTY_LINK; hresult = NOERROR; errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetUpdateOptions " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetUpdateOptions // // Synopsis: Retrieves the current update mode for the link // // Effects: // // Arguments: [pdwUpdateOpt] -- wehre to put the update options // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetUpdateOptions(LPDWORD pdwUpdateOpt) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetUpdateOptions " "( %p )\n", this, pdwUpdateOpt)); *pdwUpdateOpt = m_dwUpdateOpt; LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetUpdateOptions " "( %lx ) [ %lx ]\n", this, NOERROR, *pdwUpdateOpt)); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetSourceMoniker // // Synopsis: Sets the link source moniker // // Effects: // // Arguments: [pmk] -- moniker to the new source (NULL used // for CancelLink operations) // [rclsid] -- the clsid of the source // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: Stores the new absolute moniker and creates a new relative // moniker from the absolute moniker // // History: dd-mmm-yy Author Comment // 09-Jan-95 alexgo added call to SetUpdateTimes to keep // internal state consistent // 21-Nov-94 alexgo memory optimization /// 03-Aug-94 alexgo stabilized and handle zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetSourceMoniker( LPMONIKER pmk, REFCLSID clsid ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetSourceMoniker " "( %p , %p )\n", this, pmk, clsid)); CRefStabilize stabilize(this); if( IsZombie() ) { hresult = CO_E_RELEASED; goto errRtn; } if (pmk) { VDATEIFACE(pmk); } UnbindSource(); // REVIEW: the following code appears in several places and should // be put in a separate routine: // SetBothMk(pmkSrcAbs, , // TRUE/*fBind*/); if (m_pMonikerAbs) { m_pMonikerAbs->Release(); } if ((m_pMonikerAbs = pmk) != NULL) { pmk->AddRef(); // // TRACKLINK // // -- use ITrackingMoniker to convert file moniker to // be tracking. // #ifdef _TRACKLINK_ EnableTracking(pmk, OT_READTRACKINGINFO); #endif } UpdateRelMkFromAbsMk(NULL); // to prevent error in BindToSource when clsid is different; i.e., we // shouldn't fail to connect (or require OLELINKBIND_EVENIFCLASSDIFF) // when the moniker is changed; i.e., we expect the class to change // so don't bother the programmer. m_clsid = CLSID_NULL; if (BindIfRunning() != NOERROR) { // server not running -> use clsid given (even if CLSID_NULL // and even // if no moniker) m_clsid = clsid; } errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetSourceMoniker " "( %lx )\n", this, hresult )); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetSourceMoniker // // Synopsis: Gets the moniker to the source // // Effects: // // Arguments: [ppmk] -- where to put the moniker // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: We first try to build a new absolute moniker from the // relative one, if that fails then we return the currently // stored absolute moniker. // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetSourceMoniker(LPMONIKER *ppmk) { LPMONIKER pmkAbs = NULL; // the absolute moniker constructed from the rel HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetSourceMoniker " "( %p )\n", this, ppmk)); CRefStabilize stabilize(this); GetAbsMkFromRel(&pmkAbs, NULL); if (pmkAbs) { *ppmk = pmkAbs; // no addref } else if (*ppmk = m_pMonikerAbs) { // we've been asked to give the pointer so we should AddRef() m_pMonikerAbs->AddRef(); } else { hresult = MK_E_UNAVAILABLE; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetSourceMoniker " "( %lx ) [ %p ]\n", this, hresult, *ppmk)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetSourceDisplayName // // Synopsis: Creates a moniker from the display name and calls // SetSourceMoniker, thus setting the moniker to the source // // Effects: // // Arguments: [lpszStatusText] -- the display name // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle the zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetSourceDisplayName( LPCOLESTR lpszStatusText) { HRESULT error; IBindCtx FAR* pbc; ULONG cchEaten; IMoniker FAR* pmk; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::SetSourceDisplay" "Name ( %p )\n", this, lpszStatusText)); CRefStabilize stabilize(this); if( IsZombie() ) { error = CO_E_RELEASED; goto errRtn; } if (error = CreateBindCtx(0,&pbc)) { goto errRtn; } error = MkParseDisplayName(pbc, (LPOLESTR)lpszStatusText, &cchEaten, &pmk); // In Daytona, we release the hidden server // must release this now so the (possibly) hidden server goes away. Verify(pbc->Release() == 0); if (error != NOERROR) { goto errRtn; } error = SetSourceMoniker(pmk, CLSID_NULL); pmk->Release(); // NOTE: we don't bind to the link source now since that would leave // the server running, but hidden. If the caller want to not start // the server twice it should parse the moniker itself, call // SetSourceMoniker and then BindToSource with the bind context of // the parse. errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::SetSourceDisplay" "Name ( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetSourceDisplayName // // Synopsis: Retrieves the source display name (such as that set with // SetSourceDisplayName) // // Effects: // // Arguments: [lplpszDisplayName] -- where to put a pointer to the // display name // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: Gets the absolute moniker and asks it for the display name // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetSourceDisplayName( LPOLESTR *lplpszDisplayName ) { HRESULT hresult; IBindCtx FAR* pbc; LPMONIKER pmk = NULL; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetSourceDisplay" "Name ( %p )\n", this, lplpszDisplayName)); CRefStabilize stabilize(this); *lplpszDisplayName = NULL; GetSourceMoniker(&pmk); if (pmk == NULL) { hresult = E_FAIL; goto errRtn; } if (hresult = CreateBindCtx(0,&pbc)) { goto errRtn; } hresult = pmk->GetDisplayName(pbc, NULL,lplpszDisplayName); AssertOutPtrParam(hresult, *lplpszDisplayName); Verify(pbc->Release() == 0); errRtn: if (pmk) { pmk->Release(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetSourceDisplay" "Name ( %lx ) [ %p ]\n", this, hresult, *lplpszDisplayName)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::BindToSource // // Synopsis: Binds to the link source // // Effects: // // Arguments: [bindflags] -- controls the binding (such as binding // even if the class ID is different) // [pbc] -- the bind context // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: First try binding with the relative moniker, failing that // then try the absolute moniker. Once bound, we set up // the advises and cache. // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilize and check for zombie case // 03-Feb-94 alexgo check for NULL before SendOnLinkSrcChange // 11-Jan-94 alexgo cast -1's to DWORD to fix compile // warning // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::BindToSource(DWORD bindflags, LPBINDCTX pbc) { HRESULT error = S_OK; IOleObject FAR* pOleDelegate; IDataObject FAR* pDataDelegate; IBindCtx FAR* pBcUse; CLSID clsid; LPMONIKER pmkAbs = NULL; LPMONIKER pmkHold = NULL; LPRUNNABLEOBJECT pRODelegate; LPOLEITEMCONTAINER pOleCont; BOOL fLockedContainer; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::BindToSource " "( %lx , %p )\n", this, bindflags, pbc)); CRefStabilize stabilize(this); // if we're zombied (e.g. in the middle of our destructor), we // don't really want to bind the source again ;-) if( IsZombie() ) { error = CO_E_RELEASED; goto logRtn; } // if we're running, then we're already bound if (IsReallyRunning()) { error = NOERROR; goto logRtn; } // nobody to bind to if (m_pMonikerAbs == NULL) { error = E_UNSPEC; goto logRtn; } if ((pBcUse = pbc) == NULL && (error = CreateBindCtx(0,&pBcUse)) != NOERROR) { goto errRtn; } { // // Rewritten BillMo 30 Jan 1995. // // Enumeration which is used to keep track of what stage of resolution // we were successful in. // enum { None, Relative, Ids, Absolute } ResolveSuccess = { None }; if (m_pMonikerRel != NULL) { error = GetAbsMkFromRel(&pmkAbs, NULL); if (error == NOERROR) { error = pmkAbs->BindToObject(pBcUse, NULL, IID_IUnknown, (LPVOID FAR *) &m_pUnkDelegate); if (error == NOERROR) { ResolveSuccess = Relative; } else { pmkAbs->Release(); pmkAbs = NULL; } } } if (ResolveSuccess == None && error != RPC_E_CALL_REJECTED) { error = m_pMonikerAbs->BindToObject(pBcUse, NULL, IID_IUnknown, (LPVOID FAR *)&m_pUnkDelegate); if (error == NOERROR) { ResolveSuccess = Absolute; (pmkAbs = m_pMonikerAbs)->AddRef(); } } #ifdef _TRACKLINK_ if (ResolveSuccess == None && error != RPC_E_CALL_REJECTED) { HRESULT error2; Assert(pmkAbs == NULL); EnableTracking(m_pMonikerAbs, OT_ENABLEREDUCE); error2 = m_pMonikerAbs->Reduce(pBcUse, MKRREDUCE_ALL, NULL, &pmkAbs); EnableTracking(m_pMonikerAbs, OT_DISABLEREDUCE); if (error2 == NOERROR) { if (pmkAbs != NULL) { error2 = pmkAbs->BindToObject(pBcUse, NULL, IID_IUnknown, (LPVOID FAR *)&m_pUnkDelegate); if (error2 == NOERROR) { ResolveSuccess = Ids; error = NOERROR; } else { pmkAbs->Release(); pmkAbs=NULL; } } // else error contains error from m_pMonikerAbs->BindToObject } else if (error2 == MK_S_REDUCED_TO_SELF) { if (pmkAbs != NULL) { pmkAbs->Release(); pmkAbs=NULL; } } // else error contains error from m_pMonikerAbs->BindToObject } #endif // // Update the link state // if (ResolveSuccess == None) goto errRtn; // Update the absolute moniker and send OnLinkSrcChange // (may update relative if this was an Ids resolve) if (ResolveSuccess == Relative || ResolveSuccess == Ids) { // binding succeeded with relative moniker or ids // Update the absolute one. // hold on to old absolute one pmkHold = m_pMonikerAbs; if (pmkHold) { pmkHold->AddRef(); } if (m_pMonikerAbs) { m_pMonikerAbs->Release(); m_pMonikerAbs = NULL; } if (ResolveSuccess == Relative) { GetAbsMkFromRel(&m_pMonikerAbs, NULL); } else { // Ids m_pMonikerAbs = pmkAbs; pmkAbs = NULL; UpdateRelMkFromAbsMk(NULL); } // // test to see if we had no abs moniker before OR the // one we had is different to the new one. // if (pmkHold == NULL || pmkHold->IsEqual(m_pMonikerAbs) != NOERROR ) { m_flags |= DL_DIRTY_LINK; // send change notification if the advise // holder present. if( m_pCOAHolder) { m_pCOAHolder->SendOnLinkSrcChange( m_pMonikerAbs); } } // have new absolute moniker; dirty if (pmkHold) { pmkHold->Release(); } } // Update the relative if (ResolveSuccess == Absolute) { UpdateRelMkFromAbsMk(NULL); } } #ifdef _TRACKLINK_ EnableTracking(m_pMonikerAbs, OT_READTRACKINGINFO); if (m_pMonikerAbs->IsDirty()) m_flags |= DL_DIRTY_LINK; #endif // NOTE: don't need to update the relative moniker when there isn't // one because we will do that at save time. // Successfully bound, Lock the Object. if ((pRODelegate = GetRODelegate()) != NULL) { // lock so invisible link source does not goes away. Assert(0 == (m_flags & DL_LOCKED_RUNNABLEOBJECT)); if (NOERROR == pRODelegate->LockRunning(TRUE, FALSE)) { m_flags |= DL_LOCKED_RUNNABLEOBJECT; } } else if( (pOleCont = GetOleItemContainerDelegate()) != NULL) { // have container in same process or handler which doesn't // support IRunnableObject. Assert(0 == (m_flags & DL_LOCKED_OLEITEMCONTAINER)); if (NOERROR == pOleCont->LockContainer(TRUE)) { m_flags |= DL_LOCKED_OLEITEMCONTAINER; } } // Lock the container fLockedContainer = m_flags & DL_LOCKED_CONTAINER; DuLockContainer(m_pAppClientSite, TRUE, &fLockedContainer ); if(fLockedContainer) { m_flags |= DL_LOCKED_CONTAINER; } else { m_flags &= ~DL_LOCKED_CONTAINER; } // By this point, we have successfully bound to the server. Now // we take care of misc administrative tasks. Assert(m_pUnkDelegate != NULL && "CDefLink::BindToSource expected valid m_pUnkDelegate"); // get class of link source; use NULL if it doesn't support IOleObject // or IOleObject::GetUserClassID returns an error. if ((pOleDelegate = GetOleDelegate()) == NULL || pOleDelegate->GetUserClassID(&clsid) != NOERROR) { clsid = CLSID_NULL; } // if different and no flag, release and return error. if ( IsEqualCLSID(m_clsid,CLSID_NULL)) { // m_clsid now NULL; link becomes dirty m_flags |= DL_DIRTY_LINK; } else if ( !IsEqualCLSID(clsid, m_clsid) ) { if ((bindflags & OLELINKBIND_EVENIFCLASSDIFF) == 0) { CLSID TreatAsCLSID; // Initialize error error = OLE_E_CLASSDIFF; // Check for TreatAs case if(CoGetTreatAsClass(m_clsid, &TreatAsCLSID) == S_OK) { // Check if the server clsid is same as TreatAs clsid if(IsEqualCLSID(clsid, TreatAsCLSID)) error = NOERROR; } if(error == OLE_E_CLASSDIFF) goto errRtn; } // clsid's do no match; link becomes dirty m_flags |= DL_DIRTY_LINK; } // use new class (even if null); dirty flag set above m_clsid = clsid; // it is possible that a re-entrant call unbound our source // thus making pOleDelegate invalid (since it's a local on // the stack LEWARN(pOleDelegate != m_pOleDelegate, "Unbind during IOL::BindToSource"); // we fetched m_pOleDelegate in the call to GetOleDelegate above. if( m_pOleDelegate != NULL) { // set single ole advise (we multiplex) if ((error = m_pOleDelegate->Advise( &m_AdviseSink, &m_dwConnOle)) != NOERROR) { goto errRtn; } } // Set up advise connections for data changes if( pDataDelegate = GetDataDelegate() ) { // setup wild card advise to get time change FORMATETC fetc; fetc.cfFormat = NULL; fetc.ptd = NULL; fetc.dwAspect = (DWORD)-1L; fetc.lindex = -1; fetc.tymed = (DWORD)-1L; if ((error = pDataDelegate->DAdvise(&fetc, ADVF_NODATA, &m_AdviseSink, &m_dwConnTime)) != NOERROR) { LEDebugOut((DEB_WARN, "WARNING: server does not " "support wild card advises\n")); goto errRtn; } // it is possible that a re-entrant call unbound our // link server, so we need to fetch the data object // pointer again LEWARN(pDataDelegate != m_pDataDelegate, "Unbind during IOL::BindToSource"); // this will set up data advise connections with // everybody in our data advise holder // (see dacache.cpp) error = m_pDataAdvCache->EnumAndAdvise( m_pDataDelegate, TRUE); if( error != NOERROR ) { goto errRtn; } } if (m_dwUpdateOpt == OLEUPDATE_ALWAYS) { // we inform the cache that we are running only if auto // update; otherwise, we are a manual link and will call // Update directly (which doesn't require OnRun to be called). BeginUpdates(); } // Our m_pUnkDelegate may have been released by an // OnClose advise that came in while we were setting up Advise // sinks. if (NULL == m_pUnkDelegate) { LEDebugOut((DEB_WARN, "CDefLink::BindToSource - " "m_pUnkDelegate was released " "(probably due to OnClose)")); error = MK_E_NOOBJECT; } errRtn: // free used resources if (pmkAbs) { pmkAbs->Release(); } if (error == NOERROR) { m_ContentSRVMSBits = 0; m_ContentSRVMSHResult = 0xFFFFFFFF; } else { UnbindSource(); // ClientSite will be unlocked in UnBindSource } if (pbc == NULL && pBcUse != NULL) { // created bind ctx locally Verify(pBcUse->Release() == 0); } #if DBG == 1 if( m_pUnkDelegate ) { Assert(error == NOERROR ); } else { Assert(error != NOERROR ); } #endif AssertOutPtrIface(error, m_pUnkDelegate); logRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::BindToSource " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::BindIfRunning // // Synopsis: Binds to the source server only if it is currently running // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT (NOERROR if connected) // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: Gets a good moniker to the source (first tries relative, // then tries absolute), ask it if the server is running, if // yes, then bind to it. // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilize and handle the zombie case // 19-Nov-93 alexgo 32bit port // // Notes: We may return NOERROR (connected) even if the server has // crashed unexpectedly // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::BindIfRunning(void) { HRESULT error; LPBC pbc; LPMONIKER pmkAbs = NULL; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::BindIfRunning " "( )\n", this )); CRefStabilize stabilize(this); // if we're zombied (e.g. in our destructor), then we don't want // to bind to the server! if( IsZombie() ) { error = CO_E_RELEASED; goto errRtn; } if (IsReallyRunning()) { error = NOERROR; goto errRtn; } // try getting an absolute moniker from the relative moniker first if (GetAbsMkFromRel(&pmkAbs, NULL) != NOERROR) { // can't get relative moniker; use abs if available if ((pmkAbs = m_pMonikerAbs) == NULL) { error = E_FAIL; goto errRtn; } pmkAbs->AddRef(); } // NOTE: we used to try both monikers, but this caused problems if // both would bind and the absolute one was running: we would bind // to the wrong one or force the relative one to be running. Now, // if we have a relative moniker, we try that one only; if we only // have an absolute moniker, we try that one; otherwise we fail. error = CreateBindCtx( 0, &pbc ); if (error != NOERROR) { goto errRtn; } if ((error = pmkAbs->IsRunning(pbc, NULL, NULL)) == NOERROR) { // abs is running, but rel is not; force BindToSource to use // the absolute moniker error = BindToSource(0, pbc); } // else return last error (from pmkAbs->IsRunning) pbc->Release(); errRtn: if (pmkAbs) { pmkAbs->Release(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::BindIfRunning " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::GetBoundSource // // Synopsis: Returns a pointer to the server delegate // // Effects: // // Arguments: [ppUnk] -- where to put the pointer to the server // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetBoundSource(LPUNKNOWN *ppUnk) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetBoundSource " "( %p )\n", this, ppUnk)); CRefStabilize stabilize(this); if (!IsReallyRunning()) { *ppUnk = NULL; hresult = E_FAIL; } else { (*ppUnk = m_pUnkDelegate)->AddRef(); hresult = NOERROR; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetBoundSource " "( %lx ) [ %p ]\n", this, hresult, *ppUnk)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::UnbindSource // // Synopsis: Unbinds the connection to the link source server // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: First unadvise all advise connections and then tickle // the container by lock/unlocking (to handle the silent // update case). Finally, we release all pointers to the // server. If we were the only folks connected, the server // will go away // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::UnbindSource(void) { LPDATAOBJECT pDataDelegate; LPOLEOBJECT pOleDelegate; LPRUNNABLEOBJECT pRODelegate; LPOLEITEMCONTAINER pOleCont; HRESULT hresult = NOERROR; IUnknown * pUnkDelegate; BOOL fLockedContainer; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::UnbindSource " "( )\n", this )); CRefStabilize stabilize(this); if (!m_pUnkDelegate) { // hresult == NOERROR goto errRtn; } // unadvise so if delegate stays around, we get the correct results if ((pOleDelegate = GetOleDelegate()) != NULL && m_dwConnOle != 0) { pOleDelegate->Unadvise(m_dwConnOle); m_dwConnOle = 0; } if( pDataDelegate = GetDataDelegate() ) { if (m_dwConnTime) { pDataDelegate->DUnadvise(m_dwConnTime); m_dwConnTime = 0; } // we can actually be re-entered here, so refetch the // IDO pointer from the server (if it's still around) LEWARN(pDataDelegate != m_pDataDelegate, "Unbind called within IOL::UnbindSource!"); // pDataDelegate should still be good, since we still have // an AddRef on it. Go through and do the unadvises again // anyway. // this will unadvise everybody registered in the data // advise holder m_pDataAdvCache->EnumAndAdvise( pDataDelegate, FALSE); } // inform cache that we are not running (even if OnRun was not called) EndUpdates(); if ( (m_flags & DL_LOCKED_RUNNABLEOBJECT) && ((pRODelegate = GetRODelegate()) != NULL) ) { // unlock so invisible link source goes away. // do it just before release delegates so above unadvises go m_flags &= ~DL_LOCKED_RUNNABLEOBJECT; pRODelegate->LockRunning(FALSE, TRUE); } if( (m_flags & DL_LOCKED_OLEITEMCONTAINER) && ((pOleCont = GetOleItemContainerDelegate()) != NULL) ) { // have container in same process or handler // Unlock to shutdown. m_flags &= ~DL_LOCKED_OLEITEMCONTAINER; pOleCont->LockContainer(FALSE); } Assert(0 == (m_flags & (DL_LOCKED_OLEITEMCONTAINER | DL_LOCKED_RUNNABLEOBJECT))); // release all of our pointers. ReleaseOleDelegate(); ReleaseDataDelegate(); ReleaseRODelegate(); ReleaseOleItemContainerDelegate(); // if we are the only consumer of this data, the following will stop // the server; set member to NULL first since release may cause this // object to be accessed again. pUnkDelegate = m_pUnkDelegate; LEWARN(pUnkDelegate == NULL, "Unbind called within IOL::UnbindSource"); m_pUnkDelegate = NULL; if( pUnkDelegate ) { pUnkDelegate->Release(); } // make sure unlocked if we locked it fLockedContainer = m_flags & DL_LOCKED_CONTAINER; m_flags &= ~DL_LOCKED_CONTAINER; DuLockContainer(m_pAppClientSite, FALSE, &fLockedContainer); AssertSz( hresult == NOERROR, "Bogus code modification, check error " "paths"); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::UnbindSource " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::Update // // Synopsis: Updates the link (fills cache, etc). // // Effects: // // Arguments: [pbc] -- the bind context // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IOleLink // // Algorithm: Bind to the server, then update the caches // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // As in IOO::DoVerb, we try to "fix" crashed servers by // staying bound to them if we rebind due to a crash. See // the Notes in IOO::DoVerb for more info. // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Update(LPBINDCTX pbc) { HRESULT error = NOERROR; BOOL bStartedNow = !m_pUnkDelegate; LPBINDCTX pBcUse; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Update ( %p )\n", this, pbc)); CRefStabilize stabilize(this); if (pbc) { pBcUse = pbc; } else { if (FAILED(error = CreateBindCtx( 0, &pBcUse ))) goto errBndCtx; } if (FAILED(error = BindToSource(0,pBcUse))) { goto errRtn; } // store the pUnk there to allow for // better optimization (if we didn't, file based link sources would // be launched multiple times since the moniker code does not put // them in the bind ctx (and probably shouldn't)). pBcUse->RegisterObjectBound(m_pUnkDelegate); SetUpdateTimes(); // ignore error. if (bStartedNow && (m_dwUpdateOpt == OLEUPDATE_ALWAYS)) { // if this is an auto-link and we ran it now, then all the // automatic caches would have been updated during the // running process. So, here we have to update only the // ADVFCACHE_ONSAVE caches. error= m_pCOleCache->UpdateCache( GetDataDelegate(), UPDFCACHE_IFBLANKORONSAVECACHE, NULL); } else { // This is a manual-link or it is an auto-link then it is // already running. In all these cases, all the caches need // to be updated. // (See bug 1616, to find out why we also have to update // the autmatic caches of auto-links). error= m_pCOleCache->UpdateCache( GetDataDelegate(), UPDFCACHE_ALLBUTNODATACACHE, NULL); } if (bStartedNow) { UnbindSource(); } errRtn: // if we created a bind context release it. if ( (NULL == pbc) && pBcUse) { pBcUse->Release(); } errBndCtx: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Update ( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::EnableTracking // // Synopsis: Calls ITrackingMoniker::EnableTracking on the passed moniker. // // Arguments: [pmk] -- moniker // // [ulFlags] // OT_READTRACKINGINFO -- read tracking info from source // OT_ENABLESAVE -- enable save of tracking info // OT_DISABLESAVE -- disable save of tracking info // // Algorithm: QI to ITrackingMoniker and call EnableTracking // // //-------------------------------------------------------------------------- #ifdef _TRACKLINK_ INTERNAL CDefLink::EnableTracking(IMoniker *pmk, ULONG ulFlags) { ITrackingMoniker *ptm = NULL; HRESULT hr = E_FAIL; if (pmk != NULL) { hr = pmk->QueryInterface(IID_ITrackingMoniker, (void**)&ptm); if (hr == S_OK) { hr = ptm->EnableTracking(NULL, ulFlags); LEDebugOut((DEB_TRACK, "CDefLink(%08X)::EnableTracking -- ptm->EnableTracking() = %08X\n", this, hr)); ptm->Release(); } else { LEDebugOut((DEB_TRACK, "CDefLink(%08X)::EnableTracking -- pmk->QI failed (%08X)\n", this, hr)); } } else { LEDebugOut((DEB_TRACK, "CDefLink(%08X)::EnableTracking -- pmk is NULL\n", this)); } return(hr); } #endif /* * IMPLEMENTATION of CROImpl methods */ //+------------------------------------------------------------------------- // // Member: CDefLink::GetRODelegate (private) // // Synopsis: gets the IRunnableObject from the interface // // Effects: // // Arguments: void // // Requires: // // Returns: IRunnableObject * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle the zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // This function may return misleading information if the // server has died (i.e., you'll return a pointer to a cached // interface proxy). It is the responsibility of the caller // to handler server crashes. // //-------------------------------------------------------------------------- INTERNAL_(IRunnableObject *) CDefLink::GetRODelegate(void) { IRunnableObject *pRODelegate; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::GetRODelegate " "( )\n", this )); // if we're zombied, then we don't want to QI for a new interface!! if( !IsZombie() ) { DuCacheDelegate(&(m_pUnkDelegate), IID_IRunnableObject, (LPLPVOID)&m_pRODelegate, NULL); pRODelegate = m_pRODelegate; #if DBG == 1 if( m_pRODelegate ) { Assert(m_pUnkDelegate); } #endif // DBG == 1 } else { pRODelegate = NULL; } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::GetRODelegate " "( %p )\n", this, pRODelegate)); return pRODelegate; } //+------------------------------------------------------------------------- // // Member: CDefLink::ReleaseRODelegate (private) // // Synopsis: Releases the IRO pointer to the server // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::ReleaseRODelegate(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::ReleaseRODelegate " "( )\n", this )); if (m_pRODelegate) { SafeReleaseAndNULL((IUnknown **)&m_pRODelegate); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::ReleaseRODelegate " "( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::GetRunningClass // // Synopsis: retrieves the class of the the default link // // Effects: // // Arguments: [lpClsid] -- where to put the class id // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IRunnableObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetRunningClass(LPCLSID lpClsid) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::GetRunningClass " "( %p )\n", this, lpClsid)); VDATEPTROUT(lpClsid, CLSID); *lpClsid = CLSID_StdOleLink; LEDebugOut((DEB_TRACE, "%p OUT CDefLink::GetRunningClass " "( %lx )\n", this, NOERROR)); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::Run // // Synopsis: Runs the object (binds to the server) // // Effects: // // Arguments: [pbc] -- the bind context // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IRunnableObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Run (LPBINDCTX pbc) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Run ( %p )\n", this, pbc )); CRefStabilize stabilize(this); hresult = BindToSource(0, pbc); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Run ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::IsRunning // // Synopsis: returns whether or not we've bound to the link server // // Effects: // // Arguments: none // // Requires: // // Returns: TRUE/FALSE // // Signals: // // Modifies: // // Derivation: IRunnableObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 27-Aug-94 alexgo author // // Notes: 16bit OLE only ever implemented this function. We // implement IsReallyRunning to allow links to recover // from a crashed server. // Unfortunately, we can't just move the IsReallyRunning // implementation into IsRunning. Many apps (like Project) // sit and spin calling OleIsRunning. IsReallyRunning also // will sometimes make outgoing RPC calls; with Project, // this causes a really cool infinite feedback loop. (the // outgoing call resets some state in Project and they decide // to call OleIsRunning again ;-) // //-------------------------------------------------------------------------- STDMETHODIMP_(BOOL) CDefLink::IsRunning (void) { BOOL fRet; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::IsRunning ( )\n", this )); if( m_pUnkDelegate ) { fRet = TRUE; } else { fRet = FALSE; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::IsRunning ( %d )\n", this, fRet)); return fRet; } //+------------------------------------------------------------------------- // // Member: CDefLink::IsReallyRunning // // Synopsis: Returns whether or not the link server is running // // Effects: // // Arguments: void // // Requires: // // Returns: BOOL -- TRUE == is running // // Signals: // // Modifies: // // Derivation: // // Algorithm: If we have not yet bound to the link server, then we // are not running. If we have, we would like to verify // that the server is still running (i.e. it hasn't crashed). // Thus, we ask the absolute moniker if we are still running. // (it will ping the rot, which will ping the server). // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 06-May-94 alexgo now calls IMoniker::IsRunning // 19-Nov-93 alexgo 32bit port // // Notes: // // //-------------------------------------------------------------------------- STDMETHODIMP_(BOOL) CDefLink::IsReallyRunning (void) { BOOL fRet = FALSE; LPBC pbc; HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::IsReallyRunning " "( )\n", this)); CRefStabilize stabilize(this); if( m_pUnkDelegate != NULL ) { if( CreateBindCtx( 0, &pbc ) != NOERROR ) { // this is a bit counter-intuitive. Basically, // the only error we'll get is OutOfMemory, but // we have no way of returning that error. // In order to mimimize the amount of work we need // to do (since we are in a low-mem state), just // return the current Running state (in this, // TRUE, since m_pUnkDelegate is not NULL fRet = TRUE; goto errRtn; } if( m_pMonikerAbs ) { hresult = m_pMonikerAbs->IsRunning(pbc, NULL, NULL); if( hresult != NOERROR ) { LEDebugOut((DEB_WARN, "WARNING: link server " "crashed or exited inappropriately " "( %lx ). Recovering...\n", hresult)); // wowsers, the server has crashed or gone // away even though we were bound to it. // let's go ahead and unbind. // don't worry about errors here; we're // just trying to cleanup as best we can UnbindSource(); } if( hresult == NOERROR ) { fRet = TRUE; } #if DBG == 1 else { Assert(fRet == FALSE); } #endif // DBG == 1 } #if DBG == 1 else { // we cannot have a pointer to the link server // if we don't have a moniker to it. If we get // to this state, something is hosed. AssertSz(0, "Pointer to link server without a moniker"); } #endif // DBG == 1 pbc->Release(); } errRtn: // do some checking here. If we say we're running, then // we should have a valid pUnkDelegate. Otherwise, it should // be NULL. Note, however, that is *is* possible for us // to unbind during this call even if we think we're running // // This occurs if during the call to IMoniker::IsRunning, we // get another call in which does an UnbindSource; thus // we'll think we're really running (from IMoniker::IsRunning), // but we've really unbound. // // We'll check for that condition here if( fRet == TRUE ) { if( m_pUnkDelegate == NULL ) { fRet = FALSE; LEDebugOut((DEB_WARN, "WARNING: Re-entrant Unbind" " during IsReallyRunning, should be OK\n")); } } #if DBG == 1 if( fRet == TRUE ) { Assert(m_pUnkDelegate != NULL); } else { Assert(m_pUnkDelegate == NULL); } #endif // DBG == 1 LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::IsReallyRunning" "( %lu )\n", this, fRet)); return fRet; } //+------------------------------------------------------------------------- // // Member: CDefLink::SetContainedObject // // Synopsis: Sets the object as an embedding, not relevant for links // // Effects: // // Arguments: [fContained] -- flag to toggle embedding status // // Requires: // // Returns: HRESULT (NOERROR) // // Signals: // // Modifies: // // Derivation: IRunnableObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // // note contained object; links don't care at present // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SetContainedObject(BOOL fContained) { VDATEHEAP(); VDATETHREAD(this); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::LockRunning // // Synopsis: Lock/Unlock the connection to the server. Does nothing // for links. // // Effects: // // Arguments: [fLock] -- flag to lock/unlock // [fLastUnlockCloses] -- close if its the last unlock // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IRunnableObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // Links have different liveness characteristics than embeddings. // We do not need to do anything for LockRunning for links. // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::LockRunning(BOOL fLock, BOOL fLastUnlockCloses) { VDATEHEAP(); VDATETHREAD(this); return NOERROR; } /* * IMPLEMENTATION of CPersistStgImpl methods */ //+------------------------------------------------------------------------- // // Member: CDefLink::GetClassID // // Synopsis: Retrieves the class id of the default link // // Effects: // // Arguments: [pClassID] -- where to put the class ID // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::GetClassID (CLSID *pClassID) { VDATEHEAP(); VDATETHREAD(this); *pClassID = CLSID_StdOleLink; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::IsDirty // // Synopsis: Returns TRUE if the linked object has changed // // Effects: // // Arguments: void // // Requires: // // Returns: NOERROR if dirty // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::IsDirty(void) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::IsDirty" " ( )\n", this)); if( (m_flags & DL_DIRTY_LINK) ) { hresult = NOERROR; } else { Assert(m_pCOleCache != NULL); hresult = m_pCOleCache->IsDirty(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::IsDirty " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::InitNew // // Synopsis: Initialize a new link object from the given storage // // Effects: // // Arguments: [pstg] -- the new storage for the link // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: Delegates to the cache // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle the zombie case // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::InitNew( IStorage *pstg) { HRESULT error; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::InitNew " "( %p )\n", this, pstg)); VDATEIFACE(pstg); CRefStabilize stabilize(this); if( IsZombie() ) { error = CO_E_RELEASED; } else if (m_pStg == NULL) { Assert(m_pCOleCache != NULL); if ((error = m_pCOleCache->InitNew(pstg)) == NOERROR) { m_flags |= DL_DIRTY_LINK; (m_pStg = pstg)->AddRef(); } } else { error = CO_E_ALREADYINITIALIZED; } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::InitNew " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::Load // // Synopsis: Initializes a link from data stored in the storage // // Effects: // // Arguments: [pstg] -- the storage with link data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: Read ole private data and set internal link information. // Then delegate to the cache to load presentation data, etc. // // History: dd-mmm-yy Author Comment // 20-Feb-94 KentCe Buffer internal stream i/o. // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle zombie case // 19-Nov-93 alexgo 32bit port // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Load(IStorage *pstg) { HRESULT error; LPMONIKER pmk = NULL; LPMONIKER pmkSrcAbs = NULL; LPMONIKER pmkSrcRel = NULL; CLSID clsid; DWORD dwOptUpdate; LPSTREAM pstm = NULL; DWORD dummy; ULONG cbRead; CStmBufRead StmRead; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CPeristStgImpl::Load " "( %p )\n", this, pstg )); VDATEIFACE(pstg); CRefStabilize stabilize(this); // if we're in a zombie state, we don't want to be reloading // our object!! if( IsZombie() ) { error = CO_E_RELEASED; goto logRtn; } if (m_pStg) { error = CO_E_ALREADYINITIALIZED; goto logRtn; } //read link data from the storage error = ReadOleStg (pstg, &m_dwObjFlags, &dwOptUpdate, NULL, &pmk, &pstm); if (error == NOERROR) { // set the update options. SetUpdateOptions (dwOptUpdate); // we can get the moniker from container, so no need to // remeber this if (pmk) { pmk->Release(); } Assert (pstm != NULL); // Read relative source moniker. Write NULL for the time being if ((error = ReadMonikerStm (pstm, &pmkSrcRel)) != NOERROR) { goto errRtn; } // Read absolute source moniker; stored in link below if ((error = ReadMonikerStm (pstm, &pmkSrcAbs)) != NOERROR) { goto errRtn; } // // Buffer the read i/o from the stream. // StmRead.Init(pstm); // Read -1 followed by the last class name if ((error = ReadM1ClassStmBuf(StmRead, &clsid)) != NOERROR) { goto errRtn; } // Read the last display name // Right now, this is always an empty string LPOLESTR pstr = NULL; if ((error = ReadStringStream (StmRead, &pstr)) != NOERROR) { goto errRtn; } if (pstr) { LEDebugOut((DEB_ERROR, "ERROR!: Link user type " "string found, unexpected\n")); PubMemFree(pstr); } if ((error = StmRead.Read(&dummy, sizeof(DWORD))) != NOERROR) { goto errRtn; } if ((error = StmRead.Read(&(m_ltChangeOfUpdate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } if ((error = StmRead.Read(&(m_ltKnownUpToDate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } if ((error = StmRead.Read(&(m_rtUpdate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } // // TRACKLINK // // - tell the absolute moniker to convert itself // into a tracking moniker using ITrackingMoniker:: // EnableTracking. (The composite // moniker should pass this on to each of // its contained monikers.) // - if the moniker is already a tracking file moniker // ignore the request. // #ifdef _TRACKLINK_ EnableTracking(pmkSrcAbs, OT_READTRACKINGINFO); #endif m_pMonikerRel = pmkSrcRel; if (pmkSrcRel) { pmkSrcRel->AddRef(); } m_pMonikerAbs = pmkSrcAbs; if (pmkSrcAbs) { pmkSrcAbs->AddRef(); } m_clsid = clsid; // just loaded; thus not dirty m_flags &= ~(DL_DIRTY_LINK); } else if( error == STG_E_FILENOTFOUND) { // It's OK if the Ole stream doesn't exist. error = NOERROR; } else { return error; } // now load cache from pstg Assert(m_pCOleCache != NULL); if(m_dwObjFlags & OBJFLAGS_CACHEEMPTY) { error = m_pCOleCache->Load(pstg, TRUE); if(error != NOERROR) goto errRtn; } else { error = m_pCOleCache->Load(pstg); if(error != NOERROR) goto errRtn; } (m_pStg = pstg)->AddRef(); errRtn: StmRead.Release(); if (pmkSrcAbs) { pmkSrcAbs->Release(); } if (pmkSrcRel) { pmkSrcRel->Release(); } if (pstm) { pstm->Release(); } #ifdef REVIEW if (error == NOERROR && m_pAppClientSite) { BindIfRunning(); } #endif logRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Load " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::Save // // Synopsis: Saves the link the given storage // // Effects: // // Arguments: [pstgSave] -- the storage to save into // [fSameAsLoad] -- FALSE indicates SaveAs operation // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: Writes private ole data (such as the clsid, monikers, // and update times) and the presentations stored in the // cache to the given storage // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized // 19-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::Save( IStorage *pstgSave, BOOL fSameAsLoad) { HRESULT error = NOERROR; LPSTREAM pstm = NULL; DWORD cbWritten; CStmBufWrite StmWrite; DWORD ObjFlags = 0; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CPeristStgImpl::Save " "( %p , %lu )\n", this, pstgSave, fSameAsLoad )); VDATEIFACE(pstgSave); CRefStabilize stabilize(this); // update any cache which has ADVFCACHE_ONSAVE UpdateAutoOnSave(); if(fSameAsLoad && !(m_flags & DL_DIRTY_LINK) && (!!(m_dwObjFlags & OBJFLAGS_CACHEEMPTY)==m_pCOleCache->IsEmpty())) { // The storage is not a new one (so we don't need to // initialize our private data) and the link is not // dirty, so we just need to delegate to the cache goto LSaveCache; } // Obtain cache status if(m_pCOleCache->IsEmpty()) ObjFlags |= OBJFLAGS_CACHEEMPTY; // assign object moniker (used by WriteOleStg); we don't save this // moniker since WriteOleStg gets it again; we also don't care if // this failes as we don't want a failure here to prevent the link // from being saved; the assignment might fail if some container has // yet to be saved to a file. REIVEW PERF: we could pass this mk to // WriteOleStg. We don't get the moniker for !fSameAsLoad since the // relative moniker is not correct for the new stg and it causes the // container to do work in a case for which it might not be prepared. IMoniker * pMkObjRel; if (fSameAsLoad && GetMoniker( OLEGETMONIKER_FORCEASSIGN, OLEWHICHMK_OBJREL, &pMkObjRel) == NOERROR) { pMkObjRel->Release(); } if ((error = WriteOleStgEx(pstgSave,(IOleObject *)this, NULL, ObjFlags, &pstm)) != NOERROR) { goto logRtn; } Assert(pstm != NULL); // Write relative source moniker. // if it is NULL, try to compute it now. We may be saving a file for // the first time, so the container now has a moniker for the first // time. if (m_pMonikerRel == NULL || m_pUnkDelegate) { // if the link is connected, we know that the absolute // moniker is correct -- it was updated at bind time if // necessary. If the link container moniker has changed // (file/saveas) then we can exploit this opportunity to // straighten things out and improve our link tracking // since we know which of the two monikers is correct. UpdateRelMkFromAbsMk(NULL); } if ((error = WriteMonikerStm (pstm, m_pMonikerRel)) != NOERROR) { goto errRtn; } #ifdef _TRACKLINK_ EnableTracking(m_pMonikerAbs, OT_ENABLESAVE); #endif // Write absolute source moniker. error = WriteMonikerStm (pstm, m_pMonikerAbs); #ifdef _TRACKLINK_ EnableTracking(m_pMonikerAbs, OT_DISABLESAVE); #endif if (error != NOERROR) goto errRtn; // // // StmWrite.Init(pstm); // write last class name UpdateUserClassID(); if ((error = WriteM1ClassStmBuf(StmWrite, m_clsid)) != NOERROR) { goto errRtn; } // write last display name, should be NULL if the moniker's are // non-NULL. For the time being this is always NULL. if ((error = StmWrite.WriteLong(0)) != NOERROR) { goto errRtn; } // write -1 as the end marker, so that if we want to extend // the file formats (ex: adding network name) it will be easier. if ((error = StmWrite.WriteLong(-1)) != NOERROR) { goto errRtn; } if ((error = StmWrite.Write(&(m_ltChangeOfUpdate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } if ((error = StmWrite.Write(&(m_ltKnownUpToDate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } if ((error = StmWrite.Write(&(m_rtUpdate), sizeof(FILETIME))) != NOERROR) { goto errRtn; } if ((error = StmWrite.Flush()) != NOERROR) { goto errRtn; } if (!fSameAsLoad) { // Copy link tracking info static const LPOLESTR lpszLinkTracker = OLESTR("\1OleLink"); pstgSave->DestroyElement(lpszLinkTracker); if (m_pStg) { // copy link tracking info, if one existed, // ignore error m_pStg->MoveElementTo(lpszLinkTracker, pstgSave, lpszLinkTracker, STGMOVE_COPY); } } LSaveCache: // last, save cache Assert(m_pCOleCache != NULL); error = m_pCOleCache->Save(pstgSave, fSameAsLoad); errRtn: StmWrite.Release(); if (pstm) { pstm->Release(); } if (error == NOERROR) { m_flags |= DL_NO_SCRIBBLE_MODE; if( fSameAsLoad ) { m_flags |= DL_SAME_AS_LOAD; m_dwObjFlags |= ObjFlags; } else { m_flags &= ~(DL_SAME_AS_LOAD); } } logRtn: LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Save " "( %lx )\n", this, error )); return error; } //+------------------------------------------------------------------------- // // Member: CDefLink::SaveCompleted // // Synopsis: Called once the save is completed (for all objects in the // container). Clear the dirty flag and update the storage // that we hand onto. // // Effects: // // Arguments: [pstgNew] -- the new default storage for the object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 03-Aug-94 alexgo stabilized and handle zombie case // 20-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::SaveCompleted( IStorage *pstgNew) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::Save" "Completed ( %p )\n", this, pstgNew )); if (pstgNew) { VDATEIFACE(pstgNew); } // don't hang on to the new storage if we're in a zombie state! if (pstgNew && !IsZombie() ) { if (m_pStg) { m_pStg->Release(); } m_pStg = pstgNew; pstgNew->AddRef(); } // REVIEW: do we send on save??? if( (m_flags & DL_SAME_AS_LOAD) || pstgNew) { if( (m_flags & DL_NO_SCRIBBLE_MODE) ) { m_flags &= ~(DL_DIRTY_LINK); } m_flags &= ~(DL_SAME_AS_LOAD); } // let the cache know that the save is completed, so that it can clear // its dirty flag in Save or SaveAs situation, as well as remember the // new storage pointer if a new one is given Assert(m_pCOleCache != NULL); m_pCOleCache->SaveCompleted(pstgNew); m_flags &= ~(DL_NO_SCRIBBLE_MODE); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::Save" "Completed ( %lx )\n", this, NOERROR )); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CDefLink::HandsOffStorage // // Synopsis: Releases all pointers to the storage (useful for low-mem // situations) // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT (NOERROR) // // Signals: // // Modifies: // // Derivation: IPersistStorage // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 20-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::HandsOffStorage(void) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CDefLink::HandsOff" "Storage ( )\n", this )); if (m_pStg) { m_pStg->Release(); m_pStg = NULL; } Assert(m_pCOleCache != NULL); m_pCOleCache->HandsOffStorage(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::HandsOff" "Storage ( %lx )\n", this, NOERROR)); return NOERROR; } /* * * IMPLEMENTATION of CAdvSinkImpl methods * */ // // NOTE: Advise Sink is a nested object of Default Link that is exported // for achieving some of its functionality. This introduces some lifetime // complications. Can its lifetime be controlled by the server object to // which it exported its Advise Sink? Ideally, only its client should // control its lifetime alone, but it should also honor the ref counts // placed on it by the server object by entering into a zombie state // to prevent AV's on the incoming calls to the Advise Sink. All needed // logic is coded into the new class "CRefExportCount" which manages // the ref and export counts in a thread safe manner and invokes // appropriate methods during the object's lifetime. Any server objects // that export nested objects to other server objects should derive from // "CRefExportCount" class and call its methods to manage their lifetime // as exemplified in this Default Link implementation. // // Gopalk Jan 28, 97 // //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::QueryInterface // // Synopsis: Only supports IUnknown and IAdviseSink // // Arguments: [iid] -- Interface requested // [ppvObj] -- pointer to hold returned interface // // Returns: HRESULT // // History: dd-mmm-yy Author Comment // 10-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP CDefLink::CAdvSinkImpl::QueryInterface(REFIID iid, void **ppv) { LEDebugOut((DEB_TRACE,"%p _IN CDefLink::CAdvSinkImpl::QueryInterface()\n", this)); // Validation check VDATEHEAP(); // Local variables HRESULT hresult = NOERROR; if(IsValidPtrOut(ppv, sizeof(void *))) { if(IsEqualIID(iid, IID_IUnknown)) { *ppv = (void *)(IUnknown *) this; } else if(IsEqualIID(iid, IID_IAdviseSink)) { *ppv = (void *)(IAdviseSink *) this; } else { *ppv = NULL; hresult = E_NOINTERFACE; } } else hresult = E_INVALIDARG; if(hresult == NOERROR) ((IUnknown *) *ppv)->AddRef(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::QueryInterface(%lx)\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::AddRef // // Synopsis: Increments export count // // Returns: ULONG; New export count // // History: dd-mmm-yy Author Comment // 10-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::CAdvSinkImpl::AddRef( void ) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::AddRef()\n", this)); // Validation check VDATEHEAP(); // Local variables CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); ULONG cExportCount; // Increment export count cExportCount = pDefLink->IncrementExportCount(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::AddRef(%ld)\n", this, cExportCount)); return cExportCount; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::Release // // Synopsis: Decerement export count and potentially destroy the Link // // Returns: ULONG; New export count // // History: dd-mmm-yy Author Comment // 10-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDefLink::CAdvSinkImpl::Release ( void ) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::Release()\n", this)); // Validation check VDATEHEAP(); // Local variables CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); ULONG cExportCount; // Decrement export count. cExportCount = pDefLink->DecrementExportCount(); LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::Release(%ld)\n", this, cExportCount)); return cExportCount; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::OnDataChange // // Synopsis: Updates time of change // // Arguments: [pFormatetc] -- Data format that changed // [pStgmed] -- New data // // Returns: void // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(void) CDefLink::CAdvSinkImpl::OnDataChange(FORMATETC *pFormatetc, STGMEDIUM *pStgmed) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::OnDataChange(%p, %p)\n", this, pFormatetc, pStgmed)); // Validation checks VDATEHEAP(); // Local variable CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); // Assert that the wild card advise prompted this notification Win4Assert(pFormatetc->cfFormat == NULL && pFormatetc->ptd == NULL && pFormatetc->dwAspect == -1 && pFormatetc->tymed == -1); Win4Assert(pStgmed->tymed == TYMED_NULL); // Update time of change for automatic links if(!pDefLink->IsZombie() && pDefLink->m_dwUpdateOpt==OLEUPDATE_ALWAYS) { // Stabilize CRefStabilize stabilize(pDefLink); pDefLink->SetUpdateTimes(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::OnDataChange()\n", this)); return; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::OnViewChange // // Synopsis: Called when the view changes; should never be called for // links // // Effects: // // Arguments: [aspects] -- drawing aspect // [lindex] -- unused // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: IAdviseSink // // Algorithm: // // History: dd-mmm-yy Author Comment // 21-Nov-94 alexgo memory optimization // 20-Nov-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(void) CDefLink::CAdvSinkImpl::OnViewChange (DWORD aspects, LONG lindex) { VDATEHEAP(); Assert(FALSE); // never received } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::OnRename // // Synopsis: Updates internal monikers to the source object. Turns around // and informs its advise sinks // // Arguments: [pmk] -- New moniker name // // Returns: void // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(void) CDefLink::CAdvSinkImpl::OnRename(IMoniker *pmk) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::OnRename(%p)\n", this, pmk)); // Validation check VDATEHEAP(); // Local variable CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); if(!pDefLink->IsZombie()) { CRefStabilize stabilize(pDefLink); // Release old absolute moniker if(pDefLink->m_pMonikerAbs) pDefLink->m_pMonikerAbs->Release(); // Remember the new moniker pDefLink->m_pMonikerAbs = pmk; if(pmk) { // AddRef the new moniker pmk->AddRef(); // // Enable tracking on the new moniker // (this will get a new shellink if neccessary.) // #ifdef _TRACKLINK_ pDefLink->EnableTracking(pmk, OT_READTRACKINGINFO); #endif } // Update relative moniker from the new absolute moniker pDefLink->UpdateRelMkFromAbsMk(NULL); // Name of the link source changed. This has no bearing on the // name of the link object itself. if(pDefLink->m_pCOAHolder) pDefLink->m_pCOAHolder->SendOnLinkSrcChange(pmk); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::OnRename()\n", this)); return; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::OnSave // // Synopsis: Updates cache and turns around and informs its advise sinks // // Arguments: None // // Returns: void // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(void) CDefLink::CAdvSinkImpl::OnSave() { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::OnSave()\n", this)); // Validation check VDATEHEAP(); // Local variable CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); if(!pDefLink->IsZombie()) { // Stabilize CRefStabilize stabilize(pDefLink); // Turn around and send notification if(pDefLink->m_pCOAHolder) pDefLink->m_pCOAHolder->SendOnSave(); // Update presentations cached with ADVFCACHE_ONSAVE pDefLink->UpdateAutoOnSave(); // Update clsid pDefLink->UpdateUserClassID(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::OnSave()\n", this)); return; } //+------------------------------------------------------------------------- // // Member: CDefLink::CAdvSinkImpl::OnClose // // Synopsis: Updates time of change and turns around and informs its // advise sinks. // // Arguments: void // // Returns: void // // History: dd-mmm-yy Author Comment // 28-Jan-96 Gopalk Rewritten //-------------------------------------------------------------------------- STDMETHODIMP_(void) CDefLink::CAdvSinkImpl::OnClose(void) { LEDebugOut((DEB_TRACE, "%p _IN CDefLink::CAdvSinkImpl::OnClose()\n", this)); // Validation check VDATEHEAP(); // Local variable CDefLink *pDefLink = GETPPARENT(this, CDefLink, m_AdviseSink); if(!pDefLink->IsZombie()) { // Stabilize CRefStabilize stabilize(pDefLink); // Update time of change if(pDefLink->m_dwUpdateOpt == OLEUPDATE_ALWAYS ) pDefLink->SetUpdateTimes(); // Turn around and send notification if(pDefLink->m_pCOAHolder) pDefLink->m_pCOAHolder->SendOnClose(); // To be safe, unbind source pDefLink->UnbindSource(); } LEDebugOut((DEB_TRACE, "%p OUT CDefLink::CAdvSinkImpl::OnClose()\n", this)); return; } /* * IMPLEMENTATION of OleItemContainer methods */ //+------------------------------------------------------------------------- // // Member: CDefLink::GetOleItemContainerDelegate (private) // // Synopsis: gets the IOleItemContainer from the interface // // Effects: // // Arguments: void // // Requires: // // Returns: IOleItemContainer * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // This function may return misleading information if the // server has died (i.e., you'll return a pointer to a cached // interface proxy). It is the responsibility of the caller // to handler server crashes. // //-------------------------------------------------------------------------- INTERNAL_(IOleItemContainer *) CDefLink::GetOleItemContainerDelegate(void) { IOleItemContainer *pOleItemContainerDelegate; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::GetOleItemContainerDelegate " "( )\n", this )); // if we're zombied, then we don't want to QI for a new interface!! if(!IsZombie()) { DuCacheDelegate(&(m_pUnkDelegate), IID_IOleItemContainer, (LPLPVOID)&m_pOleItemContainerDelegate, NULL); pOleItemContainerDelegate = m_pOleItemContainerDelegate; #if DBG == 1 if( m_pOleItemContainerDelegate ) { Assert(m_pUnkDelegate); } #endif // DBG == 1 } else { m_pOleItemContainerDelegate = NULL; } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::GetOleItemContainerDelegate " "( %p )\n", this, pOleItemContainerDelegate)); return m_pOleItemContainerDelegate; } //+------------------------------------------------------------------------- // // Member: CDefLink::ReleaseOleItemContainerDelegate (private) // // Synopsis: Releases the OleItemContainer pointer to the server // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- INTERNAL_(void) CDefLink::ReleaseOleItemContainerDelegate(void) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CDefLink::ReleaseOleItemContainerDelegate " "( )\n", this )); if (m_pOleItemContainerDelegate) { SafeReleaseAndNULL((IUnknown **)&m_pOleItemContainerDelegate); } LEDebugOut((DEB_ITRACE, "%p OUT CDefLink::ReleaseOleItemContainerDelegate " "( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CDefLink::Dump, public (_DEBUG only) // // Synopsis: return a string containing the contents of the data members // // Effects: // // Arguments: [ppszDump] - an out pointer to a null terminated character array // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: [ppszDump] - argument // // Derivation: // // Algorithm: use dbgstream to create a string containing information on the // content of data structures // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG HRESULT CDefLink::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; char *pszPrefix; char *pszCSafeRefCount; char *pszCThreadCheck; char *pszCLSID; char *pszCOleCache; char *pszCOAHolder; char *pszDAC; char *pszFILETIME; char *pszMonikerDisplayName; dbgstream dstrPrefix; dbgstream dstrDump(5000); // determine prefix of newlines if ( ulFlag & DEB_VERBOSE ) { dstrPrefix << this << " _VB "; } // determine indentation prefix for all newlines for (i = 0; i < nIndentLevel; i++) { dstrPrefix << DUMPTAB; } pszPrefix = dstrPrefix.str(); // put data members in stream pszCThreadCheck = DumpCThreadCheck((CThreadCheck *)this, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "CThreadCheck:" << endl; dstrDump << pszCThreadCheck; CoTaskMemFree(pszCThreadCheck); // only vtable pointers (plus we don't get the right address in debugger extensions) // dstrDump << pszPrefix << "&IUnknown = " << &m_Unknown << endl; // dstrDump << pszPrefix << "&IAdviseSink = " << &m_AdviseSink << endl; dstrDump << pszPrefix << "Link flags = "; if (m_flags & DL_SAME_AS_LOAD) { dstrDump << "DL_SAME_AS_LOAD "; } if (m_flags & DL_NO_SCRIBBLE_MODE) { dstrDump << "DL_NO_SCRIBBLE_MODE "; } if (m_flags & DL_DIRTY_LINK) { dstrDump << "DL_DIRTY_LINK "; } if (m_flags & DL_LOCKED_CONTAINER) { dstrDump << "DL_LOCKED_CONTAINER "; } // if none of the flags are set... if ( !( (m_flags & DL_SAME_AS_LOAD) | (m_flags & DL_LOCKED_CONTAINER) | (m_flags & DL_NO_SCRIBBLE_MODE) | (m_flags & DL_DIRTY_LINK))) { dstrDump << "No FLAGS SET!"; } dstrDump << "(" << LongToPtr(m_flags) << ")" << endl; dstrDump << pszPrefix << "pIOleObject Delegate = " << m_pOleDelegate << endl; dstrDump << pszPrefix << "pIDataObject Delegate = " << m_pDataDelegate << endl; dstrDump << pszPrefix << "pIRunnableObject Delegate = " << m_pRODelegate << endl; dstrDump << pszPrefix << "No. of Refs. on Link = " << m_cRefsOnLink << endl; dstrDump << pszPrefix << "pIUnknown pUnkOuter = "; if (m_flags & DL_AGGREGATED) { dstrDump << "AGGREGATED (" << m_pUnkOuter << ")" << endl; } else { dstrDump << "NO AGGREGATION (" << m_pUnkOuter << ")" << endl; } dstrDump << pszPrefix << "pIMoniker Absolute = " << m_pMonikerAbs << endl; if (m_pMonikerAbs != NULL) { pszMonikerDisplayName = DumpMonikerDisplayName(m_pMonikerAbs); dstrDump << pszPrefix << "pIMoniker Absolute = "; dstrDump << pszMonikerDisplayName; dstrDump << "( " << m_pMonikerAbs << " )" << endl; CoTaskMemFree(pszMonikerDisplayName); } else { dstrDump << pszPrefix << "pIMoniker Absolute = NULL or unable to resolve" << endl; } if (m_pMonikerRel != NULL) { pszMonikerDisplayName = DumpMonikerDisplayName(m_pMonikerRel); dstrDump << pszPrefix << "pIMoniker Relative = "; dstrDump << pszMonikerDisplayName; dstrDump << "( " << m_pMonikerRel << " )" << endl; CoTaskMemFree(pszMonikerDisplayName); } else { dstrDump << pszPrefix << "pIMoniker Absolute = NULL or unable to resolve" << endl; } dstrDump << pszPrefix << "pIUnknown Delegate = " << m_pUnkDelegate << endl; dstrDump << pszPrefix << "OLEUPDATE flags = "; if (m_dwUpdateOpt & OLEUPDATE_ALWAYS) { dstrDump << "OLEUPDATE_ALWAYS "; } else if (m_dwUpdateOpt & OLEUPDATE_ONCALL) { dstrDump << "OLEUPDATE_ONCALL "; } else { dstrDump << "No FLAGS SET!"; } dstrDump << "(" << LongToPtr(m_flags) << ")" << endl; pszCLSID = DumpCLSID(m_clsid); dstrDump << pszPrefix << "Last known CLSID of link = " << pszCLSID << endl; CoTaskMemFree(pszCLSID); dstrDump << pszPrefix << "pIStorage = " << m_pStg << endl; if (m_pCOleCache != NULL) { // pszCOleCache = DumpCOleCache(m_pCOleCache, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "COleCache: " << endl; // dstrDump << pszCOleCache; // CoTaskMemFree(pszCOleCache); } else { dstrDump << pszPrefix << "pCOleCache = " << m_pCOleCache << endl; } if (m_pCOAHolder != NULL) { pszCOAHolder = DumpCOAHolder(m_pCOAHolder, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "COAHolder: " << endl; dstrDump << pszCOAHolder; CoTaskMemFree(pszCOAHolder); } else { dstrDump << pszPrefix << "pCOAHolder = " << m_pCOAHolder << endl; } dstrDump << pszPrefix << "OLE Connection Advise ID = " << m_dwConnOle << endl; if (m_pDataAdvCache != NULL) { pszDAC = DumpCDataAdviseCache(m_pDataAdvCache, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "CDataAdviseCache: " << endl; dstrDump << pszDAC; CoTaskMemFree(pszDAC); } else { dstrDump << pszPrefix << "pCDataAdviseCache = " << m_pDataAdvCache << endl; } dstrDump << pszPrefix << "pIOleClientSite = " << m_pAppClientSite << endl; dstrDump << pszPrefix << "Connection for time = " << m_dwConnTime << endl; pszFILETIME = DumpFILETIME(&m_ltChangeOfUpdate); dstrDump << pszPrefix << "Change of update filetime = " << pszFILETIME << endl; CoTaskMemFree(pszFILETIME); pszFILETIME = DumpFILETIME(&m_ltKnownUpToDate); dstrDump << pszPrefix << "Known up to date filetime = " << pszFILETIME << endl; CoTaskMemFree(pszFILETIME); pszFILETIME = DumpFILETIME(&m_rtUpdate); dstrDump << pszPrefix << "Update filetime = " << pszFILETIME << endl; CoTaskMemFree(pszFILETIME); // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCDefLink, public (_DEBUG only) // // Synopsis: calls the CDefLink::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pDL] - pointer to CDefLink // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: character array of structure dump or error (null terminated) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG char *DumpCDefLink(CDefLink *pDL, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pDL == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pDL->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG