5557 lines
138 KiB
C++
5557 lines
138 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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 <le2int.h>
|
|
#include <getif.hxx>
|
|
#include "clipdata.h"
|
|
#include "clipbrd.h"
|
|
#include <ostm2stg.h> //for wCLSIDFromProgID and GenericObjectToIStorage
|
|
|
|
#ifdef _DEBUG
|
|
#include <dbgdump.h>
|
|
#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);
|
|
|
|
#ifdef WX86OLE
|
|
BOOL fWx86Caller = gcwx86.IsQIFromX86();
|
|
#endif
|
|
|
|
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
|
|
|
|
#ifdef WX86OLE
|
|
if ( fWx86Caller && gcwx86.IsN2XProxy( m_pDataObject ) )
|
|
{
|
|
// If we establish that x86 code is calling x86 code then
|
|
// use OleStubInvoked to tell MapIFacePtr()/EstablishPSThunk()
|
|
// that they can just thunk the resulting IP as IUnknown if
|
|
// they don't know the type. This is safe because we're not
|
|
// going to use the native IP anywhere here.
|
|
|
|
gcwx86.SetStubInvokeFlag((UCHAR)-1);
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
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(strlen(pszClassA) + 1, pszClassA,
|
|
NULL, ppszClass);
|
|
|
|
if( hresult != NOERROR )
|
|
{
|
|
goto errRtn;
|
|
}
|
|
}
|
|
|
|
pszFileA = pszClassA + strlen(pszClassA) + 1;
|
|
|
|
if( ppszFile )
|
|
{
|
|
hresult = UtGetUNICODEData(strlen(pszFileA) + 1, pszFileA,
|
|
NULL, ppszFile );
|
|
|
|
if( hresult != NOERROR )
|
|
{
|
|
goto errRtn;
|
|
}
|
|
}
|
|
|
|
pszItemA = pszFileA + strlen(pszFileA) +1;
|
|
|
|
if( ppszItem )
|
|
{
|
|
hresult = UtGetUNICODEData(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( RegQueryValue(HKEY_CLASSES_ROOT, 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;
|
|
}
|
|
}
|
|
}
|