// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_OLE3_SEG #pragma code_seg(AFX_OLE3_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // COleDataSource implementation struct AFX_DATACACHE_ENTRY { FORMATETC m_formatEtc; STGMEDIUM m_stgMedium; DATADIR m_nDataDir; }; ///////////////////////////////////////////////////////////////////////////// // COleDataSource construction & destruction COleDataSource::COleDataSource() { m_pDataCache = NULL; m_nMaxSize = 0; m_nSize = 0; m_nGrowBy = 10; } COleDataSource::~COleDataSource() { // clear clipboard source if this object was on the clipboard _AFX_OLE_STATE* pOleState = _afxOleState; if (this == pOleState->m_pClipboardSource) pOleState->m_pClipboardSource = NULL; // free the clipboard data cache Empty(); } void COleDataSource::Empty() { if (m_pDataCache != NULL) { ASSERT(m_nMaxSize != 0); ASSERT(m_nSize != 0); // release all of the STGMEDIUMs and FORMATETCs for (UINT nIndex = 0; nIndex < m_nSize; nIndex++) { CoTaskMemFree(m_pDataCache[nIndex].m_formatEtc.ptd); ::ReleaseStgMedium(&m_pDataCache[nIndex].m_stgMedium); } // delete the cache delete[] m_pDataCache; m_pDataCache = NULL; m_nMaxSize = 0; m_nSize = 0; } ASSERT(m_pDataCache == NULL); ASSERT(m_nMaxSize == 0); ASSERT(m_nSize == 0); } ///////////////////////////////////////////////////////////////////////////// // COleDataSource clipboard API wrappers void COleDataSource::SetClipboard() { ASSERT_VALID(this); // attempt OLE set clipboard operation LPDATAOBJECT lpDataObject = (LPDATAOBJECT)GetInterface(&IID_IDataObject); SCODE sc = ::OleSetClipboard(lpDataObject); if (sc != S_OK) AfxThrowOleException(sc); // success - set as current clipboard source _afxOleState->m_pClipboardSource = this; ASSERT(::OleIsCurrentClipboard(lpDataObject) == S_OK); InternalRelease(); } void PASCAL COleDataSource::FlushClipboard() { if (GetClipboardOwner() != NULL) { // active clipboard source and it is on the clipboard - flush it ::OleFlushClipboard(); // shouldn't be clipboard owner any more... ASSERT(GetClipboardOwner() == NULL); } } COleDataSource* PASCAL COleDataSource::GetClipboardOwner() { _AFX_OLE_STATE* pOleState = _afxOleState; if (pOleState->m_pClipboardSource == NULL) return NULL; // can't own the clipboard if pClipboardSource isn't set ASSERT_VALID(pOleState->m_pClipboardSource); LPDATAOBJECT lpDataObject = (LPDATAOBJECT) pOleState->m_pClipboardSource->GetInterface(&IID_IDataObject); if (::OleIsCurrentClipboard(lpDataObject) != S_OK) { pOleState->m_pClipboardSource = NULL; return NULL; // don't own the clipboard anymore } // return current clipboard source return pOleState->m_pClipboardSource; } ///////////////////////////////////////////////////////////////////////////// // COleDataSource cache allocation AFX_DATACACHE_ENTRY* COleDataSource::GetCacheEntry( LPFORMATETC lpFormatEtc, DATADIR nDataDir) { AFX_DATACACHE_ENTRY* pEntry = Lookup(lpFormatEtc, nDataDir); if (pEntry != NULL) { // cleanup current entry and return it CoTaskMemFree(pEntry->m_formatEtc.ptd); ::ReleaseStgMedium(&pEntry->m_stgMedium); } else { // allocate space for item at m_nSize (at least room for 1 item) if (m_pDataCache == NULL || m_nSize == m_nMaxSize) { ASSERT(m_nGrowBy != 0); AFX_DATACACHE_ENTRY* pCache = new AFX_DATACACHE_ENTRY[m_nMaxSize+m_nGrowBy]; m_nMaxSize += m_nGrowBy; if (m_pDataCache != NULL) { memcpy(pCache, m_pDataCache, m_nSize * sizeof(AFX_DATACACHE_ENTRY)); delete[] m_pDataCache; } m_pDataCache = pCache; } ASSERT(m_pDataCache != NULL); ASSERT(m_nMaxSize != 0); pEntry = &m_pDataCache[m_nSize++]; } // fill the cache entry with the format and data direction and return it pEntry->m_nDataDir = nDataDir; pEntry->m_formatEtc = *lpFormatEtc; return pEntry; } ///////////////////////////////////////////////////////////////////////////// // COleDataSource operations // for HGLOBAL based cached render void COleDataSource::CacheGlobalData(CLIPFORMAT cfFormat, HGLOBAL hGlobal, LPFORMATETC lpFormatEtc) { ASSERT(hGlobal != NULL); ASSERT(lpFormatEtc == NULL || AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE)); // fill in FORMATETC struct FORMATETC formatEtc; lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc); lpFormatEtc->tymed = TYMED_HGLOBAL; // add it to the cache AFX_DATACACHE_ENTRY* pEntry = GetCacheEntry(lpFormatEtc, DATADIR_GET); pEntry->m_stgMedium.tymed = TYMED_HGLOBAL; pEntry->m_stgMedium.hGlobal = hGlobal; pEntry->m_stgMedium.pUnkForRelease = NULL; } // for raw LPSTGMEDIUM cached render void COleDataSource::CacheData(CLIPFORMAT cfFormat, LPSTGMEDIUM lpStgMedium, LPFORMATETC lpFormatEtc) { ASSERT(lpStgMedium == NULL || lpStgMedium->tymed != TYMED_NULL); ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM), FALSE)); ASSERT(lpFormatEtc == NULL || AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE)); // fill in FORMATETC struct FORMATETC formatEtc; lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc); // Only these TYMED_GDI formats can be copied, so can't serve as // cache content (you must use DelayRenderData instead) // When using COleServerItem::CopyToClipboard this means providing an // override of COleServerItem::OnGetClipboardData to provide a custom // delayed rendering clipboard object. ASSERT(lpStgMedium->tymed != TYMED_GDI || lpFormatEtc->cfFormat == CF_METAFILEPICT || lpFormatEtc->cfFormat == CF_PALETTE || lpFormatEtc->cfFormat == CF_BITMAP); lpFormatEtc->tymed = lpStgMedium->tymed; // add it to the cache AFX_DATACACHE_ENTRY* pEntry = GetCacheEntry(lpFormatEtc, DATADIR_GET); pEntry->m_stgMedium = *lpStgMedium; } // for CFile* based delayed render void COleDataSource::DelayRenderFileData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc) { ASSERT(lpFormatEtc == NULL || AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE)); // fill in FORMATETC struct FORMATETC formatEtc; lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc); lpFormatEtc->tymed |= TYMED_ISTREAM|TYMED_HGLOBAL; // add it to the cache AFX_DATACACHE_ENTRY* pEntry = GetCacheEntry(lpFormatEtc, DATADIR_GET); pEntry->m_stgMedium.tymed = TYMED_NULL; pEntry->m_stgMedium.hGlobal = NULL; pEntry->m_stgMedium.pUnkForRelease = NULL; } // for LPSTGMEDIUM or HGLOBAL based delayed render void COleDataSource::DelayRenderData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc) { ASSERT(lpFormatEtc == NULL || AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE)); // fill in FORMATETC struct FORMATETC formatEtc; if (lpFormatEtc == NULL) { lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc); lpFormatEtc->tymed = TYMED_HGLOBAL; } // insure that cfFormat member is set if (cfFormat != 0) lpFormatEtc->cfFormat = cfFormat; // add it to the cache AFX_DATACACHE_ENTRY* pEntry = GetCacheEntry(lpFormatEtc, DATADIR_GET); memset(&pEntry->m_stgMedium, 0, sizeof pEntry->m_stgMedium); } // DelaySetData -- used to allow SetData on given LPFORMATETC void COleDataSource::DelaySetData(CLIPFORMAT cfFormat, LPFORMATETC lpFormatEtc) { ASSERT(lpFormatEtc == NULL || AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE)); // fill in FORMATETC struct FORMATETC formatEtc; lpFormatEtc = _AfxFillFormatEtc(lpFormatEtc, cfFormat, &formatEtc); // add it to the cache AFX_DATACACHE_ENTRY* pEntry = GetCacheEntry(lpFormatEtc, DATADIR_SET); pEntry->m_stgMedium.tymed = TYMED_NULL; pEntry->m_stgMedium.hGlobal = NULL; pEntry->m_stgMedium.pUnkForRelease = NULL; } ///////////////////////////////////////////////////////////////////////////// // COleDataSource cache implementation AFX_DATACACHE_ENTRY* COleDataSource::Lookup( LPFORMATETC lpFormatEtc, DATADIR nDataDir) const { AFX_DATACACHE_ENTRY* pLast = NULL; // look for suitable match to lpFormatEtc in cache for (UINT nIndex = 0; nIndex < m_nSize; nIndex++) { // get entry from cache at nIndex AFX_DATACACHE_ENTRY* pCache = &m_pDataCache[nIndex]; FORMATETC *pCacheFormat = &pCache->m_formatEtc; // check for match if (pCacheFormat->cfFormat == lpFormatEtc->cfFormat && (pCacheFormat->tymed & lpFormatEtc->tymed) != 0 && (pCache->m_stgMedium.tymed == TYMED_NULL || pCacheFormat->lindex == lpFormatEtc->lindex) && pCacheFormat->dwAspect == lpFormatEtc->dwAspect && pCache->m_nDataDir == nDataDir) { // for backward compatibility we match even if we never // find an exact match for the DVTARGETDEVICE pLast = pCache; DVTARGETDEVICE* ptd1 = pCacheFormat->ptd; DVTARGETDEVICE* ptd2 = lpFormatEtc->ptd; if (ptd1 == NULL && ptd2 == NULL) { // both target devices are NULL (exact match), so return it break; } if (ptd1 != NULL && ptd2 != NULL && ptd1->tdSize == ptd2->tdSize && memcmp(ptd1, ptd2, ptd1->tdSize) == 0) { // exact match, so return it break; } // continue looking for better match } } return pLast; // not found } ///////////////////////////////////////////////////////////////////////////// // COleDataSource overidable default implementation BOOL COleDataSource::OnRenderGlobalData( LPFORMATETC /*lpFormatEtc*/, HGLOBAL* /*phGlobal*/) { return FALSE; // default does nothing } BOOL COleDataSource::OnRenderFileData( LPFORMATETC /*lpFormatEtc*/, CFile* /*pFile*/) { return FALSE; // default does nothing } BOOL COleDataSource::OnRenderData( LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium) { // attempt TYMED_HGLOBAL as prefered format if (lpFormatEtc->tymed & TYMED_HGLOBAL) { // attempt HGLOBAL delay render hook HGLOBAL hGlobal = lpStgMedium->hGlobal; if (OnRenderGlobalData(lpFormatEtc, &hGlobal)) { ASSERT(lpStgMedium->tymed != TYMED_HGLOBAL || (lpStgMedium->hGlobal == hGlobal)); ASSERT(hGlobal != NULL); lpStgMedium->tymed = TYMED_HGLOBAL; lpStgMedium->hGlobal = hGlobal; return TRUE; } // attempt CFile* based delay render hook CSharedFile file; if (lpStgMedium->tymed == TYMED_HGLOBAL) { ASSERT(lpStgMedium->hGlobal != NULL); file.SetHandle(lpStgMedium->hGlobal, FALSE); } if (OnRenderFileData(lpFormatEtc, &file)) { lpStgMedium->tymed = TYMED_HGLOBAL; lpStgMedium->hGlobal = file.Detach(); ASSERT(lpStgMedium->hGlobal != NULL); return TRUE; } if (lpStgMedium->tymed == TYMED_HGLOBAL) file.Detach(); } // attempt TYMED_ISTREAM format if (lpFormatEtc->tymed & TYMED_ISTREAM) { COleStreamFile file; if (lpStgMedium->tymed == TYMED_ISTREAM) { ASSERT(lpStgMedium->pstm != NULL); file.Attach(lpStgMedium->pstm); } else { if (!file.CreateMemoryStream()) AfxThrowMemoryException(); } // get data into the stream if (OnRenderFileData(lpFormatEtc, &file)) { lpStgMedium->tymed = TYMED_ISTREAM; lpStgMedium->pstm = file.Detach(); return TRUE; } if (lpStgMedium->tymed == TYMED_ISTREAM) file.Detach(); } return FALSE; // default does nothing } BOOL COleDataSource::OnSetData( LPFORMATETC /*lpFormatEtc*/, LPSTGMEDIUM /*lpStgMedium*/, BOOL /*bRelease*/) { return FALSE; // default does nothing } ///////////////////////////////////////////////////////////////////////////// // CEnumFormatEtc - enumerator for array for FORMATETC structures class CEnumFormatEtc : public CEnumArray { // Constructors public: CEnumFormatEtc(); // Operations void AddFormat(const FORMATETC* lpFormatEtc); // Implementation public: virtual ~CEnumFormatEtc(); protected: virtual BOOL OnNext(void* pv); UINT m_nMaxSize; // number of items allocated (>= m_nSize) DECLARE_INTERFACE_MAP() }; BEGIN_INTERFACE_MAP(CEnumFormatEtc, CEnumArray) INTERFACE_PART(CEnumFormatEtc, IID_IEnumFORMATETC, EnumVOID) END_INTERFACE_MAP() CEnumFormatEtc::CEnumFormatEtc() : CEnumArray(sizeof(FORMATETC), NULL, 0, TRUE) { m_nMaxSize = 0; } CEnumFormatEtc::~CEnumFormatEtc() { if (m_pClonedFrom == NULL) { // release all of the pointers to DVTARGETDEVICE LPFORMATETC lpFormatEtc = (LPFORMATETC)m_pvEnum; for (UINT nIndex = 0; nIndex < m_nSize; nIndex++) CoTaskMemFree(lpFormatEtc[nIndex].ptd); } // destructor will free the actual array (if it was not a clone) } BOOL CEnumFormatEtc::OnNext(void* pv) { if (!CEnumArray::OnNext(pv)) return FALSE; // any outgoing formatEtc may require the DVTARGETDEVICE to // be copied (the caller has responsibility to free it) LPFORMATETC lpFormatEtc = (LPFORMATETC)pv; if (lpFormatEtc->ptd != NULL) { lpFormatEtc->ptd = _AfxOleCopyTargetDevice(lpFormatEtc->ptd); if (lpFormatEtc->ptd == NULL) AfxThrowMemoryException(); } // otherwise, copying worked... return TRUE; } void CEnumFormatEtc::AddFormat(const FORMATETC* lpFormatEtc) { ASSERT(m_nSize <= m_nMaxSize); if (m_nSize == m_nMaxSize) { // not enough space for new item -- allocate more FORMATETC* pListNew = new FORMATETC[m_nSize+10]; m_nMaxSize += 10; memcpy(pListNew, m_pvEnum, m_nSize*sizeof(FORMATETC)); delete m_pvEnum; m_pvEnum = (BYTE*)pListNew; } // add this item to the list ASSERT(m_nSize < m_nMaxSize); FORMATETC* pFormat = &((FORMATETC*)m_pvEnum)[m_nSize]; pFormat->cfFormat = lpFormatEtc->cfFormat; pFormat->ptd = lpFormatEtc->ptd; // Note: ownership of lpFormatEtc->ptd is transfered with this call. pFormat->dwAspect = lpFormatEtc->dwAspect; pFormat->lindex = lpFormatEtc->lindex; pFormat->tymed = lpFormatEtc->tymed; ++m_nSize; } ///////////////////////////////////////////////////////////////////////////// // COleDataSource::XDataObject BEGIN_INTERFACE_MAP(COleDataSource, CCmdTarget) INTERFACE_PART(COleDataSource, IID_IDataObject, DataObject) END_INTERFACE_MAP() STDMETHODIMP_(ULONG) COleDataSource::XDataObject::AddRef() { METHOD_PROLOGUE_EX_(COleDataSource, DataObject) return pThis->ExternalAddRef(); } STDMETHODIMP_(ULONG) COleDataSource::XDataObject::Release() { METHOD_PROLOGUE_EX_(COleDataSource, DataObject) return pThis->ExternalRelease(); } STDMETHODIMP COleDataSource::XDataObject::QueryInterface( REFIID iid, LPVOID* ppvObj) { METHOD_PROLOGUE_EX_(COleDataSource, DataObject) return pThis->ExternalQueryInterface(&iid, ppvObj); } STDMETHODIMP COleDataSource::XDataObject::GetData( LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium) { METHOD_PROLOGUE_EX(COleDataSource, DataObject) ASSERT_VALID(pThis); // attempt to find match in the cache AFX_DATACACHE_ENTRY* pCache = pThis->Lookup(lpFormatEtc, DATADIR_GET); if (pCache == NULL) return DATA_E_FORMATETC; // use cache if entry is not delay render memset(lpStgMedium, 0, sizeof(STGMEDIUM)); if (pCache->m_stgMedium.tymed != TYMED_NULL) { // Copy the cached medium into the lpStgMedium provided by caller. if (!_AfxCopyStgMedium(lpFormatEtc->cfFormat, lpStgMedium, &pCache->m_stgMedium)) return DATA_E_FORMATETC; // format was supported for copying return S_OK; } SCODE sc = DATA_E_FORMATETC; TRY { // attempt LPSTGMEDIUM based delay render if (pThis->OnRenderData(lpFormatEtc, lpStgMedium)) sc = S_OK; } CATCH_ALL(e) { sc = COleException::Process(e); DELETE_EXCEPTION(e); } END_CATCH_ALL return sc; } STDMETHODIMP COleDataSource::XDataObject::GetDataHere( LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium) { METHOD_PROLOGUE_EX(COleDataSource, DataObject) ASSERT_VALID(pThis); // these two must be the same ASSERT(lpFormatEtc->tymed == lpStgMedium->tymed); lpFormatEtc->tymed = lpStgMedium->tymed; // but just in case... // attempt to find match in the cache AFX_DATACACHE_ENTRY* pCache = pThis->Lookup(lpFormatEtc, DATADIR_GET); if (pCache == NULL) return DATA_E_FORMATETC; // handle cached medium and copy if (pCache->m_stgMedium.tymed != TYMED_NULL) { // found a cached format -- copy it to dest medium ASSERT(pCache->m_stgMedium.tymed == lpStgMedium->tymed); if (!_AfxCopyStgMedium(lpFormatEtc->cfFormat, lpStgMedium, &pCache->m_stgMedium)) return DATA_E_FORMATETC; // format was supported for copying return S_OK; } SCODE sc = DATA_E_FORMATETC; TRY { // attempt LPSTGMEDIUM based delay render if (pThis->OnRenderData(lpFormatEtc, lpStgMedium)) sc = S_OK; } CATCH_ALL(e) { sc = COleException::Process(e); DELETE_EXCEPTION(e); } END_CATCH_ALL return sc; } STDMETHODIMP COleDataSource::XDataObject::QueryGetData(LPFORMATETC lpFormatEtc) { METHOD_PROLOGUE_EX_(COleDataSource, DataObject) // attempt to find match in the cache AFX_DATACACHE_ENTRY* pCache = pThis->Lookup(lpFormatEtc, DATADIR_GET); if (pCache == NULL) return DATA_E_FORMATETC; // it was found in the cache or can be rendered -- success return S_OK; } STDMETHODIMP COleDataSource::XDataObject::GetCanonicalFormatEtc( LPFORMATETC /*lpFormatEtcIn*/, LPFORMATETC /*lpFormatEtcOut*/) { // because we support the target-device (ptd) for server metafile format, // all members of the FORMATETC are significant. return DATA_S_SAMEFORMATETC; } STDMETHODIMP COleDataSource::XDataObject::SetData( LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium, BOOL bRelease) { METHOD_PROLOGUE_EX(COleDataSource, DataObject) ASSERT_VALID(pThis); ASSERT(lpFormatEtc->tymed == lpStgMedium->tymed); // attempt to find match in the cache AFX_DATACACHE_ENTRY* pCache = pThis->Lookup(lpFormatEtc, DATADIR_SET); if (pCache == NULL) return DATA_E_FORMATETC; ASSERT(pCache->m_stgMedium.tymed == TYMED_NULL); SCODE sc = E_UNEXPECTED; TRY { // attempt LPSTGMEDIUM based SetData if (pThis->OnSetData(lpFormatEtc, lpStgMedium, bRelease)) sc = S_OK; } CATCH_ALL(e) { sc = COleException::Process(e); DELETE_EXCEPTION(e); } END_CATCH_ALL return sc; } STDMETHODIMP COleDataSource::XDataObject::EnumFormatEtc( DWORD dwDirection, LPENUMFORMATETC* ppenumFormatEtc) { METHOD_PROLOGUE_EX_(COleDataSource, DataObject) *ppenumFormatEtc = NULL; CEnumFormatEtc* pFormatList = NULL; SCODE sc = E_OUTOFMEMORY; TRY { // generate a format list from the cache pFormatList = new CEnumFormatEtc; for (UINT nIndex = 0; nIndex < pThis->m_nSize; nIndex++) { AFX_DATACACHE_ENTRY* pCache = &pThis->m_pDataCache[nIndex]; if ((DWORD)pCache->m_nDataDir & dwDirection) { // entry should be enumerated -- add it to the list FORMATETC formatEtc; _AfxOleCopyFormatEtc(&formatEtc, &pCache->m_formatEtc); pFormatList->AddFormat(&formatEtc); } } // give it away to OLE (ref count is already 1) *ppenumFormatEtc = (LPENUMFORMATETC)&pFormatList->m_xEnumVOID; sc = S_OK; } END_TRY return sc; } STDMETHODIMP COleDataSource::XDataObject::DAdvise( FORMATETC* /*pFormatetc*/, DWORD /*advf*/, LPADVISESINK /*pAdvSink*/, DWORD* pdwConnection) { *pdwConnection = 0; return OLE_E_ADVISENOTSUPPORTED; } STDMETHODIMP COleDataSource::XDataObject::DUnadvise(DWORD /*dwConnection*/) { return OLE_E_ADVISENOTSUPPORTED; } STDMETHODIMP COleDataSource::XDataObject::EnumDAdvise( LPENUMSTATDATA* ppenumAdvise) { *ppenumAdvise = NULL; return OLE_E_ADVISENOTSUPPORTED; } ///////////////////////////////////////////////////////////////////////////// // COleDataSource diagnostics #ifdef _DEBUG void COleDataSource::AssertValid() const { CCmdTarget::AssertValid(); ASSERT(m_nSize <= m_nMaxSize); ASSERT(m_nMaxSize != 0 || m_pDataCache == NULL); } void COleDataSource::Dump(CDumpContext& dc) const { CCmdTarget::Dump(dc); dc << "m_nMaxSize = " << m_nMaxSize; dc << "\nm_nSize = " << m_nSize; dc << "\nm_pDataCache = " << m_pDataCache; for (UINT n = 0; n < m_nSize; n++) { dc << "\n\tentry [" << n << "] = {"; AFX_DATACACHE_ENTRY& rEntry = m_pDataCache[n]; dc << "\n\t m_formatEtc.cfFormat = " << rEntry.m_formatEtc.cfFormat; dc << "\n\t m_formatEtc.pdt = " << rEntry.m_formatEtc.ptd; dc << "\n\t m_formatEtc.dwAspect = " << rEntry.m_formatEtc.dwAspect; dc << "\n\t m_formatEtc.lindex = " << rEntry.m_formatEtc.lindex; dc << "\n\t m_formatEtc.tymed = " << rEntry.m_formatEtc.tymed; dc << "\n\t m_stgMedium.tymed = " << rEntry.m_stgMedium.tymed; dc << "\n\t m_nDataDir = " << (UINT)rEntry.m_nDataDir; dc << "\n\t}"; } dc << "\n"; } #endif //_DEBUG /////////////////////////////////////////////////////////////////////////////