//+---------------------------------------------------------------------------- // // File: // dacache.cpp // // Contents: // implementation of the data advise cache - CDataAdviseCache // // Classes: // CDataAdviseCache // // Functions: // // History: // 31-Jan-95 t-ScottH add Dump method to CDataAdviseCache and // DumpCDataAdviseCache API // 24-Jan-94 alexgo first pass at converting to Cairo-style // memory allocation // 01/11/94 - AlexGo - added VDATEHEAP macros to every function // and method // 11/02/93 - ChrisWe - file inspection and cleanup // 12/15/92 - JasonFul - Created // //----------------------------------------------------------------------------- #include #pragma SEG(dacache) #include #include #ifdef _DEBUG #include #include #endif // _DEBUG ASSERTDATA //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::CreateDataAdviseCache, static public // // Synopsis: // Creates an instance of the CDataAdviseCache // // Arguments: // [pp] -- pointer to a location to where to return the // newly created CDataAdviseCache // // Returns: // E_OUTOFMEMORY, S_OK // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CreateDataAdviseCache) FARINTERNAL CDataAdviseCache::CreateDataAdviseCache(LPDATAADVCACHE FAR* pp) { VDATEHEAP(); VDATEPTRIN(pp, LPDATAADVCACHE); // try to allocate the CDataAdviseCache if(NULL == (*pp = new DATAADVCACHE)) return ReportResult(0, E_OUTOFMEMORY, 0, 0); // initialize the DataAdviseHolder member if(CreateDataAdviseHolder(&((*pp)->m_pDAH)) != NOERROR) { // free the DataAdviseCache delete *pp; *pp = NULL; return ReportResult(0, E_OUTOFMEMORY, 0, 0); } return(NOERROR); } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::CDataAdviseCache, private // // Synopsis: // constructor // // Arguments: // none // // Notes: // This is private because it does not create a fully // formed CDataAdviseCache. m_pDAH must be allocated before // this can be used. That is done by the static member // CreateDataAdviseCache, which first calls this // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_ctor) CDataAdviseCache::CDataAdviseCache(): m_mapClientToDelegate(MEMCTX_TASK) { VDATEHEAP(); //now allocated with system allocator //Assert(CoMemctxOf(this) == MEMCTX_TASK); // no data advise holder allocated yet m_pDAH = NULL; } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::~CDataAdviseCache, public // // Synopsis: // destructor // // Arguments: // none // // Requires: // successful call to CreateDataAdviseCache // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_dtor) CDataAdviseCache::~CDataAdviseCache() { VDATEHEAP(); // release the data advise holder if( m_pDAH ) { m_pDAH->Release(); } } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::Advise, public // // Synopsis: // Records an advise sink for later use. The sink will be // registered with the data object, if there is one, and // will be remembered for later registration with the data object, // in case it should go away, and return later. // // Effects: // // Arguments: // [pDataObject] -- the data object that the advise sink is // interested in changes to; may be null if the // data object isn't running // [pFetc] -- the format the advise sink would like to recieve // new data in // [advf] -- advise control flags ADVF_* // [pAdvise] -- the advise sink // [pdwClient] -- a token identifying the connection // // Returns: // E_OUTOFMEMORY, S_OK // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_Advise) HRESULT CDataAdviseCache::Advise(LPDATAOBJECT pDataObject, FORMATETC FAR* pFetc, DWORD advf, LPADVISESINK pAdvise, DWORD FAR* pdwClient) // first 4 parms are as in DataObject::Advise { VDATEHEAP(); DWORD dwDelegate = 0; // the delegate connection number HRESULT hr; // if there is a data object, ask to be advised of changes if(pDataObject != NULL) RetErr(pDataObject->DAdvise(pFetc, advf, pAdvise, &dwDelegate)); // if there is no data object, (i.e. the object is not active, // dwDelegate is zero // Here we are using the data advise holder only to hold advise // connections. We are not going to use it to send OnDataChange to // sinks. // REVIEW, handling of ADVF_ONLYONCE seems broken... // it's clear that we can't cope with this flag properly; we have // no way of knowing when the notification takes place, and therefore // we can't remove the entry from m_pDAH. The notification may have // taken place above, and it may not have. If the data object wasn't // around, then the advise request here is lost, and the sink will // never be notified. Or, if the request isn't PRIMEFIRST, and the // data object is deactivated, then the data object loses the request, // and on subsequent activation, we won't readvise it on EnumAndAdvise. // So, what good are we for ONLYONCE sinks? What does this break? if(advf & ADVF_ONLYONCE) return NOERROR; // keep a local copy of the advise hr = m_pDAH->Advise(NULL, pFetc, advf, pAdvise, pdwClient); // if we failed to keep a local reference to the advise sink, // we won't be able to maintain this mapping, so remove the // advise on the data object, if there is one if (hr != NOERROR) { Exit1: if (pDataObject != NULL) pDataObject->DUnadvise(dwDelegate); return(hr); } // create a map entry from *pdwClient -> dwDelegate // if the map entry creation failed, undo all work if (m_mapClientToDelegate.SetAt(*pdwClient, dwDelegate) != TRUE) { // map failed to allocate memory, undo advise since we won't // be able to find this one again m_pDAH->Unadvise(*pdwClient); // map entry creation must have failed from lack of allocation hr = ReportResult(0, E_OUTOFMEMORY, 0, 0); // undo the advise on the data object goto Exit1; } return(NOERROR); } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::Unadvise, public // // Synopsis: // Remove an advise sink from the list of sinks the advise cache // maintains; the sink is also removed from the list of items // registered with the data object, if the data object is provided // // Effects: // // Arguments: // [pDataObject] -- the data object, if it is running, or NULL // [dwClient] -- the token that identifies this connection // // Returns: // OLE_E_NOCONNECTION, for a bad dwClient // S_OK // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_Unadvise) HRESULT CDataAdviseCache::Unadvise(IDataObject FAR* pDataObject, DWORD dwClient) { VDATEHEAP(); DWORD dwDelegate; // retrieve dwDelegate before removing from map if(pDataObject != NULL) RetErr(ClientToDelegate(dwClient, &dwDelegate)); // do these first so error from remote unadvise is last(which might // be sync call during async dispatch RetErr(m_pDAH->Unadvise(dwClient)); // If the above line succeeded, Remove Key must succeed. Verify(TRUE == m_mapClientToDelegate.RemoveKey(dwClient)); // Delegate connection could be 0 if it did not accept the Advise if(pDataObject != NULL && dwDelegate != 0) { // Unadvise is asynchronous, don't worry about return value pDataObject->DUnadvise(dwDelegate); } return NOERROR; } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::EnumAdvise, public // // Synopsis: // returns an enumerator over the advisory connections // // Arguments: // [ppenumAdvise] -- pointer to where to return the enumerator // // Returns: // E_OUTOFMEMORY, S_OK // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_EnumAdvise) HRESULT CDataAdviseCache::EnumAdvise(LPENUMSTATDATA FAR* ppenumAdvise) { VDATEHEAP(); return m_pDAH->EnumAdvise(ppenumAdvise); } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::ClientToDelegate, private // // Synopsis: // returns the delegate connection id for a given client // connection id // // Arguments: // [dwClient] -- the client connection identifier // [pdwDelegate] -- pointer to where to return the delegate // connection identifier // // Returns: // OLE_E_NOCONNECTION, for a bad dwClient // S_OK // // Notes: // // History: // 11/02/93 - ChrisWe - file cleanup and inspection // //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_ClientToDelegate) HRESULT CDataAdviseCache::ClientToDelegate(DWORD dwClient, DWORD FAR* pdwDelegate) { VDATEHEAP(); VDATEPTRIN(pdwDelegate, DWORD); DWORD dwDelegate = *pdwDelegate = 0; if (FALSE == m_mapClientToDelegate.Lookup(dwClient, dwDelegate)) return(ReportResult(0, OLE_E_NOCONNECTION, 0, 0)); *pdwDelegate = dwDelegate; return NOERROR; } //+---------------------------------------------------------------------------- // // Member: // CDataAdviseCache::EnumAndAdvise, public // // Synopsis: // Enumerate all the advise sinks registered in the data advise // cache. For each one, either register it with the // given data object, or deregister it, depending on [fAdvise]. // Does not change what sinks are known to the data advise cache. // // Effects: // // Arguments: // [pDataDelegate] -- a data object that the advise sinks // are interested in // [fAdvise] -- if TRUE, register the advise sinks with // pDataDelegate object (with IDataObject::DAdvise();) if // FALSE, the deregister the advise sinks // (with DUnadvise().) // // Returns: // OLE_E_NOCONNECTION, if the mapping is corrupt (REVIEW!) // S_OK // // Notes: // // History: // 11/04/93 - ChrisWe - file cleanup and inspection //----------------------------------------------------------------------------- #pragma SEG(CDataAdviseCache_EnumAndAdvise) HRESULT CDataAdviseCache::EnumAndAdvise(LPDATAOBJECT pDataDelegate, BOOL fAdvise) { VDATEHEAP(); if(pDataDelegate) { VDATEIFACE(pDataDelegate); } else { Win4Assert(!fAdvise); } LPENUMSTATDATA penumAdvise; // enumerator for the data advise holder DWORD dwDelegate; // delegate connection id for the current connection STATDATA statdata; // filled in by the penumAdvise enumerator HRESULT hresult = NOERROR; // current error status // get an enumerator from the data advise holder RetErr(m_pDAH->EnumAdvise(&penumAdvise)); // repeat for each advise sink in the data advise holder... while(NOERROR == penumAdvise->Next(1, &statdata, NULL)) { if(fAdvise) { // It is possible that the delegate's Advise will fail // even though we allowed the advise on the loaded // object to succeed(because the delegate is "pickier".) if(NOERROR==pDataDelegate->DAdvise(&statdata.formatetc, statdata.advf, statdata.pAdvSink, &dwDelegate)) { // we know the key is present; this SetAt // should not fail Verify(m_mapClientToDelegate.SetAt( statdata.dwConnection, dwDelegate)); } } else // unadvise { if((hresult=ClientToDelegate(statdata.dwConnection, &dwDelegate)) != NOERROR) { AssertSz(0, "Corrupt mapping"); UtReleaseStatData(&statdata); goto errRtn; } if(dwDelegate != 0) { // Unadvise only if valid object if(pDataDelegate) if(pDataDelegate->DUnadvise(dwDelegate) != NOERROR) Win4Assert(FALSE); // Always remove the key Verify(m_mapClientToDelegate.SetAt(statdata.dwConnection, 0)); } } UtReleaseStatData(&statdata); } errRtn: // release the enumerator penumAdvise->Release(); return hresult; } //+------------------------------------------------------------------------- // // Member: CDataAdviseCache::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: [ppsz] - argument // // Derivation: // // Algorithm: use dbgstream to create a string containing information on the // content of data structures // // History: dd-mmm-yy Author Comment // 31-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG HRESULT CDataAdviseCache::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; char *pszPrefix; char *pszDAH; char *pszCMapDD; dbgstream dstrPrefix; dbgstream dstrDump(1000); // 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 if (m_pDAH != NULL) { pszDAH = DumpCDAHolder((CDAHolder *)m_pDAH, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "CDAHolder: " << endl; dstrDump << pszDAH; CoTaskMemFree(pszDAH); } else { dstrDump << pszPrefix << "pIDataAdviseHolder = " << m_pDAH << endl; } pszCMapDD = DumpCMapDwordDword(&m_mapClientToDelegate, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "Map of Clients to Delegate:" << endl; dstrDump << pszCMapDD; CoTaskMemFree(pszCMapDD); // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCDataAdviseCache, public (_DEBUG only) // // Synopsis: calls the CDataAdviseCache::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pDAC] - pointer to CDataAdviseCache // [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 // 31-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG char *DumpCDataAdviseCache(CDataAdviseCache *pDAC, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pDAC == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pDAC->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG