//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: clipdata.cpp // // Contents: implementation of CClipDataObject // // Classes: // // Functions: // // History: dd-mmm-yy Author Comment // 01-Feb-95 t-ScottH added Dump method to CClipDataObject, // CEnumFormatEtcDataArray and added APIs // DumpCClipDataObject, DumpCEnumFormatEtcDataArray // 09-Jan-95 t-scotth changed VDATETHREAD to accept a pointer, and // ensured VDATETHREAD is before VDATEPTRIN and // VDATEPTROUT // 21-Nov-94 alexgo added thread validation // 11-Aug-94 alexgo added support for EmbeddedObject from // OLE1 formats. // 04-Jun-94 alexgo now converts OLE1 formats into OLE2 // 30-May-94 alexgo now supports enhanced metafiles // 17-May-94 alexgo now use OleOpenClipboard instead of // OpenClipboard. // 11-May-94 alexgo eliminated allocations for 0 // bytes from the enumerator. // 02-Apr-94 alexgo author // // Notes: // This file is laid out as follows: // ClipboardDataObject private methods // ClipboardDataObject IDataObject methods // OLE1 support functions (in alphabetical order) // Formatetc Enumerator methods // //-------------------------------------------------------------------------- #include #include #include "clipdata.h" #include "clipbrd.h" #include //for wCLSIDFromProgID and GenericObjectToIStorage #include #ifdef _DEBUG #include #endif // _DEBUG // helper function used by the data object and formatetc enumerator HRESULT BmToPres(HANDLE hBM, PPRES ppres); BOOL CanRetrieveOle2FromOle1( UINT cf); HRESULT DibToPres(HANDLE hDib, PPRES ppres); BOOL IsOwnerLinkStdOleLink( void ); HRESULT MfToPres( HANDLE hMFPict, PPRES ppres); HRESULT NativeToStorage(IStorage *pstg, HANDLE hNative); //+------------------------------------------------------------------------- // // Member: CClipDataObject::CClipDataObject // // Synopsis: constructor // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: initializes reference count to 1 // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- CClipDataObject::CClipDataObject( ) { m_refs = 1; m_Internalrefs = 0; // Up To Caller to Place InternalRef. m_pFormatEtcDataArray = NULL; // OLE1 support stuff m_hOle1 = NULL; m_pUnkOle1 = NULL; // Data object to use to get the data. m_pDataObject = NULL; m_fTriedToGetDataObject = FALSE; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::~CClipDataObject // // Synopsis: destructor // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: frees the formatetc array (if one exists) // // History: dd-mmm-yy Author Comment // 19-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- CClipDataObject::~CClipDataObject( ) { if (m_pDataObject != NULL) { // Release our reference to the data object. m_pDataObject->Release(); } if (m_pFormatEtcDataArray) { if (0 == --m_pFormatEtcDataArray->_cRefs) { PrivMemFree(m_pFormatEtcDataArray); m_pFormatEtcDataArray = NULL; } } } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetRealDataObjPtr (private) // // Synopsis: Get clip board data object from the clipboard // // Effects: // // Arguments: void // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: If we've tried this before, then return; otherwise: // Open the clipboard. Get the data object from the clipboard // if it is there. Close the clipboard and update our private // member. // // History: dd-mmm-yy Author Comment // 12-Aug-94 alexgo handle identity better // 17-Jun-94 alexgo optimized // 13-Jun-94 Ricksa author // // Notes: We must only try to get the real data object once in order // to preserve OLE identity. Recall that we call this function // if somebody does a QI for IUnknown. Now suppose that // GetInterfaceFromWindowProp fails for a 'benign' reason // (like we're in the middle of processing a send message). // Our QueryInterface will then return *our* IUnknown, thus // establishing the identity of this object to be CClipDataObject. // // Now suppose another call to QI for IUnknown is made. If // we simply noticed that m_pDataObject was NULL and called // GetInterfaceFromWindowProp again, it could succeed and we // would return a *different* IUnknown (thus violating OLE // object identity). // // For this reason, we only allow one call to // GetInterfaceFromWindowProp. // // Note that it is not strictly necessary to disable multiple // calls to GetInterfaceFromWindowProp for GetData and // GetDataHere methods. Neither of these methods affect // object identity. However, for the sake of consistency and // simplicity (!), we treat GetData and QI the same. // //-------------------------------------------------------------------------- LPDATAOBJECT CClipDataObject::GetRealDataObjPtr( ) { HGLOBAL hDataObject; HWND * phClipWnd; HWND hClipWnd = NULL; HRESULT hresult; #if DBG == 1 BOOL fCloseClipSucceeded; #endif // DBG LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetRealDataObjPtr ( )\n", this)); // if we already have a data object, or we've already tried and failed // to get one, then we don't need to do any work here. if( m_pDataObject || m_fTriedToGetDataObject == TRUE ) { goto logRtn; } // if cfDataObject is not on the clipboard, don't bother opening it; // we know that we can't get a data object. if( !SSIsClipboardFormatAvailable(g_cfDataObject)) { goto errRtn; } // // // BEGIN: OPEN CLIPBOARD // // // Open the clipboard in preparation for the get hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { LEDebugOut((DEB_ERROR, "ERROR: OleOpenClipboard failed!\n")); goto errRtn; } hDataObject = SSGetClipboardData(g_cfDataObject); if( hDataObject ) { phClipWnd = (HWND *)GlobalLock(hDataObject); LEERROR(phClipWnd == NULL, "GlobalLock failed!"); if( phClipWnd ) { hClipWnd = *phClipWnd; GlobalUnlock(hDataObject); } } #if DBG == 1 fCloseClipSucceeded = #endif // DBG SSCloseClipboard(); #if DBG == 1 // We only report this error in debug if (!fCloseClipSucceeded) { LEDebugOut((DEB_ERROR, "ERROR: CloseClipboard failed!\n")); } #endif // DBG // // // END: CLOSE CLIPBOARD // // if( hClipWnd ) { // See if we can get a data object hresult = GetInterfaceFromWindowProp( hClipWnd, IID_IDataObject, (IUnknown **) &m_pDataObject, CLIPBOARD_DATA_OBJECT_PROP ); #if DBG ==1 if( hresult != NOERROR ) { Assert(m_pDataObject == NULL); } else { Assert(m_pDataObject != NULL); } #endif // DBG == 1 } errRtn: logRtn: // if we didn't get a data object, then set a flag so we // don't try to do this again. m_fTriedToGetDataObject = TRUE; LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetRealDataObjPtr ( ) " "[ %p ]\n", this, m_pDataObject)); return m_pDataObject; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetFormatEtcDataArray (private) // // Synopsis: if don't already have shared formats creates. // // Effects: // // Arguments: void // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 13-Jun-94 Ricksa author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetFormatEtcDataArray() { HRESULT hresult = ResultFromScode(E_OUTOFMEMORY); UINT cfFormat = NULL; ULONG i; ULONG cExtraFormats; FORMATETCDATA * pFormatEtcData; DWORD cTotal; DWORD dwSize; DWORD flatmediums, structuredmediums; #define MAX_OLE2FORMATS 4 // we can at most construct 4 OLE2 // formats from OLE1 data if (m_pFormatEtcDataArray) // if already have the formats just return. return NOERROR; // 16bit apps do binary comparisons on the tymed instead // of bit-masking. This is a hack to make them work. if( IsWOWThread() ) { flatmediums = TYMED_HGLOBAL; structuredmediums = TYMED_ISTORAGE; } else { flatmediums = (TYMED_HGLOBAL | TYMED_ISTREAM); structuredmediums = (TYMED_ISTORAGE | TYMED_ISTREAM | TYMED_HGLOBAL); } Assert(m_pFormatEtcDataArray == NULL); // // BEGIN: OPENCLIPBOARD // // hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { goto errRtn; } // first count the number of formats on the clipboard cTotal = CountClipboardFormats(); // don't include OLE's private clipboard formats in the count if (SSIsClipboardFormatAvailable(g_cfDataObject)) cTotal--; if (SSIsClipboardFormatAvailable(g_cfOlePrivateData)) cTotal--; // now allocate memory for the array // if there are zero formats, then don't bother allocating // the memory Assert((LONG) cTotal >= 0); // here we will allocate enough memory for the formats // we know about plus enough to cover any OLE2 formats // that we might be able to synthesize from OLE1 formats dwSize = ((cTotal + MAX_OLE2FORMATS) * sizeof(FORMATETCDATA)) + sizeof(FORMATETCDATAARRAY); m_pFormatEtcDataArray = (FORMATETCDATAARRAY *) PrivMemAlloc(dwSize); if( !m_pFormatEtcDataArray ) { hresult = ResultFromScode(E_OUTOFMEMORY); if( !SSCloseClipboard() ) { LEDebugOut((DEB_WARN, "WARNING: " "CloseClipboard failed!\n")); ; // no-op to keep the compiler happy } // // END: CLOSECLIPBOARD // goto errRtn; } _xmemset(m_pFormatEtcDataArray, 0,dwSize); m_pFormatEtcDataArray->_dwSig = 0; m_pFormatEtcDataArray->_dwSize = dwSize; m_pFormatEtcDataArray->_cRefs = 1; m_pFormatEtcDataArray->_fIs64BitArray = IS_WIN64; // first check to see if we can synthesize any OLE2 formats // from OLE1 data. cExtraFormats = 0; pFormatEtcData = &(m_pFormatEtcDataArray->_FormatEtcData[0]); // point to first value. // check for EmbedSource first if( CanRetrieveOle2FromOle1(g_cfEmbedSource) ) { // set up a formatetc entry for EmbedSource INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfEmbedSource; pFormatEtcData->_FormatEtc.tymed = structuredmediums; cExtraFormats++; pFormatEtcData++; // we only want to support cfObjectDescriptor if we // can offer EmbedSource (which is why we're in this // if block) if( CanRetrieveOle2FromOle1(g_cfObjectDescriptor) ) { INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfObjectDescriptor; pFormatEtcData->_FormatEtc.tymed = flatmediums; cExtraFormats++; pFormatEtcData++; } } // check for EmbeddedObject if( CanRetrieveOle2FromOle1(g_cfEmbeddedObject) ) { // set up a formatetc entry for EmbedSource INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfEmbeddedObject; pFormatEtcData->_FormatEtc.tymed = structuredmediums; cExtraFormats++; pFormatEtcData++; // we only want to support cfObjectDescriptor if we // can offer EmbedEmbedded (which is why we're in this // if block) if( CanRetrieveOle2FromOle1(g_cfObjectDescriptor) ) { INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfObjectDescriptor; pFormatEtcData->_FormatEtc.tymed = flatmediums; cExtraFormats++; pFormatEtcData++; } } // check for LinkSource if( CanRetrieveOle2FromOle1(g_cfLinkSource) ) { INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfLinkSource; // for LinkSource in WOW, we want to explicitly offer // only ISTREAM tymed because that's what 16bit code // did. if( IsWOWThread() ) { pFormatEtcData->_FormatEtc.tymed = TYMED_ISTREAM; } else { pFormatEtcData->_FormatEtc.tymed = flatmediums; } cExtraFormats++; pFormatEtcData++; // we only want to support cfLinkSrcDescriptor if we // can offer LinkSource if( CanRetrieveOle2FromOle1(g_cfLinkSrcDescriptor) ) { INIT_FORETC(pFormatEtcData->_FormatEtc); pFormatEtcData->_FormatEtc.cfFormat = g_cfLinkSrcDescriptor; pFormatEtcData->_FormatEtc.tymed = flatmediums; cExtraFormats++; pFormatEtcData++; } } // Update Shared Format Header. Assert(cExtraFormats <= MAX_OLE2FORMATS); cTotal += cExtraFormats; m_pFormatEtcDataArray->_cFormats = cTotal; // now we need to go through and initialize each formatetc array // for the remaining formats available directly on the clipboard // NB: this includes any ole1 formats from which constructed OLE2 // formats above. This will make it easier for apps, the interop // layer, and our api's to special case behaviour for backwards // compatibility with old apps. cfFormat = NULL; // we increment the loop counter at the bottom (so we can skip // private clipboard formats) // pFormatEtcData points to the proper starting position. for( i = cExtraFormats; i < cTotal; i++ ) { // lindex == DEF_LINDEX // aspect == DVASPECT_CONTENT // ptd == NULL INIT_FORETC(pFormatEtcData->_FormatEtc); cfFormat = SSEnumClipboardFormats(cfFormat); Assert(cfFormat); // if it's NULL, something // really weird is happening. pFormatEtcData->_FormatEtc.cfFormat = (CLIPFORMAT) cfFormat; // try to make some reasonable guesses as to what's // there. switch( cfFormat ) { case CF_BITMAP: case CF_PALETTE: pFormatEtcData->_FormatEtc.tymed = TYMED_GDI; break; case CF_METAFILEPICT: pFormatEtcData->_FormatEtc.tymed = TYMED_MFPICT; break; case CF_ENHMETAFILE: pFormatEtcData->_FormatEtc.tymed = TYMED_ENHMF; break; default: // check for Storage-based OLE2 formats. if( cfFormat == g_cfEmbedSource || cfFormat == g_cfEmbeddedObject ) { // we can get these on any structured and flat // mediums pFormatEtcData->_FormatEtc.tymed = structuredmediums; // In order to get here, the app must have // manually set these formats on the clipboard // (i.e. by not using OleSetClipboard()). // This is OK, but print out a warning. LEDebugOut((DEB_WARN, "WARNING: Ole2 formats " "unexpected on clipboard\n")); } else { // we don't know, so be safe and just answer // with flat mediums pFormatEtcData->_FormatEtc.tymed = flatmediums; } break; } ++pFormatEtcData; } if( !SSCloseClipboard() ) { LEDebugOut((DEB_WARN, "WARNING: CloseClipboard failed!\n")); ; // no-op to keep the compiler happy } // // // END: CLOSECLIPBOARD // // hresult = NOERROR; errRtn: return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::MatchFormatetc // // Synopsis: checks the given formatetc against the array of formatetc's // that is on the Native Clipboard. // // Effects: // // Arguments: [pformatetc] -- the formatetc to check // [fNativeOnly] -- If Set, return valid matches for only items that are on the Native clipboard. // [ptymed] -- where to stuff the tymed of the *original* // formatetc (may be NULL) // // Requires: // // Returns: FormatMatchFlag -- // FORMAT_NOTFOUND - Format was not found in Enumerator or synthesized Data. // FORMAT_BADMATCH - Format or synthesized Found but doesn't match // FORMAT_GOODMATCH - Format Found in Enumerator or is valid synthesized data. // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 18-Aug-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- typedef struct SYNTHESIZED_MAP { CLIPFORMAT cfSynthesized; // Synthesized Formats CLIPFORMAT cfOrig; // Original Format. } SYNTHESIZED_MAP; #define CF_MAXSYNTHESIZED CF_ENHMETAFILE // CF_ENHMETAFILE == 14 and >> cfFormat value we check. #define NumSynthesizedFormats 12 // Number of Synthesized formats in the Array. const SYNTHESIZED_MAP pSynthesized[] = { CF_DIB, CF_BITMAP, CF_BITMAP, CF_DIB, CF_PALETTE, CF_DIB, CF_PALETTE, CF_BITMAP, CF_METAFILEPICT,CF_ENHMETAFILE, CF_ENHMETAFILE, CF_METAFILEPICT, CF_TEXT, CF_OEMTEXT, CF_TEXT, CF_UNICODETEXT, CF_OEMTEXT, CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT, CF_UNICODETEXT, CF_OEMTEXT, CF_UNICODETEXT, CF_TEXT }; FormatMatchFlag CClipDataObject::MatchFormatetc( FORMATETC *pformatetc,BOOL fNativeOnly, TYMED *ptymed ) { FORMATETC formatetc; ULONG i; FormatMatchFlag fFlag = FORMAT_NOTFOUND; FORMATETCDATA * pFormatEtcData = NULL; DWORD dwNumFormats; FORMATETC *pformatetcNative[CF_MAXSYNTHESIZED + 1]; LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::MatchFormatetc (" " %p , %p)\n", this, pformatetc, ptymed)); formatetc = *pformatetc; // some applications as for an Aspect of 0 which maps to content. if (0 == formatetc.dwAspect) formatetc.dwAspect = DVASPECT_CONTENT; // make sure all pFormatEtcs in array are initially NULL. _xmemset(pformatetcNative, 0, sizeof(FORMATETC *) * (CF_MAXSYNTHESIZED + 1)); GetFormatEtcDataArray(); // Create SharedFormats from Native Clipboard if Necessary. if( ptymed ) { *ptymed = TYMED_NULL; } if( m_pFormatEtcDataArray ) { dwNumFormats = m_pFormatEtcDataArray->_cFormats; pFormatEtcData = &(m_pFormatEtcDataArray->_FormatEtcData[0]); for( i = 0; i < dwNumFormats; i++ ) { FORMATETC tempformatetc = pFormatEtcData->_FormatEtc; // if the clipboard format matchs AND // the aspect matches AND // the tymed matches // then, return success if( tempformatetc.cfFormat == formatetc.cfFormat) { // fix up the ptd if necessary if (tempformatetc.ptd) { tempformatetc.ptd = (DVTARGETDEVICE *) ((BYTE *) m_pFormatEtcDataArray + (ULONG_PTR) tempformatetc.ptd); } // we don't need to check TYMED because // this clipboard data object can satisfy // almost all valid TYMED's, and specfically, // more than will be contained in the // formatetc tymed field. if( ((tempformatetc.dwAspect & formatetc.dwAspect) == formatetc.dwAspect) && (tempformatetc.lindex == formatetc.lindex) && ( (tempformatetc.ptd == formatetc.ptd) || UtCompareTargetDevice(tempformatetc.ptd,formatetc.ptd)) ) { fFlag = FORMAT_GOODMATCH; // keep track of the tymed if( ptymed ) { // this cast is a cute one; // formatetc.tymed is // actually declared to be // a DWORD, since compiler // type-checking is a bad // thing in OLE16. *ptymed = (TYMED) tempformatetc.tymed; } break; } else { fFlag = FORMAT_BADMATCH; if (fNativeOnly) // Only check first cfFormat match if only looking at native Clipboard. break; } } // if cfFormat is in predefined range and don't yet have a value for synthesized mapping, set it. // to point to the Current pFormatEtcData arrays formatEtc. if ( (tempformatetc.cfFormat <= CF_MAXSYNTHESIZED) && (NULL == pformatetcNative[tempformatetc.cfFormat]) ) { pformatetcNative[tempformatetc.cfFormat] = &(pFormatEtcData->_FormatEtc); } ++pFormatEtcData; } // if no match was found in the Enumerator see if it can be synthesized from the // native clipboard. // if have enumerator and couldn't find in either enumerator or synthesized // aspect must be Content and should be one of our synthesized formats that was requested. if (FORMAT_NOTFOUND == fFlag && (formatetc.cfFormat <= CF_MAXSYNTHESIZED) ) { for( i = 0; i < NumSynthesizedFormats; i++ ) { // if format matches synthesized and the apspect has been set check the match // else it could have been set by another format that can be synthesized from. if ( (pSynthesized[i].cfSynthesized == formatetc.cfFormat) && (pformatetcNative[(pSynthesized[i].cfOrig)] != NULL) ) { FORMATETC tempformatetc = *(pformatetcNative[(pSynthesized[i].cfOrig)]); Assert(pSynthesized[i].cfOrig <= CF_MAXSYNTHESIZED); // fix up the ptd if necessary if (tempformatetc.ptd) { tempformatetc.ptd = (DVTARGETDEVICE *) ((BYTE *) m_pFormatEtcDataArray + (ULONG_PTR) tempformatetc.ptd); } if ( ((tempformatetc.dwAspect & formatetc.dwAspect) == formatetc.dwAspect) && (tempformatetc.lindex == formatetc.lindex) && ( (tempformatetc.ptd == formatetc.ptd) || UtCompareTargetDevice(tempformatetc.ptd,formatetc.ptd) ) ) { // leave tymed out param TYMED_NULL, GetData will figure this out as a not found case fFlag = FORMAT_GOODMATCH; } else { // This is a Bad Match. fFlag = FORMAT_BADMATCH; } break; } } } } // if format not found we return not found // This can happen if the format is not on the Clipboard or it is // one of the OLE synthesized formats, // If Didn't find match, the aspect is enforced to be content if ( (FORMAT_NOTFOUND == fFlag) && (formatetc.dwAspect != DVASPECT_CONTENT) ) { fFlag = FORMAT_BADMATCH; } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::MatchFormatetc (" "%lx )\n", this, fFlag)); return fFlag; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::Create (static) // // Synopsis: Creates a new Clipboard data object // // Effects: // // Arguments: [ppDataObj] -- where to put the data object // [cFormats] -- the count of formatetcs // [prgFormats] -- the array of formatetcs (may be NULL) // // Requires: the clipboard must be open // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: Creates a new data object, initializing an internal // formatetc array if g_cfOlePrivateData is available. // // History: dd-mmm-yy Author Comment // 19-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::Create( IDataObject **ppDataObj, FORMATETCDATAARRAY *pFormatEtcDataArray) { HRESULT hresult = NOERROR; CClipDataObject * pClipData; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::Create ( %p )\n", NULL, ppDataObj)); pClipData = new CClipDataObject(); if( pClipData ) { pClipData->m_pFormatEtcDataArray = pFormatEtcDataArray; *ppDataObj = pClipData; } else { hresult = ResultFromScode(E_OUTOFMEMORY); } Assert((NULL == pClipData->m_pFormatEtcDataArray) || (1 == pClipData->m_pFormatEtcDataArray->_cRefs)); LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::Create ( %lx ) " "[ %lx ]\n", NULL, hresult, *ppDataObj)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::QueryInterface // // Synopsis: returns requested interfaces // // Effects: // // Arguments: [riid] -- the requested interface // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Since we always create one of these data objects for // OleGetClipboard, we need to be careful about how we // handle QueryInterface since apps are free to QI for IFoo // // Identity laws: for each object with identity, QI for // IUnknown should always return the same // IUnknown. However, IFoo-->IUnknown-->IFoo // does NOT have to give you back the same // IFoo. We take advantage of this loophole. // // QI for: // IDataObject: always return a pointer to us (the fake // data object) // IFoo: if we can get a pointer back to the // original data object, delegate to it. // Note that a QI back to IDataObject will // not get back to this fake data object // IUnknown: as above, delegate to the real data object // if available. If we're in the remote case, // we'll end up getting the standard identity // object's IUnknown (unless the data object // was custom-marshalled). // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::QueryInterface( REFIID riid, LPVOID *ppvObj ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::QueryInterface " "( %p , %p )\n", this, riid, ppvObj)); // We always return our data object if IDataObject is requested. if(IsEqualIID(riid, IID_IDataObject) || IsEqualIID(riid, IID_IUnknown)) { *ppvObj = this; AddRef(); goto logRtn; } // try to get the remote data object; // GetRealDataObjPtr will set m_pDataObject. GetRealDataObjPtr(); if (m_pDataObject != NULL) { // If we have a real data object, the we use that to give us // the new interface since they probably want something strange hresult = m_pDataObject->QueryInterface(riid, ppvObj); } else { *ppvObj = NULL; hresult = ResultFromScode(E_NOINTERFACE); } logRtn: LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::QueryInterface " "( %lx ) [ %p ]\n", this, hresult, *ppvObj )); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::AddRef // // Synopsis: increments the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG-- the new reference count // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CClipDataObject::AddRef( ) { VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::AddRef ( )\n", this)); ++m_refs; LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::AddRef ( %lu )\n", this, m_refs)); return m_refs; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::InternalAddRef // // Synopsis: Internal Reference count to ensure object stays alive // as long as Clipboard Code needs it. // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG - Remaining Internal Reference Counts. // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Sep-96 rogerg author // // Notes: // //-------------------------------------------------------------------------- ULONG CClipDataObject::InternalAddRef(void) { ++m_Internalrefs; Assert(m_Internalrefs == 1); // Should only have 1 InternalRef on Object. return m_Internalrefs; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::Release // // Synopsis: decrements the reference count on the object // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CClipDataObject::Release( ) { ULONG cRefs; VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::Release ( )\n", this)); Assert( (m_refs > 0) && (m_Internalrefs <= 1) ); if( (cRefs = --m_refs ) == 0 ) { // Release the Real DataObject even when still have internal since if // if Clipboard Object Changes DataObject may not be valid but would still // be use. if (m_pDataObject != NULL) { // Release our reference to the data object. m_pDataObject->Release(); m_pDataObject = NULL; } m_fTriedToGetDataObject = FALSE; Assert(m_hOle1 == NULL); Assert(m_pUnkOle1 == NULL); if (m_Internalrefs == 0) { LEDebugOut((DEB_TRACE, "%p DELETED CClipDataObject\n", this)); delete this; } } // using "this" below is OK, since we only want its value LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::Release ( %lu )\n", this, cRefs)); return cRefs; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::InternalRelease // // Synopsis: Internal Reference count to ensure object stays alive // as long as Clipboard Code needs it. // // Effects: // // Arguments: void // // Requires: // // Returns: DWORD - Number of Internal Reference Counts on the Object. // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Sep-96 rogerg author // // Notes: // //-------------------------------------------------------------------------- ULONG CClipDataObject::InternalRelease(void) { ULONG cRefs; Assert(m_Internalrefs == 1); // InternalRef should always either be 0 or 1. if( (cRefs = --m_Internalrefs ) == 0 && (m_refs == 0) ) { LEDebugOut((DEB_TRACE, "%p DELETED CClipDataObject\n", this)); delete this; } return cRefs; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetData // // Synopsis: retrieves data of the specified format // // Effects: // // Arguments: [pformatetc] -- the requested format // [pmedium] -- where to put the data // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: General algorithm: we *always* duplicate data from the // clipboard and offer it back to the caller in the desired // format. // // GDI objects (BITMAPs, metafiles): these are duplicated // via OleDuplicateData and offered back only if // the requested tymed is correct (i.e. either // TYMED_GDI or TYMED_MFPICT). We do not attempt // to extract the bits and write them into a file, // for example. Note that DIBs are really memory // objects. // // for all other formats (which are flat): // // if asked for TYMED_FILE: create a temporary file and call // GetDataHere. // if asked for TYMED_ISTREAM: create a stream on an hglobal // and call GetDataHere. // if asked for TYMED_HGLOBAL: simply duplicate the data and // return. // if asked for TYMED_ISTORAGE, we will create a storage on // an hglobal and call GetDataHere. GetDataHere // will call StgIsStorageILockBytes to verify that // the data in the HGlobal is really a flattened // storage. This allows apps to pass app-defined // formats as storages. // // Note that we do no checking on whether it is sensible // for data in a particular flat format to be passed on // a storage. StgIsStorageILockBytes will detect that // we can't construct a storage on the flat data, so we // will catch all illegal attempts to get storage data. // // Medium preferences: // GDI objects: only one allowed (depends on format) // Others: ISTORAGE, then HGLOBAL, then ISTREAM, // then FILE. If we know the 'prefered' medium // of the data (from the original formatetc), // then we use the ordering above to find the // first match between what the caller wants // and the 'preferred' mediums of the data. // Otherwise, we use the first medium from the // above list that matches what the caller wants. // // // OLE1 Compatibility: // The basic problem: Ole1 objects only offer cfNative, // cfOwnerLink, and/or cfObjectLink on the clipboard. // We need to translate these into cfEmbedSource, // cfLinkSource, etc. // Basic Algorithm: // First check to see if we can satisfy an OLE2 data // request directly, without medium translation. If so, // then we simply return the data to the user. // Otherwise, we create the Ole2 data and then copy it // into whatever medium the caller desired. Note that // this potentially means an extra allocation, but apps // are not likely to ask for ObjectDescriptor on a // memory stream ;-) // // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo added OLE1 support // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::GetData( LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) { HRESULT hresult = NOERROR; HANDLE handle; TYMED tymedOriginal = TYMED_NULL; BOOL fMatchResult; VDATEHEAP(); VDATETHREAD(this); VDATEPTRIN(pformatetc, FORMATETC); VDATEPTROUT(pmedium, STGMEDIUM); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::GetData ( %p , %p )\n", this, pformatetc, pmedium)); // zero the pmedium structure. _xmemset(pmedium, 0, sizeof(STGMEDIUM)); // try to get the remote data object // GetRealDataObjPtr will set m_pDataObject. GetRealDataObjPtr(); if (m_pDataObject != NULL) { // We have a data object, so just forward that call to the // real data object and then exit the routine since it did // all the work. hresult = m_pDataObject->GetData(pformatetc, pmedium); // WOW HACK alert! Some Win16 apps, like Word6 and XL, // won't work if we continue and offer data in the requested // format anyway. By failing here, we more closely mimic // 16bit OLE behaviour if (hresult == NOERROR || IsWOWThread() ) { goto errRtn; } // If this GetData failed, we just fall through since the // generic code may be able to handle the request } // first, we are going through and verify that we can satisfy // the format and medium request. We will fetch the data in // the subsequent switch statement. // we first need to check to see if the clipboard format is a // user-defined GDI format. We do not know how to duplicate // these, so we can't satisfy the GetData request. if( pformatetc->cfFormat >= CF_GDIOBJFIRST && pformatetc->cfFormat <= CF_GDIOBJLAST ) { hresult = ResultFromScode(DV_E_FORMATETC); goto errRtn; } // There is no DataObject or request for DataObject Failed // then MatchFormat must return it found a match or the aspect // requested must be content. fMatchResult = MatchFormatetc(pformatetc,TRUE /*fNativeOnly */, &tymedOriginal); if (FORMAT_BADMATCH == fMatchResult) { hresult = ResultFromScode(DV_E_FORMATETC); goto errRtn; } // now check for "standard" formats switch( pformatetc->cfFormat ) { case CF_BITMAP: case CF_PALETTE: // TYMED_GDI is the only medium we support. if( (pformatetc->tymed & TYMED_GDI) ) { pmedium->tymed = TYMED_GDI; } else { hresult = ResultFromScode(DV_E_TYMED); goto errRtn; } break; case CF_METAFILEPICT: // TYMED_MFPICT is the only medium we support if( (pformatetc->tymed & TYMED_MFPICT) ) { pmedium->tymed = TYMED_MFPICT; } else { hresult = ResultFromScode(DV_E_TYMED); goto errRtn; } break; case CF_ENHMETAFILE: // TYMED_ENHMF is the only medium we support if( (pformatetc->tymed & TYMED_ENHMF) ) { pmedium->tymed = TYMED_ENHMF; } else { hresult = ResultFromScode(DV_E_TYMED); goto errRtn; } break; // all other formats default: // we prefer TYMED_ISTORAGE, then TYMED_HGLOBAL, then // TYMED_ISTREAM // first check for matches with the 'preferred' // mediums of the data if( tymedOriginal != TYMED_NULL ) { if( ((pformatetc->tymed & TYMED_ISTORAGE) & tymedOriginal) ) { pmedium->tymed = TYMED_ISTORAGE; break; } else if( ((pformatetc->tymed & TYMED_HGLOBAL) & tymedOriginal)) { pmedium->tymed = TYMED_HGLOBAL; break; } else if( ((pformatetc->tymed & TYMED_ISTREAM) & tymedOriginal)) { pmedium->tymed = TYMED_ISTREAM; break; } } // if we didn't match above or if we don't know // the preferred formats, then make a best guess // and keep going. if( (pformatetc->tymed & TYMED_ISTORAGE) ) { pmedium->tymed = TYMED_ISTORAGE; } else if( (pformatetc->tymed & TYMED_HGLOBAL) ) { pmedium->tymed = TYMED_HGLOBAL; } else if( (pformatetc->tymed & TYMED_ISTREAM) ) { pmedium->tymed = TYMED_ISTREAM; } else { hresult = ResultFromScode(DV_E_TYMED); goto errRtn; } break; } // if we get this far, we've successfully picked the medium // on which we want to get our data. For each medium, grab // the data. // If we need to construct OLE2 formats from OLE1 data, // then go ahead and try to fetch the data here. If we can // fetch the data in the desired medium, then go ahead and return. // This optimization saves 1 extra allocation and copy when // retrieving OLE1 data. if( CanRetrieveOle2FromOle1(pformatetc->cfFormat) ) { // // // BEGIN: OPENCLIPBOARD // // hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { goto errRtn; } // now fetch the data. Since we're passing in the caller's // pmedium, this call *may* fail (since GetOle2FromOle1 // *only* retrieves HGLOBAL or the native TYMED). If so, // we'll fetch HGLOBAL from the OleGetClipboardData call // below and then do the appropriate conversion. hresult = GetOle2FromOle1(pformatetc->cfFormat, pmedium); // no matter what the result, we want to close the // clipboard if( !SSCloseClipboard() ) { LEDebugOut((DEB_WARN, "WARNING: CloseClipboard " "failed!\n")); ; // no-op } // // // END: CLOSECLIPBOARD // // if( hresult == NOERROR ) { // we successfully retrieved the Ole2 data the // caller wanted. First reset our state // (*without* freeing the data we're returning // to the caller) and then go ahead and // return. FreeResources(JUST_RESET); goto errRtn; } // FALL-THROUGH. If we weren't able // to retrieve data in the desired format, it probably // means the caller was asking for data on non-primary // medium. The default processing below should take care of // this. // Recall that this code block is an optimization to // avoid multiple allocations and copies in the "normal" // case. } switch( pmedium->tymed ) { case TYMED_HGLOBAL: case TYMED_MFPICT: case TYMED_ENHMF: case TYMED_GDI: // Mini-algorithm: Open the clipboard, fetch and // duplicate the data, then close the clipboard. // we only open the clipboard here because the // GetDataHere call will open the clipboard for // the other cases. (Recall that OpenClipboard and // CloseClipboard are not balanced; only one CloseClipboard // is necessary to actually close the clipboard). // // // BEGIN: OPENCLIPBOARD // // hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { break; } hresult = OleGetClipboardData(pformatetc->cfFormat, &handle); if( hresult == NOERROR ) { // since hGlobal is in a union, we don't need to // explicity assign for each medium type. pmedium->hGlobal = OleDuplicateData(handle, pformatetc->cfFormat, NULL); if( !pmedium->hGlobal ) { hresult = ResultFromScode(E_OUTOFMEMORY); // FALL-THROUGH!!: this is deliberate; we want // to close the clipboard and get out (which is // what the code below does) } } if( !SSCloseClipboard() ) { LEDebugOut((DEB_WARN, "WARNING: CloseClipboard failed!" "\n")); // don't overwrite the original error code if( hresult == NOERROR ) { hresult = ResultFromScode(CLIPBRD_E_CANT_CLOSE); } // FALL-THROUGH!! to the break below; } // // // END: CLOSECLIPBOARD // // break; case TYMED_ISTREAM: // create a memory stream. hresult = CreateStreamOnHGlobal(NULL, TRUE /*fDeleteOnRelease*/, &(pmedium->pstm)); if( hresult != NOERROR ) { break; } hresult = GetDataHere( pformatetc, pmedium ); break; case TYMED_ISTORAGE: // create a memory storage (ILockBytes on top of a // a docfile). hresult = UtCreateStorageOnHGlobal(NULL, TRUE /*fDeleteOnRelease*/, &(pmedium->pstg), NULL); if( hresult != NOERROR ) { break; } hresult = GetDataHere( pformatetc, pmedium ); break; case TYMED_FILE: // create a temporary file pmedium->lpszFileName = (LPOLESTR)PubMemAlloc( MAX_PATH +1 ); if( !pmedium->lpszFileName ) { hresult = ResultFromScode(E_OUTOFMEMORY); break; } hresult = UtGetTempFileName( OLESTR("~OLE"), pmedium->lpszFileName); if( hresult == NOERROR ) { hresult = GetDataHere( pformatetc, pmedium ); } break; default: // should never get here AssertSz(0, "Unknown TYMED for get Data"); hresult = ResultFromScode(E_UNEXPECTED); break; } // NB!!! Do not put any extra processing here without modifying the // error paths in the above switch (they just break, instead of // doing a goto errRtn. This was done to avoid some duplicated // code. if( hresult != NOERROR ) { // ReleaseStgMedium will correctly cleanup NULL and // partially NULL mediums, so we can rely on it for // general-purpose cleanup ReleaseStgMedium(pmedium); } // no matter what the error code, we should reset our state and // free any resources the OLE1 compatibility code may have allocated FreeResources(RESET_AND_FREE); errRtn: LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::GetData ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetDataHere // // Synopsis: retrieves data of the specified format // // Effects: // // Arguments: [pformatetc] -- the requested format // [pmedium] -- where to put the data, if NULL, then // the call is treated as a Query. // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: General algorithm: we *always* duplicate data from the // clipboard and offer it back to the caller in the desired // medium. Since this is GetDataHere, we attempt to copy the // data into the medium provided by the caller. // // GDI objects (BITMAPs, metafiles): *cannot* be retrieved // by GetDataHere, since we do not translate GDI // objects into byte arrays and we cannot copy a // metafile into a metafile (for example). // // for all other formats (which are flat): // // if asked for TYMED_FILE: open the file for create/append and // write the data into it. // if asked for TYMED_ISTREAM: write the data into the stream // if asked for TYMED_HGLOBAL: verify first that the given // hglobal is big enough; if so, then copy the clipboard // data into it. // // if asked for TYMED_ISTORAGE: call StgIsStorageILockBytes // to verify that the data in the HGlobal is really // a flattened storage. This allows apps to pass // app-defined formats as storages. // // OLE1 Compatibility: // OleGetClipboardData will manufacture OLE2 formats from // OLE1 data as necessary. We simply take this handle and // copy it into the caller's medium (as with any other handle). // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo added OLE1 support // 02-Apr-94 alexgo author // // Notes: The spec does not say that a NULL pmedium should be treated // as a Query; however the 16bit code did that and it was fairly // easy for us to duplicate that behaviour. // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::GetDataHere( LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) { HRESULT hresult = NOERROR; HANDLE handle; DWORD cbClipData = 0; BOOL fMatchResult; VDATEHEAP(); VDATETHREAD(this); VDATEPTRIN(pformatetc, FORMATETC); if( pmedium ) { VDATEPTRIN(pmedium, STGMEDIUM); } LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::GetDataHere ( %p , %p" " )\n", this, pformatetc, pmedium)); // try to get the remote data object // GetRealDataObjPtr will set m_pDataObject. GetRealDataObjPtr(); if (m_pDataObject != NULL) { // We have a data object, so just forward that call to the // real data object and then exit the routine since it did // all the work. hresult = m_pDataObject->GetDataHere(pformatetc, pmedium); // If this this failed, we just fall through since the // generic code may be able to handle the request // WOW HACK alert! Some Win16 apps, like Word6 and XL, // won't work if we continue and offer data in the requested // format anyway. By failing here, we more closely mimic // 16bit OLE behaviour if (hresult == NOERROR || IsWOWThread() ) { goto logRtn; } // If this GetDataHere failed, we just fall through since the // generic code may be able to handle the request } // eliminate the GDI case immediately // we do not satisfy requests to GetDataHere for a GDI object // note that DIB's are really memory objects. if( (pformatetc->cfFormat == CF_BITMAP) || (pformatetc->cfFormat == CF_PALETTE) || (pformatetc->cfFormat == CF_METAFILEPICT) || (pformatetc->cfFormat == CF_ENHMETAFILE) || (pformatetc->cfFormat >= CF_GDIOBJFIRST && pformatetc->cfFormat <= CF_GDIOBJLAST )) { hresult = ResultFromScode(DV_E_FORMATETC); goto logRtn; } // There is no DataObject or request for DataObject Failed // then MatchFormat must return it found a match or the aspect // requested must be content. fMatchResult = MatchFormatetc(pformatetc,TRUE /*fNativeOnly */, NULL); if (FORMAT_BADMATCH == fMatchResult) { hresult = ResultFromScode(DV_E_FORMATETC); goto errRtn; } // If pmedium == NULL, then we will just // query and leave. As noted above, this behavior is for 16bit // compatibility. if( !pmedium ) { if( OleIsClipboardFormatAvailable(pformatetc->cfFormat) ) { hresult = NOERROR; } else { hresult = ResultFromScode(DV_E_CLIPFORMAT); } goto logRtn; } // // // BEGIN: OPENCLIPBOARD // // // open the clipboard and retrieve the data. Once we have it, // we'll do a switch and stuff it into the right spot. hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { goto logRtn; } // now actually get the data Assert(pmedium); hresult = OleGetClipboardData(pformatetc->cfFormat, &handle); if( hresult != NOERROR ) { goto errRtn; } // now copy the data into the given medium // for everything but storages, we need to know the size of the // data coming off the clipboard. // note that we have a general problem with comparing sizes-- // GlobalSize returns the size of the *allocated* block, which // is not the same as the size of the real data (which, in // general, we have no way of determining). // When transfering from HGLOBAL to HGBOBAL, we therefore have // a boundary case where we actually have enough room to copy // the *real* data from the clipboard, but the global block // from the clipboard is bigger (causing a failure) // If an app really cares, GetData should be called instead. if( pmedium->tymed != TYMED_ISTORAGE ) { cbClipData = (ULONG) GlobalSize(handle); if( cbClipData == 0 ) { // got bad data from the clipboard hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } } switch( pmedium->tymed ) { case TYMED_HGLOBAL: // if there is enough room to stuff the data in the given // hglobal, then do so. hresult = UtHGLOBALtoHGLOBAL( handle, cbClipData, pmedium->hGlobal); break; case TYMED_ISTREAM: // copy the data into the medium's stream hresult = UtHGLOBALtoStm( handle, cbClipData, pmedium->pstm); break; case TYMED_ISTORAGE: // create a storage on top of the HGLOBAL and CopyTo to the // medium's storage. Note that this will only work if // the HGLOBAL originally had a storage dumped on it hresult = UtHGLOBALtoStorage( handle, pmedium->pstg); break; case TYMED_FILE: // append the data into the file hresult = UtHGLOBALtoFile( handle, cbClipData, pmedium->lpszFileName); break; default: // we can't GetDataHere into GDI objects!!! (etc). hresult = ResultFromScode(DV_E_TYMED); break; } // NB!!: Be careful about adding extra code here; the above // switch does nothing special for error cases. errRtn: if( !SSCloseClipboard() ) { LEDebugOut((DEB_WARN, "WARNING: CloseClipboard failed!\n")); if( hresult == NOERROR ) { hresult = ResultFromScode(CLIPBRD_E_CANT_CLOSE); } } // // // END: CLOSECLIPBOARD // // // now free any resources we may have used for OLE1 compatibility FreeResources(RESET_AND_FREE); logRtn: LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::GetDataHere ( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::QueryGetData // // Synopsis: queries whether a GetData call would succeed // // Effects: // // Arguments: [pformatetc] -- the requested format // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: we simply see if the requested clipboard format is on the // clipboard. // // If we have an array of formatetcs (m_rgFormats from the // g_cfOlePrivateData clipboard data), then we use that info // to check. Otherwise, we will do as much checking as we can // without actually fetching the data. // // Note that this is not 100% accurate (because // we may not be able to get the data in the requested medium // (such as TYMED_ISTORAGE)). Without actually doing a GetData // call, however, this is the best we can do. // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo added OLE1 support // 17-May-94 alexgo removed call to OpenClipboard // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::QueryGetData( LPFORMATETC pformatetc ) { HRESULT hresult = NOERROR; FormatMatchFlag fFlag; VDATEHEAP(); VDATETHREAD(this); VDATEPTRIN(pformatetc, FORMATETC); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::QueryGetData ( %p )\n", this, pformatetc)); // we check if the clipboard format is available *before* // checking the formatetc list as an optimization. If a previous // attempt to render data had failed, then NT will remove that // clipboard format from the clipboard. if( OleIsClipboardFormatAvailable(pformatetc->cfFormat)) { fFlag = MatchFormatetc(pformatetc,FALSE /*fNativeOnly */, NULL); if( fFlag == FORMAT_GOODMATCH ) { hresult = NOERROR; goto errRtn; } else if( fFlag == FORMAT_BADMATCH ) { hresult = ResultFromScode(DV_E_FORMATETC); goto errRtn; } // even if we didn't match in the formatetc list, // continue to check below. We can satisfy // many more GetData requests than the app may // have orginally. // do all the verification we can without actually // fetching the data switch( pformatetc->cfFormat ) { case CF_BITMAP: case CF_PALETTE: // GDI objects must be requested on TYMED_GDI if( pformatetc->tymed != TYMED_GDI ) { hresult = ResultFromScode(DV_E_TYMED); } break; case CF_METAFILEPICT: // metafiles must be on TYMED_MFPICT if( pformatetc->tymed != TYMED_MFPICT ) { hresult = ResultFromScode(DV_E_TYMED); } break; case CF_ENHMETAFILE: // enhanced metafiles must be on TYMED_ENHMF; if( pformatetc->tymed != TYMED_ENHMF ) { hresult = ResultFromScode(DV_E_TYMED); } break; default: // we cannot deal with special GDI objects if( pformatetc->cfFormat >= CF_GDIOBJFIRST && pformatetc->cfFormat <= CF_GDIOBJLAST ) { hresult = ResultFromScode(DV_E_FORMATETC); break; } // we cannot put other formats onto metafiles // or GDI objects // failure case: if somebody requests // TYMED_ISTORAGE but the actually hglobal on the // clipboard does not contain storage-formated data if( pformatetc->tymed == TYMED_GDI || pformatetc->tymed == TYMED_MFPICT || pformatetc->tymed == TYMED_ENHMF ) { hresult = ResultFromScode(DV_E_TYMED); } break; } } else { hresult = ResultFromScode(DV_E_CLIPFORMAT); } errRtn: LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::QueryGetData " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetCanonicalFormatEtc // // Synopsis: retrieve the canonical format // // Effects: // // Arguments: [pformatetc] -- the requested format // [pformatetcOut] -- the canonical format // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: Since we always return the same data for each clipboard // format, this function is very simple (basically returns // the input formatetc, with a NULL target device). // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::GetCanonicalFormatEtc( LPFORMATETC pformatetc, LPFORMATETC pformatetcOut) { VDATEHEAP(); VDATETHREAD(this); VDATEPTRIN(pformatetc, FORMATETC); VDATEPTROUT(pformatetcOut, FORMATETC); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::GetCanonicalFormatEtc" " ( %p , %p )\n", this, pformatetc, pformatetcOut)); // initialize the out param INIT_FORETC(*pformatetcOut); pformatetcOut->cfFormat = pformatetc->cfFormat; pformatetcOut->tymed = pformatetc->tymed; LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::GetCanonicalFormatEtc" " ( %lx )\n", this, NOERROR )); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::SetData // // Synopsis: sets data of the specified format // // Effects: // // Arguments: [pformatetc] -- the format of the data // [pmedium] -- the data // // Requires: // // Returns: E_NOTIMPL // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::SetData( LPFORMATETC pformatetc, LPSTGMEDIUM pmedium, BOOL fRelease) { HRESULT hr; VDATEHEAP(); VDATETHREAD(this); VDATEREADPTRIN(pformatetc, FORMATETC); VDATEREADPTRIN(pmedium, STGMEDIUM); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::SetData ( %p , %p )\n", this, pformatetc, pmedium)); // try to get the remote data object // GetRealDataObjPtr will set m_pDataObject. GetRealDataObjPtr(); if (NULL != m_pDataObject) { hr = m_pDataObject->SetData(pformatetc,pmedium,fRelease); } else { hr = E_FAIL; } LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::SetData ( %lx )\n", this, hr)); return hr; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::EnumFormatEtc // // Synopsis: return an enumerator for the available data formats // // Effects: // // Arguments: [dwDirection] -- the direction (GET or SET) // [ppenum] -- where to put the enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: create a clipboard formatetc enumerator. Upon creation, // we'll grab everything off clipboard we need (so that simple // enumeration doesn't block access to the clipboard). // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::EnumFormatEtc( DWORD dwDirection, LPENUMFORMATETC * ppenum ) { HRESULT hresult; VDATEHEAP(); VDATETHREAD(this); VDATEPTROUT(ppenum, LPENUMFORMATETC); LEDebugOut((DEB_TRACE, "%p _IN CClipDataObject::EnumFormatEtc ( %lx " ", %p )\n", this, dwDirection, ppenum)); // we can only enumerate in the GET direction *ppenum = NULL; if( dwDirection != DATADIR_GET ) { hresult = ResultFromScode(E_NOTIMPL); goto errRtn; } GetFormatEtcDataArray(); // make sure dataArray is Set up. if (m_pFormatEtcDataArray) { *ppenum = new CEnumFormatEtcDataArray(m_pFormatEtcDataArray,0); } hresult = *ppenum ? NOERROR : E_OUTOFMEMORY; errRtn: LEDebugOut((DEB_TRACE, "%p OUT CClipDataObject::EnumFormatEtc ( %lx )" "\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::DAdvise // // Synopsis: register a data advise // // Effects: // // Arguments: [pformatetc] -- the requested format // [dwAdvf] -- advise flags // [pAdvSink] -- the advise sink // [pdwConnection] -- where to put the connection ID // // Requires: // // Returns: OLE_E_ADVISENOTSUPPORTED // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::DAdvise( LPFORMATETC pformatetc, DWORD dwAdvf, IAdviseSink * pAdvSink, DWORD *pdwConnection ) { (void)pformatetc; (void)dwAdvf; (void)pAdvSink; (void)pdwConnection; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_WARN, "WARNING: DAdvise on the clipboard data" "object is not supported!\n")); return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); } //+------------------------------------------------------------------------- // // Member: CClipDataObject::DUnadvise // // Synopsis: unadvises an advise connection // // Effects: // // Arguments: [dwConnection] -- the connection to remove // // Requires: // // Returns: OLE_E_ADVISENOTSUPPORTED // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::DUnadvise(DWORD dwConnection) { (void)dwConnection; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_WARN, "WARNING: DUnadvise on the clipboard data" "object is not supported!\n")); return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); } //+------------------------------------------------------------------------- // // Member: CClipDataObject::EnumDAdvise // // Synopsis: enumerates data advises // // Effects: // // Arguments: [ppenum] -- where to put the enumerator // // Requires: // // Returns: OLE_E_ADVISENOTSUPPORTED // // Signals: // // Modifies: // // Derivation: IDataObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CClipDataObject::EnumDAdvise( LPENUMSTATDATA *ppenum) { (void)ppenum; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_WARN, "WARNING: EnumDAdvise on the clipboard data" "object is not supported!\n")); return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); } // // Private methods on CClipDataObject // //+------------------------------------------------------------------------- // // Member: CClipDataObject::FreeResources (private) // // Synopsis: frees any resources allocated by OLE1 compatibility // code and resets state // // Effects: // // Arguments: [fFlags] -- either JUST_RESET or RESET_AND_FREE // // Requires: // // Returns: void // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- void CClipDataObject::FreeResources( FreeResourcesFlags fFlags ) { VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::FreeResources " "( %lx )\n", this, fFlags)); if( (fFlags & RESET_AND_FREE) ) { if( m_hOle1 ) { GlobalFree(m_hOle1); } if( m_pUnkOle1 ) { m_pUnkOle1->Release(); } } m_hOle1 = NULL; m_pUnkOle1 = NULL; LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::FreeResources " "( )\n", this )); } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetAndTranslateOle1 (private) // // Synopsis: Retrieves either cfOwnerLink or cfObjectLink from the // clipboard, reads the strings and converts to Unicode // // Effects: all strings will be allocated with the public allocator // // Arguments: [cf] -- the clipboard format to retrieve // must be either cfOwnerLink or cfObjectLink // [ppszClass] -- where to put the class name (may be NULL) // [ppszFile] -- where to put the file name (may be NULL) // [ppszItem] -- where to put the item name (may be NULL) // [ppszItemA] -- where to put the ANSI item name // (may be NULL) // // Requires: the clipboard must be open // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: cfOwnerLink and cfObjectLink are laid out as follows // classname\0filename\0\itemname\0\0 // These strings are ANSI, so we must convert to unicode. // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetAndTranslateOle1( UINT cf, LPOLESTR *ppszClass, LPOLESTR *ppszFile, LPOLESTR *ppszItem, LPSTR *ppszItemA ) { LPSTR pszClassA = NULL, pszFileA = NULL, pszItemA = NULL; HGLOBAL hOle1; HRESULT hresult = NOERROR; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetAndTranslate" "Ole1 ( %d , %p , %p , %p )\n", this, cf, ppszClass, ppszFile, ppszItem)); Assert( cf == g_cfOwnerLink || cf == g_cfObjectLink ); // NULL out-params if( ppszClass ) { *ppszClass = NULL; } if( ppszFile ) { *ppszFile = NULL; } if( ppszItem ) { *ppszItem = NULL; } hOle1 = SSGetClipboardData(cf); if( hOle1 == NULL ) { LEDebugOut((DEB_WARN, "WARNING: GetClipboardData Failed!\n")); hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto logRtn; } pszClassA = (LPSTR)GlobalLock(hOle1); if( pszClassA == NULL ) { LEDebugOut((DEB_WARN, "WARNING: GlobalLock failed!\n")); hresult = ResultFromScode(E_OUTOFMEMORY); goto logRtn; } if( ppszClass ) { hresult = UtGetUNICODEData((ULONG) strlen(pszClassA) + 1, pszClassA, NULL, ppszClass); if( hresult != NOERROR ) { goto errRtn; } } pszFileA = pszClassA + strlen(pszClassA) + 1; if( ppszFile ) { hresult = UtGetUNICODEData((ULONG) strlen(pszFileA) + 1, pszFileA, NULL, ppszFile ); if( hresult != NOERROR ) { goto errRtn; } } pszItemA = pszFileA + strlen(pszFileA) +1; if( ppszItem ) { hresult = UtGetUNICODEData((ULONG) strlen(pszItemA) + 1, pszItemA, NULL, ppszItem); if( hresult != NOERROR ) { goto errRtn; } } if( ppszItemA ) { *ppszItemA = UtDupStringA(pszItemA); if( !*ppszItemA ) { hresult = ResultFromScode(E_OUTOFMEMORY); // FALL-THROUGH! no need to goto the error // handling code right below us } } errRtn: GlobalUnlock(hOle1); if( hresult != NOERROR ) { if( ppszClass && *ppszClass ) { PubMemFree(*ppszClass); *ppszClass = NULL; } if( ppszFile && *ppszFile ) { PubMemFree(*ppszFile); *ppszFile = NULL; } if( ppszItem && *ppszItem ) { PubMemFree(*ppszItem); *ppszItem = NULL; } #if DBG == 1 // if this assert goes off, then we added more code // without modifying the error paths for ansi item strings if( ppszItemA ) { Assert(*ppszItem == NULL ); } #endif // DBG ==1 } logRtn: LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetAndTranslate" "Ole1 ( %lx ) [ %p , %p , %p ]\n", this, hresult, (ppszClass)? *ppszClass : 0, (ppszFile) ? *ppszFile : 0, (ppszItem) ? *ppszItem : 0 )); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetEmbeddedObjectFromOle1 // // Synopsis: synthesizes cfEmbeddedObject from available OLE1 // data. // // Effects: // // Arguments: [pmedium] -- where to put the requested data // // Requires: The clipboard must be OPEN // we must have verified that the correct formats are // available before calling // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: create a memory-based stroage and stuff the following // infomation in it: // clsid StdOleLink // an available presentation // the class from OwnerLink data as the user type // link information // // Much of this work is done by the helper function // GenericObjectToIStorage // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo author // // Notes: This code is largely based from 16bit OLE sources // REVIEW: we may want to rework portions of this code, // particularly if we rewrite the GENOBJ code (in // ostm2stg.cpp). // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetEmbeddedObjectFromOle1( STGMEDIUM *pmedium ) { HRESULT hresult; IStorage * pstg = NULL; LPOLESTR pszClass = NULL; ILockBytes * plockbytes = NULL; BOOL fDeleteOnRelease = TRUE; GENOBJ genobj; HGLOBAL hglobal; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetEmbeddedObject" "FromOle1 ( %p )\n", this, pmedium)); // if we are asking for EmbeddedObject on an hglobal, then we // don't want to delete the hglobal when we release the storage if( pmedium->tymed == TYMED_HGLOBAL ) { fDeleteOnRelease = FALSE; } genobj.m_class.Set(CLSID_StdOleLink, NULL); // the destructor for the generic object will free the // presentation. genobj.m_ppres = new PRES; if( genobj.m_ppres == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } genobj.m_fLink = TRUE; genobj.m_lnkupdopt = UPDATE_ALWAYS; if( SSIsClipboardFormatAvailable(CF_METAFILEPICT)) { hglobal = SSGetClipboardData(CF_METAFILEPICT); if( hglobal ) { if( (hresult = MfToPres(hglobal, genobj.m_ppres)) != NOERROR) { goto errRtn; } } else { LEDebugOut((DEB_WARN, "WARNING: Unable to " "retrieve CF_METAFILEPICT\n")); hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } } else if( SSIsClipboardFormatAvailable(CF_DIB) ) { hglobal = SSGetClipboardData(CF_DIB); if( hglobal ) { // DibToPres will take ownership of the // hglobal HGLOBAL hTemp; hTemp = UtDupGlobal(hglobal, GMEM_DDESHARE | GMEM_MOVEABLE); if( !hTemp ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } if( (hresult = DibToPres(hTemp, genobj.m_ppres)) != NOERROR ) { GlobalFree(hTemp); goto errRtn; } } else { LEDebugOut((DEB_WARN, "WARNING: Unable to " "retrieve CF_DIB\n")); hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } } else if (SSIsClipboardFormatAvailable(CF_BITMAP)) { hglobal = SSGetClipboardData(CF_BITMAP); if( hglobal ) { if( (hresult = BmToPres(hglobal, genobj.m_ppres)) != NOERROR ) { goto errRtn; } } else { LEDebugOut((DEB_WARN, "WARNING: Unable to " "retrieve CF_BITMAP\n")); hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } } else { delete genobj.m_ppres; genobj.m_ppres = NULL; genobj.m_fNoBlankPres = TRUE; } hresult = GetAndTranslateOle1(g_cfOwnerLink, &pszClass, &genobj.m_szTopic, &genobj.m_szItem, NULL); if( hresult != NOERROR ) { goto errRtn; } genobj.m_classLast.SetSz(pszClass); // now we need to create a storage to stuff the generic object // into. hresult = UtCreateStorageOnHGlobal(NULL, fDeleteOnRelease, &pstg, &plockbytes); if( hresult != NOERROR ) { goto errRtn; } hresult = GenericObjectToIStorage(genobj, pstg, NULL); if (SUCCEEDED(hresult)) { hresult = NOERROR; } if( IsOwnerLinkStdOleLink() && SSIsClipboardFormatAvailable( g_cfNative) ) { // Case of copying an OLE 2 link from a 1.0 container. // The first part of this function created a presentation // stream from the presentation on the clipboard. The // presentation is NOT already inside the Native data (i.e., // the cfEmbeddedObject) because we removed it to conserve // space. hglobal = SSGetClipboardData(g_cfNative); if( hglobal == NULL ) { LEDebugOut((DEB_WARN, "WARNING: GetClipboardData for " "cfNative failed!\n")); hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } // now stuff the native data into the storage, first // removing any presentation streams that may have // previously existed. hresult = NativeToStorage(pstg, hglobal); } // finished!! now fill out the pmedium argument and return if( pmedium->tymed == TYMED_ISTORAGE ) { // hang onto the storage, in case we need to release // it later m_pUnkOle1 = (IUnknown *)pstg; pmedium->pstg = pstg; // NO AddRef } else { Assert(pmedium->tymed == TYMED_HGLOBAL); hresult = GetHGlobalFromILockBytes(plockbytes, &pmedium->hGlobal); // GetHGLOBAL should never fail here because we // just created the ILockBytes!! Assert( hresult == NOERROR ); // in this case, we want to release the storage // and save the hglobal for later delete m_hOle1 = pmedium->hGlobal; pstg->Release(); pstg = NULL; } errRtn: // if there was an error, we need to blow away any storage // that we may have created if( hresult != NOERROR ) { if( pszClass ) { PubMemFree(pszClass); } if( pstg ) { pstg->Release(); Assert(m_pUnkOle1 == NULL); } } // no matter what, we need to release our lockbytes if( plockbytes ) { // in case of failure we need to make sure the HGLOBAL // used by plockbytes also gets freed - fDeleteOnRelease // tells if plockbytes->Release will do that work for us if (FAILED(hresult) && !fDeleteOnRelease) { HRESULT hrCheck; // Preserve hresult // GetHGlobal should never fail here because we just // created the ILockBytes hrCheck = GetHGlobalFromILockBytes(plockbytes, &hglobal); Assert(NOERROR == hrCheck); // GlobalFree returns NULL on success hglobal = GlobalFree(hglobal); Assert(hglobal == NULL); } plockbytes->Release(); } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetEmbeddedObject" "FromOle1 ( %lx ) \n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetEmbedSourceFromOle1 (private) // // Synopsis: synthesizes cfEmbedSource from available OLE1 data // // Effects: // // Arguments: [pmedium] -- where to put the resulting data // // Requires: The clipboard must be OPEN // we must have verified that the correct formats are // available before calling and *while* the clipboard // is open (to avoid a race condition) // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: create a memory-based storage and stuff the following // information in it: // the clsid of the embedding // the class name as the user type // the native data in the OLE10_NATIVE_STREAM // the item name in the OLE10_ITEMNAME_STREAM // // History: dd-mmm-yy Author Comment // 17-Aug-94 alexgo fix the check for OLE2 data to handle // OLE2 treat as from OLE1 // 03-Aug-94 AlexT Check for OLE 2 data // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetEmbedSourceFromOle1( STGMEDIUM *pmedium ) { HRESULT hresult; IStorage * pstg = NULL; HGLOBAL hNative; HGLOBAL hCopy = NULL; LPOLESTR pszClass = NULL; LPSTR pszItemA = NULL; CLSID clsid; ILockBytes * plockbytes = NULL; BOOL fDeleteOnRelease = TRUE; BOOL fIsOle1 = TRUE; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetEmbedSourceFrom" "Ole1 ( %p )\n", this, pmedium)); Assert(SSIsClipboardFormatAvailable(g_cfOwnerLink)); Assert(SSIsClipboardFormatAvailable(g_cfNative)); // first fetch the class name of the object hresult = GetAndTranslateOle1( g_cfOwnerLink, &pszClass, NULL, NULL, &pszItemA ); if( hresult != NOERROR ) { goto errRtn; } // now fetch the clsid for the embedding hresult = wCLSIDFromProgID(pszClass, &clsid, TRUE); if( hresult != NOERROR ) { goto errRtn; } // if we are asking for EmbedSource on an hglobal, then we // don't want to delete the hglobal when we release the storage if( pmedium->tymed == TYMED_HGLOBAL ) { fDeleteOnRelease = FALSE; } // now fetch the native data hNative = SSGetClipboardData(g_cfNative); if( hNative == NULL ) { hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } if (!CoIsOle1Class(clsid)) { // Just because the clsid is OLE2 does not mean that the // underlying data is OLE2. For example, suppose a container // copies an old OLE1 object to the clipboard, but the OLE2 // version of that object has been installed on the system. // CLSIDFromProgID will return the *OLE2* class ID in this case. // If we're in this case, then we should fall through and treat // the data as normal OLE1 (StgOpenStorageOnILockBytes would // fail in any event). hresult = CreateILockBytesOnHGlobal(hNative, FALSE, &plockbytes); if( hresult != NOERROR ) { goto errRtn; } hresult = StgIsStorageILockBytes(plockbytes); plockbytes->Release(); plockbytes = NULL; if( hresult == NOERROR ) { // the hNative data really contains a serialized // IStorage. // // This will arise in two cases: // 1. Publisher 2.0, 16bit put data on the // clipboard. They do not call OLE api's but // instead synthesize the same data that 16bit // OleSetClipboard would. // // 2. An OLE1.0 container copies an OLE2 embedding // to the clipboard. fIsOle1 = FALSE; hCopy = UtDupGlobal(hNative, GMEM_DDESHARE | GMEM_MOVEABLE); if( NULL == hCopy ) { hresult = E_OUTOFMEMORY; goto errRtn; } // create plockbytes hresult = CreateILockBytesOnHGlobal(hCopy, fDeleteOnRelease, &plockbytes); if( hresult != NOERROR ) { goto errRtn; } // the HGLOBAL in plockbytes can change, so we // can't do anything with hCopy; we NULL it out // to make sure we don't try to free it hCopy = NULL; hresult = StgOpenStorageOnILockBytes(plockbytes, NULL, STGM_SALL, NULL, 0, &pstg); if (FAILED(hresult)) { goto errRtn; } // We explicitly ignore any error returned by the // following UtDoStreamOperation(pstg, NULL, OPCODE_REMOVE, STREAMTYPE_CACHE); } // else the data is really OLE1 and is just being emulated by // an OLE2 object // Just fall through to the code below which will // stuff hNative into the OLE10_NATIVE_STREAM } // this will be TRUE if the clsid is OLE1 or if the clsid is OLE2 // but the data in hNative is OLE1 anyway (see comments above) if( fIsOle1 == TRUE ) { hresult = UtCreateStorageOnHGlobal( NULL, fDeleteOnRelease, &pstg, &plockbytes); if( hresult != NOERROR ) { goto errRtn; } // we need to stuff the class id of the embedding into the // storage // REVIEW: this clsid may be an OLE2 class id. This could // cause us trouble in treat as scenarios. hresult = pstg->SetClass(clsid); if( hresult != NOERROR ) { goto errRtn; } // store the user type information, etc in our private data // streams if RegisterClipboardFormat fails, it will return 0, // which is OK for us. hresult = WriteFmtUserTypeStg(pstg, (CLIPFORMAT) RegisterClipboardFormat(pszClass), pszClass); if( hresult != NOERROR ) { goto errRtn; } // now stuff the native data into the OLE10_NATIVE_STREAM // this is a little worker function found in utstream.cpp // which will stuff the hglobal to the OLE1 data into the // right stream. // the OLE1 DDE stuff also uses this function. // REVIEW: // the FALSE flag is confusing here, it's supposed to be // fIsOle1Interop. 16bit clipboard sources passed FALSE when doing // their 1.0 interop stuff, so we'll do that here. When we // overhaul the main 1.0 interop stuff, we should change this flag // to be something more intuitive. hresult = StSave10NativeData(pstg, hNative, FALSE); if( hresult != NOERROR ) { goto errRtn; } // If we have an item name, then stuff that into // OLE10_ITEMNAME_STREAM if( pszItemA && pszItemA[0] != '\0' ) { hresult = StSave10ItemName(pstg, pszItemA); } } // this Commit call in non-intuitive. Basically, we may // try to get the underlying hglobal (see below) *before* // we release the storage. The commit guarantees that all // the important state information gets flushed to the // hglobal (which is not otherwise guaranteed). hresult = pstg->Commit(STGC_DEFAULT); if( hresult != NOERROR ) { goto errRtn; } // FINIS!! // now fill out all of the arguments if( pmedium->tymed == TYMED_ISTORAGE ) { // hang onto the storage, in case we need to release // it later m_pUnkOle1 = (IUnknown *)pstg; pmedium->pstg = pstg; // NO AddRef } else { Assert(pmedium->tymed == TYMED_HGLOBAL); hresult = GetHGlobalFromILockBytes(plockbytes, &pmedium->hGlobal); // GetHGLOBAL should never fail here because we // just created the ILockBytes!! Assert( hresult == NOERROR ); // in this case, we want to release the storage // and save the hglobal for later delete m_hOle1 = pmedium->hGlobal; pstg->Release(); pstg = NULL; } errRtn: // we are done with our strings if( pszClass ) { PubMemFree(pszClass); } if( pszItemA ) { PubMemFree(pszItemA); } // if there was an error, we need to blow away any storage // that we may have created if( hresult != NOERROR ) { if( pstg ) { pstg->Release(); m_pUnkOle1 = NULL; } } // no matter what, we need to release our lockbytes. if( plockbytes ) { // in case of failure we need to make sure the HGLOBAL // used by plockbytes also gets freed - fDeleteOnRelease // tells if plockbytes->Release will do that work for us if (FAILED(hresult) && !fDeleteOnRelease) { HRESULT hrCheck; // Preserve hresult // GetHGlobal should never fail here because we just // created the ILockBytes hrCheck = GetHGlobalFromILockBytes(plockbytes, &hCopy); Assert(NOERROR == hrCheck); // hCopy will be freed below } plockbytes->Release(); } if (NULL != hCopy) { // GlobalFree returns NULL on success hCopy = GlobalFree(hCopy); Assert(NULL == hCopy); } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetEmbedSource" "FromOle1 ( %lx ) \n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetLinkSourceFromOle1 (private) // // Synopsis: Synthesizes cfLinkSource format from OLE1 data // // Effects: // // Arguments: [pmedium] -- where to put the data // // Requires: the clipboard must be open // we must have verified that the correct formats are // available before calling and *while* the clipboard // is open (to avoid a race condition) // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: Gets the clsid, filename, and item name for the OLE1 object // and creates an OLE1 file moniker. This moniker is then // saved into a memory stream // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // 05-Aug-94 AlexT Link Source also needs class id // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetLinkSourceFromOle1( STGMEDIUM *pmedium ) { HRESULT hresult; IStream * pstm = NULL; LPMONIKER pmkFile = NULL, pmkFinal = NULL, pmkItem = NULL; IPersistStream *pPersistStream = NULL; LPOLESTR pszClass = NULL, pszFile = NULL, pszItem = NULL; CLSID clsid; UINT cf; BOOL fDeleteOnRelease = TRUE; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetLinkSourceFrom" "Ole1 ( %p , %p )\n", this, pmedium )); // fetch the info we need from cfOwnerLink or cfObjectLink // If this function is called, we should have already determined // that the formats were available in the correct order // for creating links. if( SSIsClipboardFormatAvailable(g_cfObjectLink) ) { cf = g_cfObjectLink; } else { cf = g_cfOwnerLink; Assert(SSIsClipboardFormatAvailable(g_cfOwnerLink)); } hresult = GetAndTranslateOle1(cf, &pszClass, &pszFile, &pszItem, NULL); if( hresult != NOERROR ) { goto errRtn; } // now fetch the clsid for the OLE1 server hresult = wCLSIDFromProgID(pszClass, &clsid, TRUE); if( hresult != NOERROR ) { goto errRtn; } // now build up our moniker hresult = CreateOle1FileMoniker(pszFile, clsid, &pmkFile); if( hresult != NOERROR ) { goto errRtn; } if( pszItem && pszItem[0] != OLESTR('\0') ) { hresult = CreateItemMoniker(OLESTR("!"), pszItem, &pmkItem); if( hresult != NOERROR ) { goto errRtn; } hresult = CreateGenericComposite(pmkFile, pmkItem, &pmkFinal); if( hresult != NOERROR ) { goto errRtn; } } else { pmkFinal = pmkFile; // this addref is done so we can release all of our // monikers at once (i.e., the variables pmkFinal // and pmkFile will both be released) pmkFinal->AddRef(); } // pmkFinal now contains the moniker we need. Create a // memory stream and save the moniker into it. // if we are asking for LinkSource on an hglobal, then we // don't want to delete the hglobal when we release the stream if( pmedium->tymed == TYMED_HGLOBAL ) { fDeleteOnRelease = FALSE; } hresult = CreateStreamOnHGlobal(NULL, fDeleteOnRelease, &pstm); if( hresult != NOERROR ) { goto errRtn; } hresult = pmkFinal->QueryInterface(IID_IPersistStream, (LPLPVOID)&pPersistStream); // we implemented this file moniker, it should support // IPersistStream Assert(hresult == NOERROR); hresult = OleSaveToStream(pPersistStream, pstm); if( hresult != NOERROR ) { goto errRtn; } hresult = WriteClassStm(pstm, clsid); if (hresult != NOERROR) { goto errRtn; } // no matter what, we should save the stream so we can clean // up and release our resources if needed if( pmedium->tymed == TYMED_ISTREAM ) { // save the stream, in case we need to release it later m_pUnkOle1 = (IUnknown *)pstm; pmedium->pstm = pstm; } else { Assert(pmedium->tymed == TYMED_HGLOBAL); hresult = GetHGlobalFromStream(pstm, &(pmedium->hGlobal)); // since we created the memory stream, the GetHGlobal // should never fail Assert(hresult == NOERROR); // in this case, we want to release the stream and hang // onto the hglobal m_hOle1 = pmedium->hGlobal; pstm->Release(); pstm = NULL; } errRtn: if( pPersistStream ) { pPersistStream->Release(); } if( pmkFile ) { pmkFile->Release(); } if( pmkItem ) { pmkItem->Release(); } if( pmkFinal ) { pmkFinal->Release(); } if( pszClass ) { PubMemFree(pszClass); } if( pszFile ) { PubMemFree(pszFile); } if( pszItem ) { PubMemFree(pszItem); } if( hresult != NOERROR ) { if( pstm ) { HRESULT hrCheck; if (!fDeleteOnRelease) { // pstm->Release will not free the underlying // HGLOBAL, so we need to do so ourselves HGLOBAL hgFree; hrCheck = GetHGlobalFromStream(pstm, &hgFree); // since we created the memory stream, the GetHGlobal // should never fail Assert(hrCheck == NOERROR); // GlobalFree returns NULL on success hgFree = GlobalFree(hgFree); Assert(NULL == hgFree); } pstm->Release(); m_pUnkOle1 = NULL; } } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetLinkSourceFrom" "Ole1 ( %lx ) [ %p , %p ]\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetObjectDescriptorFromOle1 (private) // // Synopsis: retrieves a UNICODE object descriptor from OLE1 data // // Effects: // // Arguments: [cf] -- the OLE1 clipboard format to use // [pmedium] -- where to put the hglobal // // Requires: the clipboard must be open // cf must be eith OwnerLink or ObjectLink // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: Calls CreateObjectDesciptor // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetObjectDescriptorFromOle1( UINT cf, STGMEDIUM *pmedium ) { HRESULT hresult; HGLOBAL hglobal; LPOLESTR pszClass = NULL, pszFile = NULL, pszItem = NULL, pszSrcOfCopy = NULL; CLSID clsid; const SIZEL sizel = {0, 0}; const POINTL pointl = {0, 0}; OLECHAR szFullName[256]; LONG cb = sizeof(szFullName); // RegQueryValue takes // bytes!! VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetObjectDescriptor" "FromOle1 ( %d , %p )\n", this, cf, pmedium)); Assert(cf == g_cfOwnerLink || cf == g_cfObjectLink); // fetch the data we need hresult = GetAndTranslateOle1( cf, &pszClass, &pszFile, &pszItem, NULL); if( hresult != NOERROR ) { goto errRtn; } hresult = wCLSIDFromProgID(pszClass, &clsid, TRUE); if( hresult != NOERROR ) { goto errRtn; } // now fetch the full user name of the object. This info // is found in the registry. if( QueryClassesRootValue(pszClass, szFullName, &cb) != 0 ) { // uh-oh, it failed for some reason. The class name // (potentially OLE2Link) was probably not registered, so // just use the class name. // // NB!! 16bit did no error checking for this case, so // szFullName in their equivalent code would be left as // a NULL string. This had the effect of making a // blank entry in most paste-special dialogs. _xstrcpy(szFullName, pszClass); } // build up the SourceOfCopy string. It will be a concatenation // of the Filename and Item name that we retrieved from the // Owner/ObjectLink OLE1 structures pszSrcOfCopy = (LPOLESTR)PrivMemAlloc( (_xstrlen(pszFile) + _xstrlen(pszItem) + 2) * sizeof(OLECHAR)); if( pszSrcOfCopy == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } _xstrcpy(pszSrcOfCopy, pszFile); if( pszItem && *pszItem != OLESTR('\0') ) { _xstrcat(pszSrcOfCopy, OLESTR("\\")); _xstrcat(pszSrcOfCopy, pszItem); } // create an object descriptor hglobal = CreateObjectDescriptor(clsid, DVASPECT_CONTENT, &sizel, &pointl, (OLEMISC_CANTLINKINSIDE | OLEMISC_CANLINKBYOLE1), szFullName, pszSrcOfCopy); if( hglobal == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } // now fill in out params Assert(pmedium->tymed == TYMED_HGLOBAL); pmedium->hGlobal = hglobal; // we need to save the hglobal so we can free it later if need // be m_hOle1 = hglobal; errRtn: if( pszClass ) { PubMemFree(pszClass); } if( pszFile ) { PubMemFree(pszFile); } if( pszItem ) { PubMemFree(pszItem); } if( pszSrcOfCopy ) { // NB!! This was allocated with *private* memory PrivMemFree(pszSrcOfCopy); } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetObjectDescriptor" "FromOle1 ( %lx )\n", this, hresult )); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::GetOle2FromOle1 (private) // // Synopsis: synthesize the given ole2 format from available ole1 data // // Effects: // // Arguments: [cf] -- the clipboard format to synthesize // [pmedium] -- where to put the data // // Requires: the clipboard must be open // CanRetrieveOle2FromOle1 should have succeeded before calling // this function // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::GetOle2FromOle1( UINT cf, STGMEDIUM *pmedium ) { HRESULT hresult = ResultFromScode(DV_E_TYMED); VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::GetOle2FromOle1 " "( %d , %p )\n", this, cf, pmedium)); if( cf == g_cfEmbedSource ) { // we can only fetch EmbedSource on an hglobal or storage if( pmedium->tymed == TYMED_HGLOBAL || pmedium->tymed == TYMED_ISTORAGE ) { hresult = GetEmbedSourceFromOle1(pmedium); } } else if( cf == g_cfEmbeddedObject ) { // we can only fetch EmbeddedObject on an hglobal or storage if( pmedium->tymed == TYMED_HGLOBAL || pmedium->tymed == TYMED_ISTORAGE ) { hresult = GetEmbeddedObjectFromOle1(pmedium); } } else if( cf == g_cfLinkSource ) { // we can only fetch LinkSource on an hglobal or stream if( pmedium->tymed == TYMED_HGLOBAL || pmedium->tymed == TYMED_ISTREAM ) { hresult = GetLinkSourceFromOle1(pmedium); } } else if( cf == g_cfObjectDescriptor ) { // we can only fetch this on an hglobal if( pmedium->tymed == TYMED_HGLOBAL ) { hresult = GetObjectDescriptorFromOle1(g_cfOwnerLink, pmedium); } } else if( cf == g_cfLinkSrcDescriptor ) { // we can only fetch this on an hglobal. Note that // a link source descriptor is really an object descriptor // also, we can use either ObjectLink or OwnerLink as the // the data source, but the only time it is valid to use // OwnerLink is if ObjectLink is not available if( pmedium->tymed == TYMED_HGLOBAL ) { UINT cfOle1; if( SSIsClipboardFormatAvailable(g_cfObjectLink) ) { cfOle1 = g_cfObjectLink; } else { cfOle1 = g_cfOwnerLink; } hresult = GetObjectDescriptorFromOle1(cfOle1, pmedium); } } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::GetOle2FromOle1 " "( %lx )\n", this, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::OleGetClipboardData (private) // // Synopsis: private replacement for GetClipboardData that synthesizes // OLE2 formats from OLE1 data if necessary // // Effects: // // Arguments: [cf] -- the clipboard format to use // [phglobal] -- where to put the fetched data // // Requires: the clipboard must be open // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: // // Algorithm: Try to fetch the request format; if that fails then // try to synthesize the data from OLE1 // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT CClipDataObject::OleGetClipboardData( UINT cf, HGLOBAL *phglobal ) { HRESULT hresult = NOERROR; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::OleGetClipboard" "Data ( %x , %p )\n", this, cf, phglobal )); Assert(phglobal); *phglobal = NULL; // fetch the real data, if available if( SSIsClipboardFormatAvailable(cf) ) { *phglobal = SSGetClipboardData(cf); } else if( CanRetrieveOle2FromOle1(cf) ) { STGMEDIUM medium; medium.tymed = TYMED_HGLOBAL; hresult = GetOle2FromOle1(cf, &medium); if( hresult == NOERROR ) { *phglobal = medium.hGlobal; } } else { hresult = ResultFromScode(DV_E_FORMATETC); } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::OleGetClipboardData" " ( %lx ) [ %lx ]\n", this, hresult, *phglobal)); return hresult; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::OleIsClipboardFormatAvailable (private) // // Synopsis: determines whether a clipboard format is available or // can be synthesized from available formats // // Effects: // // Arguments: [cf] -- the clipboard format to check for // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Jun-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- BOOL CClipDataObject::OleIsClipboardFormatAvailable( UINT cf ) { BOOL fRet; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CClipDataObject::OleIsClipboard" "FormatAvailable ( %d )\n", this, cf)); if( !SSIsClipboardFormatAvailable(cf) ) { // if the clipboard format is not normally available, see // if we can make it from available formats fRet = CanRetrieveOle2FromOle1(cf); } else { fRet = TRUE; } LEDebugOut((DEB_ITRACE, "%p OUT CClipDataObject::OleIsClipboard" "FormatAvailable ( %lu )\n", this, fRet )); return fRet; } //+------------------------------------------------------------------------- // // Member: CClipDataObject::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 CClipDataObject::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; unsigned int ui; char *pszPrefix; char *pszCThreadCheck; char *pszFORMATETC; dbgstream dstrPrefix; dbgstream dstrDump(500); // 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); dstrDump << pszPrefix << "No. of References = " << m_refs << endl; dstrDump << pszPrefix << "Handle OLE2 -> OLE1 data = " << m_hOle1 << endl; dstrDump << pszPrefix << "pIUnknown to OLE1 data = " << m_pUnkOle1 << endl; dstrDump << pszPrefix << "pIDataObject = " << m_pDataObject << endl; dstrDump << pszPrefix << "TriedToGetDataObject? = "; if (m_fTriedToGetDataObject == TRUE) { dstrDump << "TRUE" << endl; } else { dstrDump << "FALSE" << endl; } // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCClipDataObject, public (_DEBUG only) // // Synopsis: calls the CClipDataObject::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pCDO] - pointer to CClipDataObject // [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 *DumpCClipDataObject(CClipDataObject *pCDO, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pCDO == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pCDO->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG // // OLE1 support methods // //+------------------------------------------------------------------------- // // Function: BmToPres // // Synopsis: copies a bitmap into a presentation object // // Effects: // // Arguments: [hBM] -- handle to the bitmap // [ppres] -- the presentation object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Algorithm: converts the bitmap to a DIB and then calls DibToPres // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo port from 16bit // // Notes: This code is largely based from 16bit OLE sources // REVIEW: we may want to rework portions of this code, // particularly if we rewrite the PPRES/GENOBJ code (in // ostm2stg.cpp). // //-------------------------------------------------------------------------- HRESULT BmToPres(HANDLE hBM, PPRES ppres) { HANDLE hDib; HRESULT hresult; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN BmToPres ( %lx , %p )\n", NULL, hBM, ppres)); if( (hDib = UtConvertBitmapToDib((HBITMAP)hBM, NULL)) ) { // this routine keeps hDib, it doesn't make a copy of it hresult = DibToPres(hDib, ppres); } else { hresult = ResultFromScode(E_OUTOFMEMORY); } LEDebugOut((DEB_ITRACE, "%p OUT BmToPres ( %lx )\n", NULL, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Member: CanRetrieveOle2FromOle1 (private) // // Synopsis: Determines whether we can synthesize the asked for // ole2 format from the formats available on the clipboard. // Also checks to see if the *real* OLE2 format is available. // // Effects: does not need to open the clipboard // // Arguments: [cf] -- the clipboard format to check for // // Requires: // // Returns: TRUE if we can synthesize the requested format AND the // real format is NOT available // FALSE otherwise // // Signals: // // Modifies: // // Derivation: // // Algorithm: for cfEmbedSource: // cfNative and cfOwnerLink must be present and // cfNative must precede cfOwnerLink and cfOwnerLink // must not represent a StdOleLink // // for cfEmbeddedObject: // cfOwnerLink must be present and cfNative // must not be not present OR // // cfNative must come after cfOwnerLink OR // // cfNative precedes cfOwnerLink and // cfOwnerLink represents a StdOleLink // // for cfLinkSource: // cfObjectLink must be present OR // // both cfNative and cfOwnerLink must be present // and cfOwnerLink must precede cfNative // // for cfObjectDescriptor or cfLinkSrcDescriptor // either cfObjectLink or cfOwnerLink must be // available // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo added support for EmbeddedObject // retrieval ala 16bit OLE // 04-Jun-94 alexgo author // // Notes: We don't want to synthesize OLE2 formats from OLE1 // if the real OLE2 formats are available because the OLE2 // formats probably contain more information. // // We sometimes need to open the clipboard to accurately // fetch enough information to satisfy a query on // EmbedSource, EmbeddedObject or LinkSource. // Since the clipboard is a global resource, we // must only open it for brief periods of time. // //-------------------------------------------------------------------------- BOOL CanRetrieveOle2FromOle1( UINT cf ) { BOOL fRet = FALSE, fOwnerLink = FALSE, fNative = FALSE, fOpenedClipboard = FALSE; UINT cfFirst = 0, // the first format available cfTemp; HRESULT hresult; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN CanRetrieveOle2From" "Ole1 ( %d )\n", NULL, cf )); if( SSIsClipboardFormatAvailable(g_cfOlePrivateData) ) { // if we put the data on the clipboard, assume only OLE2 // data transfers goto errRtn; } // first check for LinkSourceDescriptor or ObjectDescriptor, as // we do not need to open the clipboard for these. if( cf == g_cfObjectDescriptor ) { // we must have either OwnerLink or ObjectLink if( !SSIsClipboardFormatAvailable(g_cfObjectDescriptor) && (SSIsClipboardFormatAvailable(g_cfObjectLink) || SSIsClipboardFormatAvailable(g_cfOwnerLink) ) ) { fRet = TRUE; } goto errRtn; } if( cf == g_cfLinkSrcDescriptor ) { // we must have either OwnerLink or ObjectLink if( !SSIsClipboardFormatAvailable(g_cfLinkSrcDescriptor) && (SSIsClipboardFormatAvailable(g_cfObjectLink) || SSIsClipboardFormatAvailable(g_cfOwnerLink) ) ) { fRet = TRUE; } goto errRtn; } // now check for the remaining OLE2 formats EmbedSource, // EmbeddedObject, and LinkSource. if( (cf == g_cfEmbedSource) || (cf == g_cfEmbeddedObject) || (cf == g_cfLinkSource) ) { // we need to open the clipboard so our calls to // EnumClipboardFormats and GetClipboardData will work. // however, the caller of this function may have already // opened the clipboard, so we need to check for this. // // // BEGIN: OPENCLIPBOARD // // if( GetOpenClipboardWindow() != GetPrivateClipboardWindow(CLIP_QUERY) ) { hresult = OleOpenClipboard(NULL, NULL); if( hresult != NOERROR ) { // if we can't open the clipboard, // then we can't accurately determine // if we can fetch the requested // data. Assume that we can't // and return. fRet = FALSE; goto errRtn; } fOpenedClipboard = TRUE; } // we now need to determine the ordering of the clipboard // formats Native and OwnerLink. OLE1 specifies different // behaviour based on the order in which these formats // appear (see the Algorithm section for details) fNative = SSIsClipboardFormatAvailable(g_cfNative); fOwnerLink = SSIsClipboardFormatAvailable(g_cfOwnerLink); if( fNative && fOwnerLink ) { cfTemp = 0; while( (cfTemp = SSEnumClipboardFormats(cfTemp)) != 0 ) { if( cfTemp == g_cfNative ) { cfFirst = g_cfNative; break; } else if( cfTemp == g_cfOwnerLink ) { cfFirst = g_cfOwnerLink; break; } } } if( cf == g_cfEmbeddedObject ) { // cfOwnerLink must be present and cfNative // must not be not present OR // cfNative must come after cfOwnerLink OR // cfNative comes before cfOwnerLink and // cfOwnerLink represents a StdOleLink if( fOwnerLink && !fNative ) { fRet = TRUE; } else if ( cfFirst == g_cfOwnerLink && fNative ) { fRet = TRUE; } else if( cfFirst == g_cfNative && fOwnerLink && IsOwnerLinkStdOleLink() ) { fRet = TRUE; } } else if( cf == g_cfEmbedSource ) { // cfNative and cfOwnerLink must be present // cfNative must precede cfOwnerLink and // OwnerLink must not represent a StdOleLink if( cfFirst == g_cfNative && fOwnerLink && !IsOwnerLinkStdOleLink()) { fRet = TRUE; } } else { Assert(cf == g_cfLinkSource); // cfObjectLink must be present OR // both cfNative and cfOwnerLink must be present // and cfOwnerLink must precede cfNative if( SSIsClipboardFormatAvailable(g_cfObjectLink) ) { fRet = TRUE; } else if( cfFirst == g_cfOwnerLink ) { fRet = TRUE; } } if( fOpenedClipboard ) { if( !SSCloseClipboard() ) { LEDebugOut((DEB_ERROR, "ERROR!: " "CloseClipboard failed in " "CanRetrieveOle2FromOle1!\n")); // just keep going and hope for the best. } } // // // END: CLOSECLIPBOARD // // } errRtn: LEDebugOut((DEB_ITRACE, "%p OUT CanRetrieveOle2From" "Ole1 ( %d )\n", NULL, fRet)); return fRet; } //+------------------------------------------------------------------------- // // Function: DibToPres // // Synopsis: stuffs a DIB into a presentation object // // Effects: takes ownership of hDib. // // Arguments: [hDib] -- the DIB // [ppres] -- the presentation object // // Requires: hDib *must* be a copy; this function will take ownership // of the hglobal. // // Returns: HRESULT // // Signals: // // Modifies: // // Algorithm: sets various fields in the presentation object // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo port from 16bit // // Notes: This code is largely based from 16bit OLE sources // REVIEW: we may want to rework portions of this code, // particularly if we rewrite the PPRES/GENOBJ code (in // ostm2stg.cpp). // // We take ownership of hDib because this function is // called by BmToPres, which allocates a DIB calls us. // //-------------------------------------------------------------------------- HRESULT DibToPres( HANDLE hDib, PPRES ppres) { BITMAPINFOHEADER * pbminfohdr; HRESULT hresult = NOERROR; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN DibToPres ( %lx , %p )\n", NULL, hDib, ppres)); Assert (ppres); pbminfohdr = (BITMAPINFOHEADER FAR*) GlobalLock (hDib); if( pbminfohdr == NULL ) { hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } // ftagClipFormat is defined in ostm2stg.h ppres->m_format.m_ftag = ftagClipFormat; ppres->m_format.m_cf = CF_DIB; ppres->m_ulHeight = pbminfohdr->biHeight; ppres->m_ulWidth = pbminfohdr->biWidth; // the destructor for m_data (in ostm2stg.cpp) will GlobalUnlock // m_pv and free m_h. Cute, ehh?? ppres->m_data.m_h = hDib; ppres->m_data.m_pv = pbminfohdr; ppres->m_data.m_cbSize = (ULONG) GlobalSize (hDib); // we must free hDib ppres->m_data.m_fNoFree = FALSE; // Do not unlock hDib (done by ~CData in ostm2stg.cpp) errRtn: LEDebugOut((DEB_ITRACE, "%p OUT DibToPres ( %lx )\n", NULL, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Function: IsOwnerLinkStdOleLink // // Synopsis: checks to see if the OwnerLink data on the clipboard // really represents a StdOleLink. // // Effects: // // Arguments: void // // Requires: The clipboard *must* be open. // cfOwnerLink must be on the clipboard. // // Returns: TRUE/FALSE // // Signals: // // Modifies: // // Algorithm: checks the class name in the OwnerLink data to see if it // matches 'OLE2Link'. // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo author // // Notes: OwnerLink data is laid out as follows // szClass\0szFile\0szItem\0\0 // // where sz* are ANSI strings. // //-------------------------------------------------------------------------- BOOL IsOwnerLinkStdOleLink( void ) { HGLOBAL hOwnerLink; LPSTR pszClass; BOOL fRet = FALSE; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN IsOwnerLinkStdOleLink ( )\n", NULL)); Assert(SSIsClipboardFormatAvailable(g_cfOwnerLink)); Assert(GetOpenClipboardWindow() == GetPrivateClipboardWindow(CLIP_QUERY)); hOwnerLink = SSGetClipboardData(g_cfOwnerLink); if( hOwnerLink ) { pszClass = (LPSTR)GlobalLock(hOwnerLink); if( pszClass ) { // NB!! These are intentionally ANSI strings. // OLE1 apps only understand ANSI if( _xmemcmp(pszClass, "OLE2Link", sizeof("OLE2Link")) == 0 ) { fRet = TRUE; } GlobalUnlock(hOwnerLink); } } LEDebugOut((DEB_ITRACE, "%p OUT IsOwnerLinkStdOleLink ( %lu )\n", NULL, fRet)); return fRet; } //+------------------------------------------------------------------------- // // Function: MfToPres // // Synopsis: copies the given metafile into the presentation object // // Effects: // // Arguments: [hMfPict] -- the metafilepict handle // [ppres] -- the presentation object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Algorithm: copies the metafile and fills out relevant fields in // the presentation object // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo port from 16bit // // Notes: This code is largely based from 16bit OLE sources // REVIEW: we may want to rework portions of this code, // particularly if we rewrite the PPRES/GENOBJ code (in // ostm2stg.cpp). // //-------------------------------------------------------------------------- HRESULT MfToPres( HANDLE hMfPict, PPRES ppres ) { HRESULT hresult; LPMETAFILEPICT pMfPict = NULL; HANDLE hglobal = NULL; DWORD cbSize; LPVOID pv = NULL; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN MfToPres ( %lx , %p )\n", NULL, hMfPict, ppres)); Assert (ppres); pMfPict = (LPMETAFILEPICT) GlobalLock (hMfPict); if( !pMfPict ) { hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } ppres->m_format.m_ftag = ftagClipFormat; ppres->m_format.m_cf = CF_METAFILEPICT; ppres->m_ulHeight = pMfPict->yExt; ppres->m_ulWidth = pMfPict->xExt; // in order for the presentation object stuff to work right, // we need to get the metafile bits in an HGLOBAL cbSize = GetMetaFileBitsEx(pMfPict->hMF, 0, NULL); if( cbSize == 0 ) { hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } hglobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbSize); if( hglobal == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } pv = GlobalLock(hglobal); if( pv == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } // now fetch the real bits if( GetMetaFileBitsEx(pMfPict->hMF, cbSize, pv) == 0 ) { hresult = ResultFromScode(CLIPBRD_E_BAD_DATA); goto errRtn; } ppres->m_data.m_h = hglobal; ppres->m_data.m_cbSize = cbSize; ppres->m_data.m_pv = pv; hresult = NOERROR; errRtn: if( pMfPict ) { GlobalUnlock(hMfPict); } if( hresult != NOERROR ) { if( pv ) { GlobalUnlock(hglobal); } if( hglobal ) { GlobalFree(hglobal); } } LEDebugOut((DEB_ITRACE, "%p OUT MftoPres ( %lx )\n", NULL, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Function: NativeToStorage // // Synopsis: takes the hglobal from cfNative and stuffs the data // onto the given storage. // // Effects: // // Arguments: [pstg] -- the storage // [hNative] -- the hglobal // // Requires: hNative must really have an IStorage in it // // Returns: HRESULT // // Signals: // // Modifies: // // Algorithm: layers a storage on top of the HGLOBAL, removes any // presentation streams, and then copies into the given // storage. // // History: dd-mmm-yy Author Comment // 11-Aug-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- HRESULT NativeToStorage( LPSTORAGE pstg, HANDLE hNative ) { LPLOCKBYTES plockbyte = NULL; LPSTORAGE pstgNative= NULL; HRESULT hresult; HGLOBAL hCopy = NULL; VDATEHEAP(); LEDebugOut((DEB_ITRACE, "%p _IN NativeToStorage ( %p , %lx )\n", NULL, pstg, hNative)); hCopy = UtDupGlobal(hNative, GMEM_DDESHARE | GMEM_MOVEABLE); if( hCopy == NULL ) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } hresult = CreateILockBytesOnHGlobal( hCopy, TRUE /*fDeleteOnRelease*/, &plockbyte); if( hresult != NOERROR ) { goto errRtn; } // This is really a 2.0 object disguised as a 1.0 object // for the sake of its 1.0 container, so reconstitute // the original IStorage from the native data. hresult = StgIsStorageILockBytes(plockbyte); if( hresult != NOERROR ) { LEDebugOut((DEB_ERROR, "ERROR!: Native data is not based on an" " IStorage!\n")); goto errRtn; } hresult = StgOpenStorageOnILockBytes(plockbyte, NULL, STGM_DFRALL, NULL, 0, &pstgNative); if( hresult != NOERROR ) { LEDebugOut((DEB_ERROR, "ERROR!: OpenStorage on Native data" "failed!!\n")); goto errRtn; } // now remove the any presentation streams from the native IStorage. // we do this because the OLE1 container will just hang on to the // hglobal although it may change (i.e. resize) the presenation. // Any cached presentation streams may therefore be invalid. // the caller of this function should reconstruct new presentation // streams from data available on the clipboard. hresult = UtDoStreamOperation(pstgNative,/* pstgSrc */ NULL, /* pstgDst */ OPCODE_REMOVE, /* operation to performed */ STREAMTYPE_CACHE); /* streams to be operated upon */ if( hresult != NOERROR ) { LEDebugOut((DEB_ERROR, "ERROR!: Cache stream removal " "failed for Native data-based IStorage!\n")); goto errRtn; } hresult = pstgNative->CopyTo(0, NULL, NULL, pstg); if( hresult != NOERROR ) { goto errRtn; } errRtn: if( pstgNative ) { pstgNative->Release(); } if( plockbyte ) { plockbyte->Release(); } LEDebugOut((DEB_ITRACE, "%p OUT NativeToStorage ( %lx )\n", NULL, hresult)); return hresult; } // // Enumerator implementation for enumerating a FromatEtcDataArray // //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::QueryInterface // // Synopsis: returns requested interfaces // // Effects: // // Arguments: [riid] -- the requested interface // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CEnumFormatEtcDataArray::QueryInterface( REFIID riid, LPVOID *ppvObj ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::QueryInterface " "( %p , %p )\n", this, riid, ppvObj)); if( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumFORMATETC) ) { *ppvObj = this; AddRef(); } else { *ppvObj = NULL; hresult = ResultFromScode(E_NOINTERFACE); } LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::QueryInterface " "( %lx ) [ %p ]\n", this, *ppvObj )); return hresult; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::AddRef // // Synopsis: increments the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG-- the new reference count // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEnumFormatEtcDataArray::AddRef( ) { VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::AddRef ( )\n", this)); ++m_refs; LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::AddRef ( %lu )\n", this, m_refs)); return m_refs; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::Release // // Synopsis: decrements the reference count on the object // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CEnumFormatEtcDataArray::Release( ) { ULONG cRefs; VDATEHEAP(); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::Release ( )\n", this)); if( (cRefs = --m_refs ) == 0 ) { LEDebugOut((DEB_TRACE, "%p DELETED CEnumFormatEtcDataArray\n", this)); delete this; } // using "this" below is OK, since we only want its value LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::Release ( %lu )\n", this, cRefs)); return cRefs; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::Next // // Synopsis: gets the next [celt] formats // // Effects: // // Arguments: [celt] -- the number of elements to fetch // [rgelt] -- where to put them // [pceltFetched] -- the number of formats actually fetched // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CEnumFormatEtcDataArray::Next( ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { if (celt == 0) { LEDebugOut((DEB_ERROR, "CDragEnum::Next requested entries returned is invalid\n")); return E_INVALIDARG; } if (!IsValidPtrOut(rgelt, sizeof(FORMATETC) * celt)) { LEDebugOut((DEB_ERROR, "CDragEnum::Next array to return entries invalid\n")); return E_INVALIDARG; } if (pceltFetched) { if (!IsValidPtrOut(pceltFetched, sizeof(*pceltFetched))) { LEDebugOut((DEB_ERROR, "CDragEnum::Next count to return invalid\n")); return E_INVALIDARG; } } else if (celt != 1) { LEDebugOut((DEB_ERROR, "CDragEnum::count requested != 1 & count fetched is NULL\n")); return E_INVALIDARG; } // handle the case where we have no data if( m_pFormatEtcDataArray == NULL ) { if( pceltFetched ) { *pceltFetched = 0; } return S_FALSE; } // Calculate the maximum number that we can return ULONG cToReturn = (m_cOffset < m_pFormatEtcDataArray->_cFormats) ? m_pFormatEtcDataArray->_cFormats - m_cOffset : 0; // Are we going to return any? if (cToReturn != 0) { // If the number requested is less that the maximum number // we can return, the we will return all requested/ if (celt < cToReturn) { cToReturn = celt; } // Allocate and copy the DVTARGETDEVICE - a side effect of this // loop is that our offset pointer gets updated to its value at // the completion of the routine. for (DWORD i = 0; i < cToReturn; i++, m_cOffset++) { memcpy(&rgelt[i], &(m_pFormatEtcDataArray->_FormatEtcData[m_cOffset]._FormatEtc), sizeof(FORMATETC)); if (m_pFormatEtcDataArray->_FormatEtcData[m_cOffset]._FormatEtc.ptd != NULL) { // Create a pointer to the device target - Remember when // we created the shared memory block we overroad the ptd // field of the FORMATETC so that it is now the offset // from the beginning of the shared memory. We reverse // that here so we can copy the data for the consumer. DVTARGETDEVICE *pdvtarget = (DVTARGETDEVICE *) ((BYTE *) m_pFormatEtcDataArray + (ULONG_PTR) m_pFormatEtcDataArray->_FormatEtcData[m_cOffset]._FormatEtc.ptd); // Allocate a new DVTARGETDEVICE DVTARGETDEVICE *pdvtargetNew = (DVTARGETDEVICE *) CoTaskMemAlloc(pdvtarget->tdSize); // Did the memory allocation succeed? if (pdvtargetNew == NULL) { // NO! - so clean up. First we free any device targets // that we might have allocated. for (DWORD j = 0; j < i; j++) { if (rgelt[j].ptd != NULL) { CoTaskMemFree(rgelt[j].ptd); } } // Then we restore the offset to its initial state m_cOffset -= i; return E_OUTOFMEMORY; } // Copy the old targetDevice to the new one memcpy(pdvtargetNew, pdvtarget, pdvtarget->tdSize); // Update output FORMATETC pointer rgelt[i].ptd = pdvtargetNew; } } } if (pceltFetched) { *pceltFetched = cToReturn; } return (cToReturn == celt) ? NOERROR : S_FALSE; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::Skip // // Synopsis: skips the next [celt] formats // // Effects: // // Arguments: [celt] -- the number of elements to skip // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CEnumFormatEtcDataArray::Skip( ULONG celt ) { HRESULT hresult = NOERROR; VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::Skip ( %lu )\n", this, celt)); m_cOffset += celt; if( m_cOffset > m_pFormatEtcDataArray->_cFormats ) { // whoops, skipped to far ahead. Set us to the max limit. m_cOffset = m_pFormatEtcDataArray->_cFormats ; hresult = ResultFromScode(S_FALSE); } LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::Skip ( %lx )\n", this, hresult )); return hresult; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::Reset // // Synopsis: resets the seek pointer to zero // // Effects: // // Arguments: void // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CEnumFormatEtcDataArray::Reset( void ) { VDATEHEAP(); VDATETHREAD(this); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::Reset ( )\n", this)); m_cOffset = 0; LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::Reset ( %lx )\n", this, NOERROR )); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::Clone // // Synopsis: clones the enumerator // // Effects: // // Arguments: [ppIEnum] -- where to put the cloned enumerator // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IEnumFORMATETC // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CEnumFormatEtcDataArray::Clone( IEnumFORMATETC **ppIEnum ) { HRESULT hresult; CEnumFormatEtcDataArray * pClipEnum; VDATEHEAP(); VDATETHREAD(this); VDATEPTROUT(ppIEnum, IEnumFORMATETC *); LEDebugOut((DEB_TRACE, "%p _IN CEnumFormatEtcDataArray::Clone ( %p )\n", this, ppIEnum)); *ppIEnum = new CEnumFormatEtcDataArray(m_pFormatEtcDataArray,m_cOffset); hresult = *ppIEnum ? NOERROR : E_OUTOFMEMORY; LEDebugOut((DEB_TRACE, "%p OUT CEnumFormatEtcDataArray::Clone ( %p )\n", this, *ppIEnum)); return hresult; } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::CEnumFormatEtcDataArray, private // // Synopsis: constructor // // Effects: // // Arguments: void // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- CEnumFormatEtcDataArray::CEnumFormatEtcDataArray(FORMATETCDATAARRAY *pFormatEtcDataArray,DWORD cOffset) { Assert(pFormatEtcDataArray); m_refs = 1; // give the intial reference m_pFormatEtcDataArray = pFormatEtcDataArray; m_cOffset = cOffset; ++(m_pFormatEtcDataArray->_cRefs); // hold onto Shared formats. } //+------------------------------------------------------------------------- // // Member: CEnumFormatEtcDataArray::~CEnumFormatEtcDataArray, private // // Synopsis: destructor // // Effects: // // Arguments: void // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 10-Apr-94 alexgo author // // Notes: // //-------------------------------------------------------------------------- CEnumFormatEtcDataArray::~CEnumFormatEtcDataArray( void ) { Assert(NULL != m_pFormatEtcDataArray); if( m_pFormatEtcDataArray ) { if (0 == --m_pFormatEtcDataArray->_cRefs) { PrivMemFree(m_pFormatEtcDataArray); m_pFormatEtcDataArray = NULL; } } }