Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3722 lines
107 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: clipapi.cpp
//
// Contents: Clipboard related OLE API's
//
// Classes:
//
// Functions:
// OleFlushClipboard
// OleGetClipboard
// OleIsCurrentClipboard
// OleSetClipboard
//
// History: dd-mmm-yy Author Comment
// 12-Aug-94 alexgo added support for transfering
// DVASPECT_ICON, etc.
// 08-Aug-94 BruceMa Memory sift fix
// 10-Jun-94 alexgo added support for OLE1 Containers
// 17-May-94 alexgo created OleOpenClipboard and enhanced
// code to mimize the times when the
// clipboard is kept open.
// 25-Apr-94 alexgo made thread-safe for the apartment model
// 16-Mar-94 alexgo author
//
//--------------------------------------------------------------------------
#include <le2int.h>
#include <getif.hxx>
#include <clipbrd.h>
#include <olesem.hxx>
#include <ostm2stg.h> // for wProgIDFromCLSID
#include "clipdata.h"
//
// types local to this file
//
typedef enum tagCLIPWNDFLAGS
{
CLIPWND_REMOVEFROMCLIPBOARD = 1,
CLIPWND_IGNORECLIPBOARD = 2,
CLIPWND_DONTCALLAPP = 4
} CLIPWNDFLAGS;
typedef enum tagGETCLSIDFLAGS
{
USE_NORMAL_CLSID = 1,
USE_STANDARD_LINK = 2,
} GETCLSIDFLAGS;
//
// functions local to this file. They are not "static" so the symbols
// show up in ntsd debug builds.
//
extern "C" LRESULT ClipboardWndProc( HWND, UINT, WPARAM, LPARAM );
HRESULT GetDataFromDescriptor(IDataObject *pDataObj, LPCLSID pclsid,
UINT cf, GETCLSIDFLAGS fFlags,
LPOLESTR *ppszSrcOfCopy,
DWORD *pdwStatus);
HRESULT GetDataFromStorage(IDataObject *pDataObj, UINT cf,
STGMEDIUM *pmedium, IStorage **ppstg);
HRESULT GetDataFromStream(IDataObject *pDataObj, UINT cf,
STGMEDIUM *pmedium, IStream **ppstm);
HRESULT GetNative(IDataObject *pDataObj, STGMEDIUM *pmedium);
HRESULT GetObjectLink(IDataObject *pDataObj, STGMEDIUM *pmedium);
HRESULT GetOwnerLink(IDataObject *pDataObj, STGMEDIUM *pmedium);
HRESULT HandleFromHandle(IDataObject *pDataObj, FORMATETC *pformatetc,
STGMEDIUM *pmedium);
HRESULT MapCFToFormatetc( UINT cf, FORMATETC *pformatetc );
HRESULT RemoveClipboardDataObject( HWND hClipWnd, DWORD fFlags );
HRESULT RenderFormat( HWND hClipWnd, UINT cf, IDataObject *pDataObj );
HRESULT SetClipboardDataObject( HWND hClipWnd, IDataObject *pDataObj );
HRESULT SetClipboardFormats( HWND hClipWnd, IDataObject *pDataObj );
HWND VerifyCallerIsClipboardOwner( void );
//
//static variables
//
// vcClipboardInit is used to keep track of the number of times Clipboard
// Initialize is called (right now, only from OleInitialize), so that we
// only create a private clipboard window class once per dll (even though
// the many threads may need their own instance of the window class in the
// apartment model).
static ULONG vcClipboardInit;
// vszClipboardWndClass is the name of the window class used by OLE to
// create private clipboard windows for copy/paste and other clipboard
// data transfers.
#ifdef _CHICAGO_
// Note: we have to create a unique string so that get
// register a unique class for each 16 bit app.
// The class space is global on chicago.
//
char g_vszClipboardWndClass[] = "CLIPBRDWNDCLASS 0x######## ";
#define vszClipboardWndClass g_vszClipboardWndClass
#define ClpWNDCLASS WNDCLASSA
#define ClpRegisterClass RegisterClassA
#define ClpUnregisterClass UnregisterClassA
#define ClpCreateWindowEx SSCreateWindowExA
#else
static const OLECHAR vszClipboardWndClass[] = OLESTR("CLIPBRDWNDCLASS");
#define ClpWNDCLASS WNDCLASS
#define ClpRegisterClass RegisterClass
#define ClpUnregisterClass UnregisterClass
#define ClpCreateWindowEx CreateWindowEx
#endif
// Mutex used to synchronize clipboard initialization / cleanup
extern COleStaticMutexSem g_mxsSingleThreadOle;
// Property we put on the window that is used to keep a duplicate of
// OlePrivateData (so we don't have to go the clipboard to fetch it)
WCHAR *pwszClipPrivateData = L"ClipPrivData";
//
// functions (in alphabetical order)
//
//+-------------------------------------------------------------------------
//
// Function: ClipboardInitialize (private)
//
// Synopsis: Creates the private clipboard window class (if necessary)
//
// Effects:
//
// Arguments: void
//
// Requires: hmodOLE2 must be initialized
//
// Returns: TRUE upon success, false otherwise
//
// Signals:
//
// Modifies:
//
// Algorithm: Register the clipboard class only once per dll instance
// (or more specifically, every time vcClipboardInit == 0,
// which may happen multiple times in a WOW box).
//
// History: dd-mmm-yy Author Comment
// 23-Oct-94 alexgo fixed up Chicago WOW hacks (see comments)
// 25-Apr-94 alexgo updated to the apartment model
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL ClipboardInitialize( void )
{
ClpWNDCLASS wc;
BOOL fRet = TRUE;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN ClipboardInitialize ( )\n", NULL));
// serialize access to this function
// we'll unlock the mutex automatically in "lck"'s destructor
// (called at function exit)
COleStaticLock lck(g_mxsSingleThreadOle);
// One time initializtaion (when loaded for the first time)
if (vcClipboardInit == 0)
{
#ifdef _CHICAGO_
if (IsWOWThread())
{
wsprintfA(vszClipboardWndClass,"CLIPBRDWNDCLASS %08X",
CoGetCurrentProcess());
}
#endif // _CHICAGO_
// Register Clipboard window class
wc.style = 0;
wc.lpfnWndProc = ClipboardWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(void *);
AssertSz(g_hmodOLE2, "Dll instance variable not set");
wc.hInstance = g_hmodOLE2; //global vairable set in
//ole2.cpp
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = vszClipboardWndClass;
// register this window class, returning if we fail
if (!ClpRegisterClass(&wc))
{
LEWARN(FALSE, "ClipboardInitialize RegisterClass failed!");
// it is possible that our dll got unloaded without us
// having called unregister, so we call it here and try
// again.
ClpUnregisterClass( vszClipboardWndClass, g_hmodOLE2 );
if (!ClpRegisterClass(&wc))
{
LEWARN(FALSE, "ClipboardInitialize RegisterClass failed again!");
LEDebugOut((DEB_WARN,
"WARNING: RegisterClass failed\n"));
fRet = FALSE;
goto errRtn;
}
}
vcClipboardInit++;
}
errRtn:
LEDebugOut((DEB_ITRACE, "%p OUT ClipboardIntialize ( %lu )\n",
NULL, fRet));
return fRet;
}
//+-------------------------------------------------------------------------
//
// Function: ClipboardUnitialize (internal)
//
// Synopsis: Uninitializes the clipboard for the current thread.
//
// Effects:
//
// Arguments: void
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 23-Oct-94 alexgo fixed up Chicago WOW hacks (see comments)
// 25-Apr-94 alexgo made thread-safe
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
void ClipboardUninitialize(void)
{
VDATEHEAP();
//
// BUGBUG - This cleanup is done during OleUninitialize, but not
// if somebody does FreeLibrary(OLE32.DLL). This is bad. It causes
// us to leak the class register and any clipboard window. We have
// gotten around the class re-register problem above in the
// ClipboardInitialize function, but we still leak a window.
//
LEDebugOut((DEB_ITRACE, "%p _IN ClipboardUninitialize ( )\n", NULL));
COleTls tls;
if(tls->hwndClip)
{
// destroy the window and NULL out the hwnd in the thread
// storage
Verify(SSDestroyWindow(tls->hwndClip));
tls->hwndClip = NULL;
}
}
//+-------------------------------------------------------------------------
//
// Function: ClipboardProcessUnitialize (internal)
//
// Synopsis: Uninitializes the clipboard. If this the last such time,
// then the private clipboard window class is unregistered.
//
// Effects:
//
// Arguments: void
//
// Requires: hmodOLE2 must be initialized before calling this function
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 23-Oct-94 alexgo fixed up Chicago WOW hacks (see comments)
// 25-Apr-94 alexgo made thread-safe
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
void ClipboardProcessUninitialize(void)
{
// serialize access for the apartment model
COleStaticLock lck(g_mxsSingleThreadOle);
if(vcClipboardInit == 1)
{
vcClipboardInit--;
BOOL fRet = ClpUnregisterClass(vszClipboardWndClass,
g_hmodOLE2);
LEWARN(!fRet, "UnRegisterClass failed!");
}
LEDebugOut((DEB_ITRACE, "%p OUT ClipboardUninitialize ( )\n", NULL));
}
//+-------------------------------------------------------------------------
//
// Function: ClipboardWndProc
//
// Synopsis: Window message procedure for the private clipboard window
//
// Effects:
//
// Arguments: [hWnd] -- handle to private clipboard window
// [msg] -- the Window message
// [wParam] -- parameter 1
// [lParam] -- parameter 2
//
// Requires:
//
// Returns: LRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: processes messages sent to the private clipboard window:
// WM_DESTROYCLIPBOARD: somebody else is taking ownership
// of the clipboard, so release the data object
// (if any)
// WM_RENDERFORMAT: a request has been made for data of the
// specified format--actually put it on the clipboard
// WM_RENDERALLFORMATS: the app is going away, so empty the
// clipboard! The app is supposed to call
// OleFlushClipboard before exiting, if it hasn't, then
// we can only assume that the app is terminating
// "abnormally". We currently do nothing for this call
//
// History: dd-mmm-yy Author Comment
// 23-Oct-94 alexgo added support for eating WM_CANCELMODE
// messages
// 20-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
extern "C" LRESULT ClipboardWndProc( HWND hWnd, UINT msg, WPARAM wParam,
LPARAM lParam )
{
LRESULT lresult = 0;
IDataObject * pDataObj = NULL;
UINT cf;
HRESULT hresult;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN ClipboardWndProc ( %lx , %u , %lu ,"
" %ld )\n", NULL, hWnd, msg, wParam, lParam));
AssertSz((GetCurrentThreadId()
== GetWindowThreadProcessId(hWnd, NULL)),
"Clip window not on current thread");
switch( msg )
{
// note that the clipboard should *NOT* be opened for these messages
case WM_RENDERALLFORMATS:
// this message is sent to us if this window (the private
// clipboard window) is about to be destroyed.
// We don't currently do anything for this message.
// REVIEW: in the future, we may want to render all the
// remaining formats. However, the app is *supposed* to
// call OleFlushClipboard to accomplish this task.
Assert(lresult == 0);
break;
case WM_DESTROYCLIPBOARD:
// we get this message when somebody else takes ownership
// of the clipboard. Since our app may have an AddRef'ed
// data object already there, we need to remove it.
// there is no need to open the clipboard (since we specify
// the IGNORECLIPBOARD flag)
RemoveClipboardDataObject(hWnd, CLIPWND_IGNORECLIPBOARD);
Assert(lresult == 0);
break;
case WM_RENDERFORMAT:
cf = (UINT)wParam;
pDataObj = (IDataObject *)GetProp( hWnd,
CLIPBOARD_DATA_OBJECT_PROP);
if( !pDataObj )
{
LEDebugOut((DEB_ERROR, "ERROR!: No data object "
"on the private window\n"));
break;
}
// now render the data onto the clipboard
hresult = RenderFormat( hWnd, cf, pDataObj);
#if DBG == 1
if( hresult != NOERROR )
{
char szBuf[256];
char *pszBuf;
// we have to do predefined formats by hand
if( cf > 0xC000 )
{
SSGetClipboardFormatNameA(cf, szBuf, 256);
pszBuf = szBuf;
}
else
{
switch( cf )
{
case CF_METAFILEPICT:
pszBuf = "CF_METAFILEPICT";
break;
case CF_BITMAP:
pszBuf = "CF_BITMAP";
break;
case CF_DIB:
pszBuf = "CF_DIB";
break;
case CF_PALETTE:
pszBuf = "CF_PALETTE";
break;
case CF_TEXT:
pszBuf = "CF_TEXT";
break;
case CF_UNICODETEXT:
pszBuf = "CF_UNICODETEXT";
break;
case CF_ENHMETAFILE:
pszBuf = "CF_ENHMETAFILE";
break;
default:
pszBuf = "UNKNOWN Default Format";
break;
}
}
LEDebugOut((DEB_WARN, "WARNING: Unable to render "
"format '%s' (%x)\n", pszBuf, cf));
}
#endif // DBG == 1
Assert(lresult == 0);
break;
case WM_CANCELMODE:
// we want to swallow the WM_CANCELMODE message. This
// allows us to start drag drop, alt-tab to another app
// (which causes a WM_CANCELMODE message) and continue
// dragging.
Assert(lresult == 0);
break;
case WM_DESTROY:
// apps are supposed to call OleSetClipboard(NULL) or
// OleFlushClipboard() before terminating a thread. However,
// not all apps do what they're supposed to, so just
// remove as much state as we can safely do
// Potentially, we could use CLIPWND_REMOVEFROMCLIPBOARD
// here. However, getting in this situation should be
// somewhat unusual, so we don't want to do any more work
// than absolutely necessary. Even though we'll leave a
// hwnd on the clipboard in g_cfDataObject, that hwnd
// will soon be invalid (because it's the one getting this
// WM_DESTROY message).
#if DBG == 1
// do some debug checking first though
if( GetProp( hWnd, pwszClipPrivateData) != 0 )
{
LEDebugOut((DEB_WARN, "WARNING: App did not cleanup the "
"clipboard properly, OleSetClipboard(NULL) or "
"OleFlushClipboard not called"));
}
#endif // DBG == 1
RemoveClipboardDataObject(hWnd, (CLIPWND_DONTCALLAPP |
CLIPWND_IGNORECLIPBOARD));
Assert(lresult == 0);
break;
default:
lresult = SSDefWindowProc( hWnd, msg, wParam, lParam);
break;
}
LEDebugOut((DEB_ITRACE, "%p OUT ClipboardWndProc ( %ld )\n", NULL,
lresult));
return lresult;
}
//+-------------------------------------------------------------------------
//
// Function: ClipSetCaptureForDrag
//
// Synopsis: Sets mouse capture mode for a drag operation
//
// Arguments: [pdrgop] - pointer to object that handles drag operation
//
// Returns: S_OK -- it worked
// E_FAIL -- unexpected failure occurred.
//
// Algorithm: Get the clipboard window for the thread. Record the drag
// drag operation pointer on the window for use by capture
// mode. then turn on capture mode.
//
// History: dd-mmm-yy Author Comment
// 21-Apr-94 ricksa created
//
// Notes: The purpose of this function is to hide where the drag
// pointer is stored for the window.
//
//--------------------------------------------------------------------------
HRESULT ClipSetCaptureForDrag(CDragOperation *pdrgop)
{
// Default to failure
HRESULT hr = ResultFromScode(E_FAIL);
// We will use the clipboard window to capture the mouse but we
// must have a clipboard window so we make sure it is created
// if it is not already there.
HWND hWndClip = GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
if (hWndClip != NULL)
{
AssertSz((GetCurrentThreadId()
== GetWindowThreadProcessId(hWndClip, NULL)),
"Clip window not on current thread");
// Capture the mouse
SetCapture(hWndClip);
// Teller the caller that we worked.
hr = NOERROR;
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: ClipReleaseCaptureForDrag
//
// Synopsis: Clean up drag mouse capture
//
// Algorithm: Get the clipboard window for the thread. Turn the drag
// operation pointer into null. Then release the capture.
//
// History: dd-mmm-yy Author Comment
// 21-Apr-94 ricksa created
//
// Notes: It is assumed that the clip board window and the thread
// doing drag and drop are on the same thread. Therefore,
// there should be no race between clean up here and
// the use of the pointer in the clipboard window proc.
//
//--------------------------------------------------------------------------
void ClipReleaseCaptureForDrag(void)
{
#if 0
// this code turned off by ericoe on 4/19/95
// left here for reference "just in case"
// the whole block turned off because we no longer need to do the RemoveProp
// Tell the window that the drag operation is over
HWND hWndClip = GetPrivateClipboardWindow(CLIP_QUERY);
AssertSz((hWndClip != NULL), "Release capture but no window");
if (hWndClip)
{
AssertSz((GetCurrentThreadId()
== GetWindowThreadProcessId(hWndClip, NULL)),
"Clip window not on current thread");
// We are done so remove the property
Verify(RemoveProp(hWndClip, pwszClipDragProp));
}
#endif
// Stop the mouse capture
ReleaseCapture();
}
//+-------------------------------------------------------------------------
//
// Function: GetDataFromDescriptor
//
// Synopsis: Retrieves object descriptor data from the specified
// clipboard format and fetches the clsid, SrcOfCopy
// string, and status flags
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pclsid] -- where to put the clsid
// [cf] -- the clipboard format to retrieve
// [fFlags] -- clsid conversion flags
// [ppszSrcOfCopy] -- where to put an ALLOCATED (public
// allocator) copy of the SrcOfCopy
// string.
// [pdwStatus] -- where to put the status bits
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: see synopsis
//
// History: dd-mmm-yy Author Comment
// 18-Aug-94 alexgo added support for fetching dwStatus
// 10-Jun-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT GetDataFromDescriptor(IDataObject *pDataObj, LPCLSID pclsid,
UINT cf, GETCLSIDFLAGS fFlags,
LPOLESTR *ppszSrcOfCopy,
DWORD *pdwStatus)
{
HRESULT hresult;
FORMATETC formatetc;
STGMEDIUM medium;
LPOBJECTDESCRIPTOR pObjDesc;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetDataFromDescriptor ( %p , "
"%p , %d , %lx , %p, %p )\n", NULL, pDataObj, pclsid, cf,
fFlags, ppszSrcOfCopy, pdwStatus));
// we don't bother with extensive attempts to fetch the
// OLE2 data since we're only using it to construct OLE1. If
// the data is offered in a non-standard way, the the worse
// that will happen is that you can't paste an *object* to
// an OLE1 container. 16bit was even more strict in that
// you *always* had to offer OLE2 formats on standard mediums.
INIT_FORETC(formatetc);
formatetc.cfFormat = cf;
formatetc.tymed = TYMED_HGLOBAL;
medium.tymed = TYMED_HGLOBAL;
hresult = pDataObj->GetData(&formatetc, &medium);
if( hresult != NOERROR )
{
goto logRtn;
}
pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(medium.hGlobal);
if( !pObjDesc )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
if( pclsid )
{
// if we want to use the standard link AND the object really
// is a link object (potentially a custom link), then
// just set the clsid to the be the standard link object
if( (fFlags & USE_STANDARD_LINK) &&
(pObjDesc->dwStatus & OLEMISC_ISLINKOBJECT) )
{
*pclsid = CLSID_StdOleLink;
}
else
{
*pclsid = pObjDesc->clsid;
}
}
if( ppszSrcOfCopy )
{
if( pObjDesc->dwSrcOfCopy )
{
*ppszSrcOfCopy = UtDupString(
(LPOLESTR)(((BYTE *)pObjDesc)+pObjDesc->dwSrcOfCopy));
}
else
{
*ppszSrcOfCopy = UtDupString(OLESTR(""));
}
if( !*ppszSrcOfCopy )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
}
}
if( pdwStatus )
{
*pdwStatus = pObjDesc->dwStatus;
}
GlobalUnlock(medium.hGlobal);
errRtn:
ReleaseStgMedium(&medium);
logRtn:
LEDebugOut((DEB_ITRACE, "%p OUT GetDataFromDescriptor ( %lx ) "
"[ %p ]\n", NULL, hresult,
(ppszSrcOfCopy) ? *ppszSrcOfCopy : 0 ));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetDataFromStorage
//
// Synopsis: Calls GetData[Here] for TYMED_ISTORAGE and returns the
// results on either an HGLOBAL or memory-based storage
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pformatetc] -- formatetc to retrieve
// [pmedium] -- where to put the resulting HGlobal, may
// be NULL
// [ppstg] -- where to save the real IStorage
// (may be NULL)
//
// Requires: if pmedium is specified, then pmedium->tymed must be
// TYMED_HGLOBAL
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: we create a storage on memory
// first try to GetDataHere to that storage, if that fails, then
// do a GetData and CopyTo the returned storage to our memory
// storage.
//
// History: dd-mmm-yy Author Comment
// 11-Apr-94 alexgo author
//
// Notes: NB!!: The caller takes ownership of the data--if an hglobal
// is requested, then it must be explicitly GlobalFree'd.
// Similarly, the returned storage must be released and both
// release mechanisms must be called if both data items are
// returned.
//
//--------------------------------------------------------------------------
HRESULT GetDataFromStorage(IDataObject *pDataObj, FORMATETC *pformatetc,
STGMEDIUM *pmedium, IStorage **ppstg)
{
HRESULT hresult;
STGMEDIUM memmedium; // for the memory-based IStorage
ILockBytes * pLockBytes;
BOOL fDeleteOnRelease = FALSE;
FORMATETC fetctemp;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetDataFromStorage ( %p , %p , %p"
" )\n", NULL, pDataObj, pformatetc, pmedium));
#if DBG ==1
if( pmedium )
{
Assert(pmedium->tymed == TYMED_HGLOBAL);
}
#endif // DBG ==1
Assert(pformatetc->tymed & TYMED_ISTORAGE);
// don't stomp on the in-parameter
fetctemp = *pformatetc;
fetctemp.tymed = TYMED_ISTORAGE;
_xmemset(&memmedium, 0, sizeof(STGMEDIUM));
memmedium.tymed = TYMED_ISTORAGE;
// the only time we want the hglobal that the storage will be
// constructed from to be automatically deleted is if the caller
// only requested a storage to be returned.
if( ppstg && !pmedium )
{
fDeleteOnRelease = TRUE;
}
hresult = UtCreateStorageOnHGlobal( NULL,
fDeleteOnRelease,
&(memmedium.pstg), &pLockBytes);
if( hresult != NOERROR )
{
goto errRtn;
}
// first try to do a GetDataHere call
hresult = pDataObj->GetDataHere( &fetctemp, &memmedium );
if( hresult != NOERROR )
{
STGMEDIUM appmedium; // a medium that is filled
// in by the app
_xmemset(&appmedium, 0, sizeof(STGMEDIUM));
// hmmm, that didn't work, try for a plain GetData call
hresult = pDataObj->GetData(&fetctemp, &appmedium);
if( hresult == NOERROR )
{
// now do the CopyTo
hresult = appmedium.pstg->CopyTo(0, NULL, NULL,
memmedium.pstg);
// we are now done with the app supplied medium
ReleaseStgMedium(&appmedium);
}
}
// release the storage unless there's no error and the
// caller requested a copy
if( ppstg && hresult == NOERROR )
{
*ppstg = memmedium.pstg;
// we need to do a Commit here to flush cached data to
// disk (in this case, to the hglobal). The release
// below in the alternate code path will automatically
// cause a Commit
memmedium.pstg->Commit(STGC_DEFAULT);
}
else
{
memmedium.pstg->Release();
}
// now retrieve the HGLOBAL from the storage. NB! It is very
// important to do this *after* the release; the final release
// on the storage causes a Commit. (Alternately, we can simply
// call Commit--see above).
if( hresult == NOERROR && pmedium )
{
hresult = GetHGlobalFromILockBytes(pLockBytes,
&(pmedium->hGlobal));
}
pLockBytes->Release();
errRtn:
LEDebugOut((DEB_ITRACE, "%p OUT GetDataFromStorage ( %lx )\n",
NULL, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetDataFromStream
//
// Synopsis: Calls GetData[Here] for TYMED_ISTREAM and returns the
// results on an HGLOBAL
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pformatetc] -- the formatetc to retrieve
// [pmedium] -- where to put the resulting HGlobal.
// (may be NULL)
// [ppstm] -- where to put the stream ( may be NULL )
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: we create a stream on memory
// first try to GetDataHere to that stream, if that fails, then
// do a GetData and CopyTo the returned stream to our memory
// stream.
//
// History: dd-mmm-yy Author Comment
// 11-Apr-94 alexgo author
//
// Notes: NB!!: The caller takes ownership fo the data returned, either
// GlobalFree or Release (or both) must be called.
//
//--------------------------------------------------------------------------
HRESULT GetDataFromStream(IDataObject *pDataObj, FORMATETC *pformatetc,
STGMEDIUM *pmedium, IStream **ppstm)
{
HRESULT hresult;
STGMEDIUM memmedium; // for the memory-based IStream
HGLOBAL hglobal = NULL;
BOOL fDeleteOnRelease = FALSE;
FORMATETC fetctemp;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetDataFromStream ( %p , %p , %p )\n",
NULL, pDataObj, pformatetc, pmedium));
// the only time we want the underlying hglobal for the stream to
// be automatically deleted is if the caller only wanted the
// stream returned.
if( ppstm && !pmedium )
{
fDeleteOnRelease = TRUE;
}
Assert( pformatetc->tymed & TYMED_ISTREAM );
// don't stomp on the in-parameter
fetctemp = *pformatetc;
fetctemp.tymed = TYMED_ISTREAM;
_xmemset(&memmedium, 0, sizeof(STGMEDIUM));
memmedium.tymed = TYMED_ISTREAM;
hresult = CreateStreamOnHGlobal( NULL,
fDeleteOnRelease,
&(memmedium.pstm));
if( hresult != NOERROR )
{
goto logRtn;
}
// first try to do a GetDataHere call
hresult = pDataObj->GetDataHere( &fetctemp, &memmedium );
if( hresult != NOERROR )
{
if (hresult == E_OUTOFMEMORY)
{
goto errRtn;
}
STGMEDIUM appmedium; // a medium that is filled
// in by the app
LARGE_INTEGER li;
ULARGE_INTEGER uli;
ULARGE_INTEGER uliWritten;
#if DBG == 1
ULARGE_INTEGER uliEnd;
#endif
_xmemset(&appmedium, 0, sizeof(STGMEDIUM));
// hmmm, that didn't work, try for a plain GetData call
hresult = pDataObj->GetData( &fetctemp, &appmedium );
if( hresult != NOERROR )
{
// oh well, we tried. Cleanup and away we go
goto errRtn;
}
// now do the CopyTo. In order to do this, we need
// to get the size of the returned stream, reset its
// seek pointer to the beginning and then do a stream
// CopyTo.
LISet32(li, 0);
hresult = appmedium.pstm->Seek(li, STREAM_SEEK_CUR, &uli);
if( hresult != NOERROR )
{
ReleaseStgMedium(&appmedium);
goto errRtn;
}
#if DBG == 1
// According to the spec, the end of the data should be
// positioned at the current seek pointer (which is
// not necessarily the end of the stream). Here we will
// see if the current seek pointer is at the *end* of the
// stream. If the current seek is NOT equal to the end,
// then there is a good chance of a bug somewhere in the
// system (so we'll print a warning)
hresult = appmedium.pstm->Seek(li, STREAM_SEEK_END, &uliEnd);
// we don't return on error for debug builds so retail
// and debug have exactly the same behaviour
if( hresult == NOERROR )
{
// compare the two seek pointers. The high parts
// *must* be zero (or we're hosed, since all of
// this is taking place in memory
Assert(uliEnd.HighPart == 0);
LEWARN(uliEnd.LowPart != uli.LowPart,
"Stream seek pointer "
"not at end, possible error");
}
else
{
LEDebugOut((DEB_ERROR, "ERROR!: IStream->Seek failed!"
"\n"));
// FALL-THROUGH!! This is deliberate--even
// though we're in an error case, we want
// debug && retail to have the same behaviour
// (besides, we'll most likely fail in the
// Seek call below).
}
#endif // DBG == 1
// now backup to the beginning
hresult = appmedium.pstm->Seek(li, STREAM_SEEK_SET, NULL);
if( hresult != NOERROR )
{
ReleaseStgMedium(&appmedium);
goto errRtn;
}
// now that we know how many bytes to copy, actually do so.
hresult = appmedium.pstm->CopyTo(memmedium.pstm, uli,
NULL, &uliWritten);
if( hresult == NOERROR )
{
// make sure we got enough data
if( uli.LowPart != uliWritten.LowPart )
{
// we probably ran out of memory
// trying to resize the memory stream
hresult = ResultFromScode(E_OUTOFMEMORY);
}
}
// we are now done with the app supplied medium
ReleaseStgMedium(&appmedium);
}
// now fetch the hglobal from the [resized] memory stream
if( hresult == NOERROR )
{
hresult = GetHGlobalFromStream(memmedium.pstm, &hglobal);
}
errRtn:
// if the caller wanted the stream, then give it to him
// (only if there was no error)
// otherwise, release it
if( hresult == NOERROR && ppstm )
{
*ppstm = memmedium.pstm;
// we do not need to call Commit in this case; our
// implementation of memory streams guarantees that
// the underlying hglobal always contains flushed
// information.
}
else
{
memmedium.pstm->Release();
}
// if there was an error, then would have never allocated the
// hglobal
if( hresult == NOERROR && pmedium)
{
pmedium->hGlobal = hglobal;
}
logRtn:
LEDebugOut((DEB_ITRACE, "%p OUT GetDataFromStream ( %lx )\n",
NULL, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetNative
//
// Synopsis: Retrieves or syntesizes OLE1 Native data format
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pmedium] -- where to put the data
//
// Requires: pmedium->tymed must be TYMED_HGLOBAL
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm:
// cfNative is an OLE1 format consisting of an aribtrary
// hGlobal. It is up to the source app to interpret any
// data therein; OLE1 containers merely store and forward it.
//
// first fetch either EmbedSource or EmbeddedObject
// then check to see if that NATIVE_STREAM exists. If so,
// then this was an object created from an OLE1 server and
// we should just offer it's native data.
// Otherwise, the object is an OLE2 object, and we should
// offer it's storage as the native data.
//
// History: dd-mmm-yy Author Comment
// 10-Jun-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT GetNative( IDataObject *pDataObj, STGMEDIUM *pmedium)
{
HRESULT hresult;
IStorage * pstg = NULL;
IStream * pstm = NULL;
UINT cf;
HGLOBAL hNative = NULL;
DWORD dwSize = 0;
LPVOID pv;
FORMATETC formatetc;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetNative ( %p , %p )\n", NULL,
pDataObj, pmedium));
Assert(pmedium->tymed == TYMED_HGLOBAL);
if( SSIsClipboardFormatAvailable(g_cfEmbeddedObject) )
{
cf = g_cfEmbeddedObject;
}
else if( SSIsClipboardFormatAvailable(g_cfEmbedSource) )
{
cf = g_cfEmbedSource;
}
else
{
hresult = ResultFromScode(E_UNEXPECTED);
LEDebugOut((DEB_ERROR, "ERROR!: Native data should not "
"be on clipboard!!\n"));
goto errRtn;
}
INIT_FORETC(formatetc);
formatetc.cfFormat = cf;
formatetc.tymed = TYMED_ISTORAGE;
hresult = GetDataFromStorage(pDataObj, &formatetc, pmedium, &pstg);
if( hresult != NOERROR )
{
goto errRtn;
}
hresult = pstg->OpenStream(OLE10_NATIVE_STREAM, NULL, STGM_SALL, 0,
&pstm);
if( hresult == NOERROR )
{
// we had ole1 data originally, just use it.
hresult = StRead(pstm, &dwSize, sizeof(DWORD));
if( hresult != NOERROR )
{
goto errRtn;
}
hNative = GlobalAlloc((GMEM_SHARE | GMEM_MOVEABLE), dwSize);
if( !hNative )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalAlloc failed!"
"\n"));
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
pv = GlobalLock(hNative);
if( !pv )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalLock failed!"
"\n"));
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
// now copy the data from the stream into the hglobal
hresult = StRead(pstm, pv, dwSize);
GlobalUnlock(hNative);
if( hresult != NOERROR )
{
goto errRtn;
}
// this is bit is counter-intuitive. The hglobal
// we have in pmedium->hGlobal still has a storage on
// top of it, so we must release our stream, then
// the storage, and finally free the hglobal so we
// don't leak memory. We've already allocated another
// hglobal in this routine to return the Native data.
pstm->Release();
pstg->Release();
GlobalFree(pmedium->hGlobal);
// now we assign pmedium->hGlobal to the hglobal we
// just created so we can pass it out
pmedium->hGlobal = hNative;
// don't release the streams again
goto logRtn;
}
else
{
// storage for an OLE2 object. pmedium->hGlobal
// should already contain the data we need to put
// on the clipboard (from the GetDataFromStorage call)
Assert(pmedium->hGlobal);
hresult = NOERROR;
}
errRtn:
if( pstm )
{
pstm->Release();
}
if( pstg )
{
pstg->Release();
}
if( hresult != NOERROR )
{
GlobalFree(pmedium->hGlobal);
}
logRtn:
LEDebugOut((DEB_ITRACE, "%p OUT GetNative ( %lx ) [ %lx ]\n",
NULL, hresult, pmedium->hGlobal));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetObjectLink
//
// Synopsis: Synthesizes OLE1 ObjectLink format from LinkSource data
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pmedium] -- where to put the data
//
// Requires: pmedium->tymed must be TYMED_HGLOBAL
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: Get the LinkSource data, which contains a serialized
// moniker. Load the moniker from the stream and parse it
// to retrieve the file name and item name (if available).
// Get the class ID of the link source from either the
// LinkSource stream or from LinkSrcDescriptor.
// Once these strings are converted to ANSI, we can build
// the ObjectLink format, which looks like:
//
// classname\0filename\0itemname\0\0
//
// History: dd-mmm-yy Author Comment
// 10-Jun-94 alexgo author
// Notes:
//
//--------------------------------------------------------------------------
HRESULT GetObjectLink( IDataObject *pDataObj, STGMEDIUM *pmedium)
{
HRESULT hresult;
IStream * pstm = NULL;
IMoniker * pmk = NULL;
CLSID clsid;
LPOLESTR pszFile = NULL,
pszClass = NULL,
pszItem = NULL;
LPSTR pszFileA = NULL,
pszClassA = NULL,
pszItemA = NULL,
pszObjectLink;
DWORD cbszFileA = 0,
cbszClassA = 0,
cbszItemA = 0;
LARGE_INTEGER li;
FORMATETC formatetc;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetObjectLink ( %p , %p )\n", NULL,
pDataObj, pmedium));
Assert(pmedium->tymed == TYMED_HGLOBAL);
// fetch LinkSource data
INIT_FORETC(formatetc);
formatetc.cfFormat = g_cfLinkSource;
formatetc.tymed = TYMED_ISTREAM;
hresult = GetDataFromStream(pDataObj, &formatetc, NULL, &pstm);
if( hresult != NOERROR )
{
goto errRtn;
}
// reset the stream seek pointer to the beginning
LISet32(li, 0);
hresult = pstm->Seek(li, STREAM_SEEK_SET, NULL);
if( hresult != NOERROR )
{
goto errRtn;
}
// load the moniker from the stream, so we can parse out
// it's underlying file and item name
hresult = OleLoadFromStream(pstm, IID_IMoniker, (LPLPVOID)&pmk);
if( hresult != NOERROR )
{
goto errRtn;
}
hresult = Ole10_ParseMoniker(pmk, &pszFile, &pszItem);
if( hresult != NOERROR )
{
goto errRtn;
}
// BUGBUG! we should call szFixNet here to turn a potential UNC
// filename into a drive letter combo. Some old OLE1 containers
// don't understand UNC filenames
// now fetch the class ID so we can construct the ClassName
hresult = ReadClassStm(pstm, &clsid);
if( hresult != NOERROR )
{
// it is possible that the stream does not contain
// the clsid of the link source. In this case, we should
// fetch it from the LinkSourceDescriptor
hresult = GetDataFromDescriptor(pDataObj, &clsid,
g_cfLinkSrcDescriptor,
USE_NORMAL_CLSID, NULL, NULL);
if( hresult != NOERROR )
{
goto errRtn;
}
}
hresult = ProgIDFromCLSID(clsid, &pszClass);
if( hresult != NOERROR )
{
goto errRtn;
}
// by this point, we should have all of our strings. Convert
// them to ANSI and stuff them in an hglobal.
hresult = UtPutUNICODEData(_xstrlen(pszClass)+1, pszClass, &pszClassA,
NULL, &cbszClassA);
if( hresult != NOERROR )
{
goto errRtn;
}
else if( pszClassA == NULL )
{
hresult = ResultFromScode(E_FAIL);
goto errRtn;
}
hresult = UtPutUNICODEData(_xstrlen(pszFile)+1, pszFile, &pszFileA,
NULL, &cbszFileA);
if( hresult != NOERROR )
{
goto errRtn;
}
// we are allowed to have a NULL item name
if( pszItem )
{
hresult = UtPutUNICODEData(_xstrlen(pszItem)+1, pszItem,
&pszItemA, NULL, &cbszItemA);
if( hresult != NOERROR )
{
goto errRtn;
}
}
// we allocate 2 extra bytes for terminating '\0''s. (if the
// item name is NULL, we should be safe and terminate it with a
// zero as well, so we'll end up with 3 \0's at the end.
pmedium->hGlobal = GlobalAlloc((GMEM_MOVEABLE | GMEM_SHARE ),
cbszClassA + cbszFileA + cbszItemA + 2);
if( !pmedium->hGlobal )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
pszObjectLink = (LPSTR)GlobalLock(pmedium->hGlobal);
if( !pszObjectLink )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
_xmemcpy(pszObjectLink, pszClassA, cbszClassA);
pszObjectLink += cbszClassA;
_xmemcpy(pszObjectLink, pszFileA, cbszFileA);
pszObjectLink += cbszFileA;
if( pszItemA )
{
_xmemcpy(pszObjectLink, pszItemA, cbszItemA);
pszObjectLink += cbszItemA;
}
else
{
*pszObjectLink = '\0';
pszObjectLink++;
}
*pszObjectLink = '\0';
GlobalUnlock(pmedium->hGlobal);
errRtn:
if( pmk )
{
pmk->Release();
}
if( pszClass )
{
PubMemFree(pszClass);
}
if( pszFile )
{
PubMemFree(pszFile);
}
if( pszItem )
{
PubMemFree(pszItem);
}
if( pszClassA )
{
PubMemFree(pszClassA);
}
if( pszFileA )
{
PubMemFree(pszFileA);
}
if( pszItemA )
{
PubMemFree(pszItemA);
}
if( pstm )
{
pstm->Release();
}
if( hresult != NOERROR )
{
if( pmedium->hGlobal )
{
GlobalFree(pmedium->hGlobal);
pmedium->hGlobal = NULL;
}
}
LEDebugOut((DEB_ITRACE, "%p OUT GetObjectLink ( %lx ) [ %lx ]\n",
NULL, hresult, pmedium->hGlobal));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetOwnerLink
//
// Synopsis: Synthesizes OLE1 OwnerLink format from ObjectDescriptor data
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pmedium] -- where to put the data
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: fetch the clsid and SrcOfCopy string from data offered
// in cfObjectDescriptor. Then turn the class ID into
// the prog ID and then turn all strings into ANSI. From
// this, we can build the OwnerLink format data, which looks
// like:
// szClass\0SrcOfCopy\0\0\0
//
// History: dd-mmm-yy Author Comment
// 10-Jun-94 alexgo author
// Notes:
//
//--------------------------------------------------------------------------
HRESULT GetOwnerLink( IDataObject *pDataObj, STGMEDIUM *pmedium)
{
HRESULT hresult;
LPOLESTR pszSrcOfCopy = NULL,
pszClass = NULL;
LPSTR pszSrcOfCopyA = NULL,
pszClassA = NULL,
pszOwnerLink;
DWORD cbszClassA = 0,
cbszSrcOfCopyA;
CLSID clsid;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetOwnerLink ( %p , %p )\n", NULL,
pDataObj, pmedium));
hresult = GetDataFromDescriptor(pDataObj, &clsid,
g_cfObjectDescriptor, USE_STANDARD_LINK,
&pszSrcOfCopy, NULL);
if( hresult != NOERROR )
{
goto errRtn;
}
// 16bit code called wProgIDFromCLSID, but in when
// constructing ObjectLink, simply called ProgIDFromCLSID
// directly. The w version of the function special-cases
// the prog-id string for a Link object (specifically, "OLE2Link")
// we need to do it here to handle the case of copying an OLE2
// link object to an ole1 container, and then copying the object
// from the ole1 container back to an ole2 container.
hresult = wProgIDFromCLSID(clsid, &pszClass);
if( hresult != NOERROR )
{
goto errRtn;
}
// now convert all our data to ANSI
hresult = UtPutUNICODEData(_xstrlen(pszClass)+1, pszClass,
&pszClassA, NULL, &cbszClassA);
if( hresult != NOERROR )
{
goto errRtn;
}
hresult = UtPutUNICODEData(_xstrlen(pszSrcOfCopy)+1, pszSrcOfCopy,
&pszSrcOfCopyA, NULL, &cbszSrcOfCopyA);
if( hresult != NOERROR )
{
goto errRtn;
}
// now allocate an HGLOBAL for OwnerLink and stuff the
// string data in there. We alloc 2 extra bytes for
// the terminating NULL characters.
pmedium->hGlobal = GlobalAlloc((GMEM_MOVEABLE | GMEM_SHARE |
GMEM_ZEROINIT),
cbszClassA + cbszSrcOfCopyA + 2);
if( !pmedium->hGlobal )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
pszOwnerLink = (LPSTR)GlobalLock(pmedium->hGlobal);
if( !pszOwnerLink )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
_xmemcpy(pszOwnerLink, pszClassA, cbszClassA);
pszOwnerLink += cbszClassA;
_xmemcpy(pszOwnerLink, pszSrcOfCopyA, cbszSrcOfCopyA);
pszOwnerLink += cbszSrcOfCopyA;
*pszOwnerLink = '\0';
pszOwnerLink++;
*pszOwnerLink = '\0';
GlobalUnlock(pmedium->hGlobal);
errRtn:
if( pszClass )
{
PubMemFree(pszClass);
}
if( pszSrcOfCopy )
{
PubMemFree(pszSrcOfCopy);
}
if( pszClassA )
{
PubMemFree(pszClassA);
}
if( pszSrcOfCopyA )
{
PubMemFree(pszSrcOfCopyA);
}
if( hresult != NOERROR )
{
if( pmedium->hGlobal )
{
GlobalFree(pmedium->hGlobal);
pmedium->hGlobal = NULL;
}
}
LEDebugOut((DEB_ITRACE, "%p OUT GetOwnerLink ( %lx ) [ %lx ]\n",
NULL, hresult, pmedium->hGlobal));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: GetPrivateClipboardWindow (internal)
//
// Synopsis: Finds the private ole-clipboard window associated with
// the current appartment (creating one if necessary).
//
// Effects:
//
// Arguments: [fFlags] -- if CLIP_CREATEIFNOTTHERE, then a window
// will be created if none already exists
// if CLIP_QUERY, the current clipboard
// window (if any) will be returned.
//
// Requires:
//
// Returns: HWND (NULL on failure)
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HWND GetPrivateClipboardWindow( CLIPWINDOWFLAGS fFlags )
{
HWND hClipWnd = 0;
HRESULT hr;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN GetPrivateClipboardWindow ( %lx )\n",
NULL, fFlags));
COleTls tls(hr);
if (SUCCEEDED(hr))
{
hClipWnd = tls->hwndClip;
if( !hClipWnd && (fFlags & CLIP_CREATEIFNOTTHERE) )
{
// NOTE: do not need to Stack Switch since the
// the windows is in ole itself.
if (ClipboardInitialize())
{
hClipWnd = ClpCreateWindowEx(NULL,vszClipboardWndClass, NULL,
WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, g_hmodOLE2, NULL);
// if we can't create the window, print an error
LEERROR(!hClipWnd, "Unable to create private clipboard win");
// now set the hwnd into our thread-local storage
tls->hwndClip = hClipWnd;
}
}
}
LEDebugOut((DEB_ITRACE, "%p OUT GetPrivateClipboardWindow ( %lx )\n",
NULL, hClipWnd));
// hClipWnd should always be a valid window
#if DBG ==1
if( hClipWnd )
{
Assert(IsWindow(hClipWnd));
}
#endif // DBG == 1
return hClipWnd;
}
//+-------------------------------------------------------------------------
//
// Function: HandleFromHandle
//
// Synopsis: Calls IDataObject->GetData for the given format and returns
// the resulting handle (duplicated if necessary).
//
// Effects:
//
// Arguments: [pDataObj] -- the source data object
// [pformatetc] -- the formatetc
// [pmedium] -- the tymed to use for GetData and where
// to return the data
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: if data object sets pUnkForRelease after the GetData call,
// we'll duplicate the returned data. Otherwise, we just pass
// out the results of GetData
//
// History: dd-mmm-yy Author Comment
// 11-Apr-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT HandleFromHandle(IDataObject *pDataObj, FORMATETC *pformatetc,
STGMEDIUM *pmedium)
{
HRESULT hresult;
STGMEDIUM tempmedium;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN HandleFromHandle ( %p , %p , %p )\n",
NULL, pDataObj, pformatetc, pmedium));
_xmemset(&tempmedium, 0, sizeof(STGMEDIUM));
hresult = pDataObj->GetData(pformatetc, &tempmedium);
if( hresult == NOERROR )
{
if( tempmedium.pUnkForRelease )
{
pmedium->hGlobal = OleDuplicateData(
tempmedium.hGlobal, pformatetc->cfFormat,
GMEM_MOVEABLE | GMEM_DDESHARE );
if( !pmedium->hGlobal )
{
hresult = ResultFromScode(E_OUTOFMEMORY);
// fall through so we release the original
// data
}
// now release the original data
ReleaseStgMedium(&tempmedium);
}
else
{
pmedium->hGlobal = tempmedium.hGlobal;
}
}
// we don't ever try a GetDataHere for handles
LEDebugOut((DEB_ITRACE, "%p OUT HandleFromHandle ( %lx )\n",
hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: MapCFToFormatetc
//
// Synopsis: Given a clipboard format, find the corresponding formatetc
// in our private data
//
// Effects:
//
// Arguments: [hClipWnd] -- the hwnd of our private clipboard window
// [cf] -- the clipboard format in question
// [pformatec] -- the formatetc to fill in
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 12-Aug-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT MapCFToFormatetc( HWND hClipWnd, UINT cf, FORMATETC *pformatetc )
{
FORMATETC * prgfetc;
HRESULT hresult = S_FALSE;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN MapCFToFormatetc ( %x , %p )\n",
NULL, cf, pformatetc));
prgfetc = (FORMATETC *)GetProp( hClipWnd, pwszClipPrivateData );
LEERROR(!prgfetc, "No private clipboard data!!");
if( prgfetc )
{
for( ; prgfetc->cfFormat != 0; prgfetc++ )
{
if( prgfetc->cfFormat == cf )
{
*pformatetc = *prgfetc;
// Bug#8207 - Corel Draw puts an Aspect and lindex of 0 for the Formatetc.
// if we encounter an Aspect of zero make it DVASPECT_CONTENT.
if (0 == pformatetc->dwAspect)
{
pformatetc->dwAspect = DVASPECT_CONTENT;
pformatetc->lindex = -1;
}
hresult = S_OK;
break;
}
}
}
if( S_FALSE == hresult )
{
// The only time this should fail is if the caller asked for one of our synthesized OLE 1.0 formats.
AssertSz( (cf == g_cfObjectLink) || (cf == g_cfOwnerLink) || (cf == g_cfNative),"Unknown Format");
INIT_FORETC(*pformatetc);
pformatetc->cfFormat = cf;
pformatetc->tymed = TYMED_HGLOBAL;
}
LEDebugOut((DEB_ITRACE, "%p OUT MapCFToFormatec ( )\n", NULL ));
return hresult;
}
//+-------------------------------------------------------------------------
// Function: OleFlushClipboard
//
// Synopsis: Removes the data object from the clipboard (as the app is
// going away). The formats it supports will be rendered on
// the clipboard so that the data may still be 'pasted' by
// other apps.
//
// Effects:
//
// Arguments: void
//
// Requires: the caller must be the owner of the clipboard
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: 1. Make sure the caller is the clipboard window owner
// 2. flush format data onto the clipboard
// 3. remove the clipboard data object
//
// History: dd-mmm-yy Author Comment
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDAPI OleFlushClipboard( void )
{
OLETRACEIN((API_OleFlushClipboard, NOPARAM));
HRESULT hresult;
HWND hClipWnd;
UINT cf = NULL;
HANDLE handle;
VDATEHEAP();
LEDebugOut((DEB_TRACE, "%p _IN OleFlushClipboard ( )\n", NULL));
if( (hClipWnd = VerifyCallerIsClipboardOwner()) == NULL)
{
//caller is not the clipboard owner, so return with an
//error
hresult = ResultFromScode(E_FAIL);
goto errRtn;
}
//
//
// BEGIN: OPENCLIPBOARD
//
//
// now open the clipboard so we can add and remove data
hresult = OleOpenClipboard(hClipWnd, NULL);
if( hresult != NOERROR )
{
goto errRtn;
}
// now go through all of the formats on the clipboard and render
// each one. Doing a GetClipboardData will force rendering for
// any as yet unrendered formats
while( (cf = SSEnumClipboardFormats(cf)) != NULL )
{
// we ignore the return of GetClipboardData. Even if
// fails, we ought to flush as many as we can and then
// remove our data object.
handle = SSGetClipboardData(cf);
LEWARN( !handle, "GetClipboardData failed!");
}
// now get rid of the data object on the clipboard && local
// clipboard window
hresult = RemoveClipboardDataObject(hClipWnd,
CLIPWND_REMOVEFROMCLIPBOARD);
// now close the clipboard
if( !SSCloseClipboard() )
{
LEDebugOut((DEB_WARN, "WARNING: Can't close clipboard!\n"));
// if hresult != NOERROR, then RemoveClipboardDataObject
// failed--as it would be the first failure, we do not want
// to mask that error code with CLIPBRD_E_CANT_CLOSE.
if( hresult == NOERROR )
{
hresult = ResultFromScode(CLIPBRD_E_CANT_CLOSE);
}
}
//
//
// END: CLOSECLIPBOARD
//
errRtn:
LEDebugOut((DEB_TRACE, "%p OUT OleFlushClipboard ( %lx )\n", NULL,
hresult));
OLETRACEOUT((API_OleFlushClipboard, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: OleGetClipboard
//
// Synopsis: Retrieves an IDataObject * from the clipboard.
//
// Effects:
//
// Arguments: [ppDataObj] -- where to put the data object pointer
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm:
// Excepting a hack for 16bit, we open the clipboard and
// prefetch any private clipboard formats we may have
// put there (currently g_cfDataObject and g_cfOlePrivateData).
//
// We then always create a fake data object to return to the
// caller. The QueryInterface on this data object is tweaked
// in such a way to preserve identity (see CClipDataObject::
// QueryInterface). This fake data object always tries to
// satisfy requests (such as QueryGetData) locally by using
// information stored internally (from g_cfOlePrivateData) or
// by looking on the clipboard. This has a significant
// speed advantage. If there is a real data object on the
// clipboard, then we will fetch the interface only when
// needed. See clipdata.cpp for more details.
//
// To retrieve the marshalled IDataObject pointer, we first
// look for g_cfDataObject on the clipboard and retrieve the
// hGlobal associated with that format. The hGlobal contains
// the window handle of the private clipboard window of the
// process that called OleSetClipboard. We use this window
// handle to RPC over to the server process and get the
// IDataObject data transfer object. This is exactly the same
// mechanism used by Drag'n'Drop. As mentioned above, we do
// this only when necessary as an optimization.
//
// History: dd-mmm-yy Author Comment
// 30-Jun-94 alexgo added a hack for hosehead 16bit apps
// 16-May-94 alexgo reduced the amount of work done between
// Open and CloseClipboard
// 16-Mar-94 alexgo author
//
// Notes: We must only hold the clipboard open for a small amount of
// time because apps are calling OleGetClipboard to poll the
// clipboard state during idle time. If the clipboard is held
// open for a long period of time, this leads to frequent
// collisions between multiple apps running simultaneously.
// In particular, we should not make any rpc's during the time
// in which we hold the clipboard open.
//
// If we are in WOW and the caller of OleGetClipboard is
// the clipboard owner, then we will simply return the data
// object straight from our private clipboard window. We
// need to do this because some 16bit apps (such as Project)
// have broken reference counting. See comments below in
// the code.
//
//--------------------------------------------------------------------------
STDAPI OleGetClipboard( IDataObject **ppDataObj )
{
HRESULT hresult;
HWND hClipWnd = NULL; // clipboard owner
HGLOBAL hOlePrivateData = NULL;
FORMATETC * prgFormats = NULL; // array of formats.
DWORD cFormats = 0; // count of formats in
// prgFormats
VDATEHEAP();
OLETRACEIN((API_OleGetClipboard, PARAMFMT("ppDataObj= %p"), ppDataObj));
LEDebugOut((DEB_TRACE, "%p _IN OleGetClipboard ( %p )\n", NULL,
ppDataObj));
VDATEPTROUT_LABEL(ppDataObj, IDataObject *, errNoChkRtn, hresult);
*ppDataObj = NULL;
//
// HACK ALERT!!!!
//
// 16bit Project has a cute reference counting scheme; if they
// own the clipboard, they just call Release on the data object
// they put on the clipboard instead of the data object we
// return from OleGetClipboard (thanks guys).
//
// to work around this, if we are in wow and the caller owns
// the clipboard, we simply AddRef the data object given to us
// in OleSetClipboard and return it.
//
// We do NOT do this for 32bit OLE for several reasons:
// 1. Even though the caller owns the clipboard, he
// does not necessarily control the data object given to
// OleSetClipboard (for example, he can get a data object
// from IOO::GetClipboardData). Thus, it is important
// that we wrap the data object on the clipboard
// (see comments above in the algorithm section)
// 2. Hopefully, the new algorithm makes it harder for
// apps to get away with doing stupid stuff
if( IsWOWThread() )
{
hClipWnd = VerifyCallerIsClipboardOwner();
if( hClipWnd != NULL )
{
// the caller does own the clipboard, just
// return the data object put there
*ppDataObj = (IDataObject *)GetProp( hClipWnd,
CLIPBOARD_DATA_OBJECT_PROP);
if( *ppDataObj )
{
(*ppDataObj)->AddRef();
hresult = NOERROR;
// leave the OleGetClipboard
goto errRtn;
}
// else FALL-THROUGH!!
// This is the case where the clipboard has
// been flushed but the calling app is still the
// 'owner'. We need to construct a fake data
// object in this case.
}
}
//
//
// BEGIN: OPENCLIPBOARD
//
//
hresult = OleOpenClipboard(NULL, NULL);
if( hresult != NOERROR )
{
goto errRtn;
}
// Try to fetch the formatetc data. Note that we may
// not need to use this data if we can successfully rpc
// over to the clipboard data source process to get the original
// data object.
// again, we don't worry about capturing errors here; if something
// fails, then prgFormats will remain NULL
if( SSIsClipboardFormatAvailable(g_cfOlePrivateData) )
{
HGLOBAL hOlePrivateData;
FORMATETC * pClipFormatetc;
FORMATETC * ptemp;
hOlePrivateData = SSGetClipboardData(g_cfOlePrivateData);
if( hOlePrivateData )
{
// hOlePrivateData is an hglobal with a
// zero terminated array of formatetcs in it.
//
// we count them up and copy into an
// *allocated* peice of memory, which may get passed
// to our fake clipboard data object.
pClipFormatetc =
(FORMATETC *)GlobalLock(hOlePrivateData);
LEERROR(pClipFormatetc == NULL, "GlobalLock failed!");
if( pClipFormatetc )
{
for( cFormats = 0, ptemp = pClipFormatetc;
ptemp->cfFormat != 0;
ptemp++ )
{
cFormats++;
}
if( cFormats > 0 )
{
prgFormats = (FORMATETC *)
PrivMemAlloc( cFormats *
sizeof(FORMATETC));
if( prgFormats )
{
_xmemcpy(prgFormats,
pClipFormatetc,
cFormats *
sizeof(FORMATETC));
}
}
GlobalUnlock(hOlePrivateData);
}
}
}
if( !SSCloseClipboard() )
{
LEDebugOut((DEB_ERROR, "ERROR: CloseClipboard failed!\n"));
; // no-op to keep the compiler happy.
}
//
//
// END: CLOSECLIPBOARD
//
//
// Create our own clipboard data object. We always return
// our own data object to the caller
hresult = CClipDataObject::Create(ppDataObj,
prgFormats, cFormats);
// if the Create call succeeds, the fake data object
// will take ownership of the formatetc array. If it
// failed, we should free it.
if( hresult != NOERROR && prgFormats )
{
PrivMemFree(prgFormats);
}
errRtn:
#if DBG == 1
// make the data object is non-NULL on success and NULL on failure
if( hresult != NOERROR )
{
Assert(*ppDataObj == NULL);
}
else
{
Assert(*ppDataObj != NULL);
}
#endif // DBG == 1
LEDebugOut((DEB_TRACE, "%p OUT OleGetClipboard ( %lx ) [ %p ]\n",
NULL, hresult, *ppDataObj));
// register the new IDataObject interface for HookOle
CALLHOOKOBJECTCREATE(hresult,CLSID_NULL,IID_IDataObject,
(IUnknown **)ppDataObj);
errNoChkRtn:
OLETRACEOUT((API_OleGetClipboard, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: OleIsCurrentClipboard
//
// Synopsis: returns NOERROR if the given data object is still on the
// clipboard, false otherwise.
//
// Effects:
//
// Arguments: [pDataObj] -- the data object to check against
//
// Requires: g_cfDataObject must be registered
//
// Returns: S_OK, S_FALSE
//
// Signals:
//
// Modifies:
//
// Algorithm: 1. Verify caller is the clipboard owner
// 2. Compare the data object pointer on our private clipboard
// window against the data object pointer given by the caller
//
// History: dd-mmm-yy Author Comment
// 12-Aug-94 alexgo optimized
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDAPI OleIsCurrentClipboard( IDataObject *pDataObj )
{
OLETRACEIN((API_OleIsCurrentClipboard, PARAMFMT("pDataObj= %p"), pDataObj));
HRESULT hresult = ResultFromScode(S_FALSE);
HWND hClipWnd;
IDataObject * pClipDataObject = NULL;
VDATEHEAP();
LEDebugOut((DEB_TRACE, "%p _IN OleIsCurrentClipboard ( %p )\n",
NULL, pDataObj));
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDataObject,(IUnknown **)&pDataObj);
if( pDataObj == NULL )
{
Assert(hresult == ResultFromScode(S_FALSE));
goto errRtn;
}
// the caller must be the current clipboard owner
if( (hClipWnd = VerifyCallerIsClipboardOwner()) == NULL )
{
LEDebugOut((DEB_WARN,
"WARNING: Caller not clipboard owner\n"));
Assert(hresult == ResultFromScode(S_FALSE));
goto errRtn;
}
// In order for the data object to *really* be on the clipboard,
// the g_cfDataObject must have the HWND of the private clipboard
// window (even if we still have the DataObject pointer stuck
// on the private clipboard window)
// HOWEVER, the data on the clipboard may change at any point
// in time that we don't hold it open. In order to check this data,
// we'd have to open the clipboard (a shared resource). Since
// we don't get any useful information from this check, we don't
// bother doing it.
// now get the pointer property from the window
pClipDataObject = (IDataObject *)GetProp(hClipWnd,
CLIPBOARD_DATA_OBJECT_PROP);
// since we are in the same process, we can directly compare
// these pointers.
if( pClipDataObject == pDataObj)
{
hresult = NOERROR;
}
else
{
hresult = ResultFromScode(S_FALSE);
}
errRtn:
LEDebugOut((DEB_TRACE, "%p OUT OleIsCurrentClipboard ( %lx )\n",
NULL, hresult));
OLETRACEOUT((API_OleIsCurrentClipboard, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: OleOpenClipboard (internal)
//
// Synopsis: Opens the clipboard
//
// Effects:
//
// Arguments: [hClipWnd] -- open the clipboard with this window
// may be NULL.
// [phClipWnd] -- where to put the clipboard owner
// may be NULL
//
// Requires:
//
// Returns: NOERROR: the clipboard was opened successfully
// CLIPBRD_E_CANT_OPEN: could not open the clipboard
//
// Signals:
//
// Modifies:
//
// Algorithm: If we can't open the clipboard, we sleep for a bit and then
// try again (in case we collided with another app). This
// algorithm may need to be improved.
//
// History: dd-mmm-yy Author Comment
// 17-May-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT OleOpenClipboard( HWND hClipWnd, HWND *phClipWnd )
{
HRESULT hresult = NOERROR;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN OleOpenClipboard ( %p )\n", NULL,
phClipWnd ));
if( hClipWnd == NULL )
{
// go ahead and create a clipboard window if we don't already
// have one
hClipWnd = GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
}
if( !hClipWnd )
{
hresult = ResultFromScode(E_FAIL);
}
else if( !SSOpenClipboard(hClipWnd) )
{
// OpenClipboard will fail if another window (i.e. another
// process or thread) has it open
// sleep for a bit and then try again
LEDebugOut((DEB_WARN, "WARNING: First try to open clipboard "
"failed!, sleeping 1 second\n"));
Sleep(0); // give up our time quanta and allow somebody
// else to get scheduled in.
if( !SSOpenClipboard(hClipWnd) )
{
LEDebugOut((DEB_WARN,
"WARNING: Unable to open clipboard on "
"second try\n"));
hresult = ResultFromScode(CLIPBRD_E_CANT_OPEN);
}
}
if( phClipWnd )
{
*phClipWnd = hClipWnd;
}
LEDebugOut((DEB_ITRACE, "%p OUT OleOpenClipboard ( %lx ) "
"[ %p ]\n", NULL, hresult, (phClipWnd)? *phClipWnd : 0));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: OleSetClipboard
//
// Synopsis: puts the given IDataObject on the clipboard
//
// Effects:
//
// Arguments: [pDataObj] -- the data object to put on the clipboard
// if NULL, the clipboard is cleared
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: 1. clear the clipboard of any data object and other data
// that may be there.
// 2. Set [pDataOjbect] as the new clipboard data object
// 3. Set any downlevel formats on the clipboard for delayed
// rendering.
//
// History: dd-mmm-yy Author Comment
// 11-Apr-94 alexgo added support for downlevel formats
// 24-Mar-94 alexgo allow NULL for pDataObject
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDAPI OleSetClipboard( IDataObject *pDataObject )
{
OLETRACEIN((API_OleSetClipboard, PARAMFMT("pDataObject= %p"), pDataObject));
HRESULT hresult = NOERROR;
HWND hClipWnd;
VDATEHEAP();
LEDebugOut((DEB_TRACE, "%p _IN OleSetClipboard ( %p )\n", NULL,
pDataObject));
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDataObject,(IUnknown **)&pDataObject);
//
//
// BEGIN: OPENCLIPBOARD
//
//
hresult = OleOpenClipboard(NULL, &hClipWnd);
if( hresult != NOERROR )
{
goto logRtn;
}
// now clear the data and take ownership of the clipboard with
// an EmptyClipboard call. Note that EmptyClipboard will call
// back to our private clipboard window proc (ClipboardWndProc)
// with a WM_DESTROYCLIPBOARD message. ClipboardWndProc will
// remove any existing data objects (and do the IDO->Release).
if( !SSEmptyClipboard() )
{
LEDebugOut((DEB_WARN, "WARNING: Unable to empty clipboard\n"));
hresult = ResultFromScode(CLIPBRD_E_CANT_EMPTY);
goto errRtn;
}
// NULL is a legal value for pDataObject. Basically, it says
// "clear the clipboard" (which was done above in the EmptyClipboard
// call).
if( pDataObject )
{
// now we set the data object onto the clipboard
hresult = SetClipboardDataObject(hClipWnd, pDataObject);
if( hresult == NOERROR )
{
// now set all of our downlevel formats on the
// clipboard
hresult = SetClipboardFormats(hClipWnd, pDataObject);
}
}
errRtn:
// now close the clipboard.
if( !SSCloseClipboard() )
{
LEDebugOut((DEB_WARN, "WARNING: Unable to close clipboard\n"));
// don't overwrite an earlier error code!
if( hresult == NOERROR )
{
hresult = ResultFromScode(CLIPBRD_E_CANT_CLOSE);
}
}
//
//
// END: CLOSECLIPBOARD
//
//
logRtn:
LEDebugOut((DEB_TRACE, "%p OUT OleSetClipboard ( %p ) \n", NULL,
hresult));
OLETRACEOUT((API_OleSetClipboard, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: RemoveClipboardDataObject (internal)
//
// Synopsis: removes the g_cfDataObject format from the clipboard
// along with the associated information on the private
// clipboard window
//
// Effects: the DataObject pointer will be released.
//
// Arguments: [hClipWnd] -- handle to the private clipboard window
// [fFlags] -- if CLIPWND_REMOVEFROMCLIPBOARD, then we
// will remove g_cfDataObject from the clipboard
//
// Requires: the clipboard must be open
// g_cfDataObject must be set
//
// Returns: HRESULT
//
//
// Signals:
//
// Modifies:
//
// Algorithm: we first remove the g_cfDataObject format from the clipboard
// if fFlags == CLIPWND_REMOVEFROMCLIPBOARD (see comments
// regarding this in Notes) and then remove the properties on our
// local private clipboard window, and finally release the data
// object pointer
//
// History: dd-mmm-yy Author Comment
// 16-Mar-94 alexgo author
//
// Notes: This function succeeds if there is no clipboard data object.
//
// OleSetClipboard also calls this function to remove any data
// object that may be present from a previous OleSetClipboard
// call. (Note that the call is indirect; OleSetClipboard will
// call EmptyClipboard, which will get to our clipboard window
// proc with a WM_DESTROYCLIPBOARD message). OleFlushClipboard
// will also call this function.
//
// CLIPWND_REMOVEFROMCLIPBOARD (and CLIPWND_IGNORECLIPBOARD)
// are used to handle the two different cases in which we
// need to remove the clipboard data object:
// 1. Somebody has called EmptyClipboard(). We will
// get a WM_DESTROYCLIPBOARD message in our private
// clipboard window proc. If we have an AddRef'ed
// pointer on the clipboard (well, really on our
// private clipboard window), it is imperative that
// we do the corresponding Release. However, since
// we are doing this as a result of EmptyClipboard,
// there is no need to futz with data on the clipboard
// (as it's all being deleted anyway).
//
// 2. We are in an OleFlushClipboard call. Here we
// *want* the rest of the clipboard to remain (except
// for our data object pointer), so we just need to
// disable the g_cfDataObject information.
// BUGBUG!! this is currently implemented by
// EmptyClipboard, which will change once the data object
// is implemented.
//
//--------------------------------------------------------------------------
HRESULT RemoveClipboardDataObject( HWND hClipWnd, DWORD fFlags )
{
HRESULT hresult = NOERROR;
IDataObject * pDataObj;
FORMATETC * prgformats = NULL;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN RemoveClipboardDataObject ( %lx , "
"%lx )\n", NULL, hClipWnd, fFlags ));
Assert(g_cfDataObject);
// get && remove the data object pointer. We rely on
// RemoveProp to correctly handle the race condition (somebody
// else doing a GetProp simultaneously with our call).
//
// We must not delete the property since some 16-bit applications
// rely on OleIsCurrentClipboard() during the IDataObject->Release().
//
pDataObj = (IDataObject *)GetProp(hClipWnd, CLIPBOARD_DATA_OBJECT_PROP);
// now get && remove && free our private clipboard data
if( GetProp( hClipWnd, pwszClipPrivateData) != 0 )
{
prgformats = (FORMATETC *)RemoveProp( hClipWnd, pwszClipPrivateData);
}
if( prgformats )
{
PrivMemFree(prgformats);
}
// if there is no data object, then we may have already
// removed it (from a previous call here).
if( pDataObj )
{
DWORD dwAssignAptID;
// pDataObj was AddRef'ed in SetClipboardDataObject
if( !(fFlags & CLIPWND_DONTCALLAPP) )
{
pDataObj->Release();
}
// now get rid of our endpoint property. If pDataObj is
// NULL, then there is no need to do this (which is why the
// call is in this if block!)
hresult = UnAssignEndpointProperty(hClipWnd,&dwAssignAptID);
//
// Now we can remove the property after the IDataObject->Release().
//
RemoveProp(hClipWnd, CLIPBOARD_DATA_OBJECT_PROP);
}
// else HRESULT == NOERROR from initialization
if( (fFlags & CLIPWND_REMOVEFROMCLIPBOARD) &&
SSIsClipboardFormatAvailable(g_cfDataObject) )
{
HGLOBAL hMem;
HWND * phMem;
// since we can't simply remove g_cfDataObject from the clipboard
// (and keep all the other formats), we'll simply replace
// the value (the HWND of our private clipboard window) with a
// NULL. Note that we only do this if g_cfDataObject really
// exists on the clipboard (see the conditional test above)
hMem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE,
sizeof(HWND));
if( !hMem )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalAlloc failed!!"
"\n"));
hresult = ResultFromScode(E_OUTOFMEMORY);
// keep trying to remove the rest of our state
goto errRtn;
}
phMem = (HWND *)GlobalLock(hMem);
if( !phMem )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalLock failed!!"
"\n"));
GlobalFree(hMem);
hresult = ResultFromScode(E_OUTOFMEMORY);
// keep trying to remove the rest of our state
goto errRtn;
}
*phMem = NULL;
GlobalUnlock(hMem);
if( !SSSetClipboardData(g_cfDataObject, hMem) )
{
LEDebugOut((DEB_WARN, "WARNING: Can't RESET clipboard"
" data with SetClipboardData\n"));
GlobalFree(hMem);
// FALL THROUGH!! This is deliberate. Even if
// we can't NULL out the data on the clipboard, we
// ought to at least try to remove the rpc endpoints
// and so forth on our private clipboard window.
}
}
errRtn:
LEDebugOut((DEB_ITRACE, "%p OUT RemoveClipboardDataObject ( %lx )\n",
NULL, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: RenderFormat, private
//
// Synopsis: Grab the content data for the given clipboard format and
// put it on the clipboard
//
// Effects:
//
// Arguments: [hClipWnd] -- the clipboard window
/// [pDataObj] -- the data object from which to get the
// data
// [cf] -- the clipboard format to put on the
// clipboard
//
// Requires: the clipboard must be open for this function to work
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: if format ==
// g_cfNative:
// copy either OLE10_NATIVE_STREAM (if available) into
// an hglobal or put the entire storage from EmbedSource
// or EmbeddedObject on top an hglobal
// g_cfOwnerLink:
// synthesize from g_cfObjectDescriptor
// g_cfObjectLink:
// synthesize from g_cfLinkSource if not offered
// directly by the app
// all others:
// find the formatetc corresponding to the clipboard
// format and ask for data directly using that
// formatetc. In the case of multiple TYMED's, we
// prefer TYMED_ISTORAGE, then TYMED_ISTREAM, then
// handle based mediums. TYMED_FILE is not supported.
//
// History: dd-mmm-yy Author Comment
// 11-Aug-94 alexgo optimized; now use the object's original
// formatetc for GetData calls.
// 10-Jun-94 alexgo added OLE1 support
// 11-Apr-94 alexgo author
//
// Notes: In the ideal world, we simply ask for TYMED_HGLOBAL and
// DVASPECT_CONTENT and then stuff the resulting hglobal onto
// the clipboard. However, this would require apps honoring
// the contractual obligations of an interface, which, of course,
// doesn't happen.
//
// The 16bit code effectively special cased certain formats,
// notably cfEmbeddedOjbect, g_cfLinkSource, and g_cfEmbedSource.
// The algorithm above implements behaviour similar to the
// 16bit sources. Note that new apps can take advantage of
// this functionality for app-defined formats and simplify
// their data transfer IDataObject implementations.
//
//--------------------------------------------------------------------------
HRESULT RenderFormat( HWND hClipWnd, UINT cf, IDataObject *pDataObj )
{
HRESULT hresult = E_FAIL;
STGMEDIUM medium;
FORMATETC formatetc;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN RenderFormat ( %u , %p )\n", NULL,
cf, pDataObj));
_xmemset(&medium, 0, sizeof(STGMEDIUM));
medium.tymed = TYMED_HGLOBAL;
if( cf == g_cfNative )
{
// OLE1 format: synthesize from OLE2 data
hresult = GetNative(pDataObj, &medium);
}
else if( cf == g_cfOwnerLink )
{
// OLE1 format: synthesize from OLE2 data
hresult = GetOwnerLink(pDataObj, &medium);
}
else if( cf == g_cfObjectLink )
{
// ObjectLink is a special OLE1 format. The 16bit OLE
// allowed apps to pass their own ObjectLink data, so
// we preserve that behaviour here. First check to see
// if we can fetch it directly; if not, then we synthesize
// it.
Assert(NOERROR != hresult);
if(S_OK == MapCFToFormatetc(hClipWnd, cf, &formatetc))
{
hresult = HandleFromHandle(pDataObj, &formatetc, &medium);
}
if(NOERROR != hresult)
{
hresult = GetObjectLink(pDataObj, &medium);
}
}
else if( cf == g_cfScreenPicture && IsWOWThread() )
{
//
// HACK ALERT!!!
//
// this is a really evil hack. XL 16bit puts a data format
// "Screen Picture" on the clipboard (which is really nothing
// more than a metafile). However, since neither OLE nor
// Windows knows anything about this metafile (it's just a
// 4byte number to us), the metafile is invalid after XL shuts
// down.
//
// The cute part is that Word 6 uses Screen Picture data
// first (even if it' is invalid). As a result, without
// this hack, you can't paste any objects from XL into Word after
// XL has shut down.
//
// The hack is to never allow "Screen Picture" data to ever be
// realized onto the clipboard. Word 6 then defaults to its
// "normal" OLE2 processing.
hresult = E_FAIL;
}
else
{
// find the original formatetc given to us by the data
// object and use that to fetch the data
Assert(NOERROR != hresult);
if (S_OK == MapCFToFormatetc(hClipWnd, cf, &formatetc))
{
// get the data according to the medium specified in formatetc
if( (formatetc.tymed & TYMED_ISTORAGE) )
{
hresult = GetDataFromStorage(pDataObj, &formatetc,
&medium, NULL);
}
else if( (formatetc.tymed & TYMED_ISTREAM) )
{
hresult = GetDataFromStream(pDataObj, &formatetc,
&medium, NULL);
}
else
{
// we don't support TYMED_FILE
formatetc.tymed &= ~(TYMED_FILE);
// we don't need to do any more checking on the
// formatetc. Even if we have a 'bogus' formatetc,
// it's what the app told us it could support.
hresult = HandleFromHandle(pDataObj, &formatetc,
&medium);
}
}
}
// if hresult is NOERROR, then we have successfully retrieved
// an HGLOBAL that can simply be stuffed onto the clipboard.
if(NOERROR == hresult)
{
Assert(NULL != medium.hGlobal);
if( !SSSetClipboardData(cf, medium.hGlobal ) )
{
LEDebugOut((DEB_WARN, "WARNING: SetClipboardData "
"failed!\n"));
// Dump any memory we're hanging onto
if (GMEM_INVALID_HANDLE != GlobalFlags(medium.hGlobal))
{
ReleaseStgMedium(&medium);
}
hresult = ResultFromScode(CLIPBRD_E_CANT_SET);
}
}
LEDebugOut((DEB_ITRACE, "%p OUT RenderFormat ( %lx )\n", NULL,
hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: SetClipboardDataObject (internal)
//
// Synopsis: Puts an IDataObject on the private clipboard window
// and a handle to the clipboard window on the clipboard
//
// Effects: pDataObject will get AddRef'ed
//
// Arguments: [hClipWnd] -- handle to the private clipboard window
// [pDataObject] -- the data object
//
// Requires: the clipboard must be open
// g_cfDataObject must already be registered
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: We take the private clipboard window (passed as an
// argument) and put the data object pointer on it
// it as a private property. We also attach an rpc endpoint
// to this window as a public property and then put the
// window handle on the clipboard. OleGetClipboard will
// retrieve this window handle, get the rpc endpoint, and
// rpc over here (the set clipboard process) to get the
// IDataObject pointer (marshalled, of course ;-)
//
// History: dd-mmm-yy Author Comment
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT SetClipboardDataObject( HWND hClipWnd ,
IDataObject *pDataObject )
{
HRESULT hresult;
HWND * phMem;
HANDLE hMem;
DWORD dwAssignAptID;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN SetClipboardDataObject ( %lx ,%p )\n",
NULL, hClipWnd, pDataObject ));
AssertSz(pDataObject, "Invalid data object");
Assert(g_cfDataObject);
// try to assign an endpoint property to the window
if( (hresult = AssignEndpointProperty(hClipWnd)) != NOERROR)
{
goto errRtn;
}
// put the data object pointer on the window
if( !SetProp(hClipWnd, CLIPBOARD_DATA_OBJECT_PROP, pDataObject) )
{
// uh-oh, try to back out, but don't worry if we fail
// from now on.
LEDebugOut((DEB_WARN, "WARNING: Unable to SetProp for the "
"data object pointer\n"));
UnAssignEndpointProperty(hClipWnd,&dwAssignAptID);
hresult = ResultFromScode(E_FAIL);
goto errRtn;
}
// now allocate memory for the HWND of the private clipboard
// window and put that on the clipboard
hMem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(HWND));
if( !hMem )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalAlloc failed!!\n"));
hresult = ResultFromScode(E_OUTOFMEMORY);
goto cleanup;
}
phMem = (HWND *)GlobalLock(hMem);
if( !phMem )
{
LEDebugOut((DEB_WARN, "WARNING: GlobalLock failed!!\n"));
hresult = ResultFromScode(E_OUTOFMEMORY);
goto cleanup;
}
*phMem = hClipWnd;
GlobalUnlock(hMem);
if( !SSSetClipboardData( g_cfDataObject, hMem ) )
{
LEDebugOut((DEB_WARN, "WARNING: SetClipboardData for "
"g_cfDataObject failed (%lx) !!\n", GetLastError()));
hresult = ResultFromScode(CLIPBRD_E_CANT_SET);
goto cleanup;
}
pDataObject->AddRef();
hresult = NOERROR;
goto errRtn;
cleanup:
UnAssignEndpointProperty(hClipWnd,&dwAssignAptID);
RemoveProp(hClipWnd, CLIPBOARD_DATA_OBJECT_PROP);
if( hMem )
{
GlobalFree(hMem);
}
errRtn:
LEDebugOut((DEB_ITRACE, "%p OUT SetClipboardDataObject ( %lx )\n",
NULL, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: SetClipboardFormats
//
// Synopsis: enumerates the formats available from the data object and
// sets up the clipboard to delay-render those formats.
//
// Effects:
//
// Arguments: [hClipWnd] -- the clipboard window
// [pDataObj] -- the data object
//
// Requires: the clipboard must be open
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Algorithm: Simply enumerate all of the formats available on the object
// and set each up for delayed rendereing (via
// SetClipboardData(cf, NULL)). We also keep track of the
// "real" formatetc for each clipboard format that we'll render.
// These formatetc's are placed in an array and put on the
// clipboard as g_cfOlePrivateData.
//
// See Notes below for more discussion on this.
//
// OLE1 support: In order to allow OLE1 containers to
// paste OLE2 objects, we have to offer OLE1 formats in addition
// to OLE2 data. We'll offer OLE1 formats as follows:
//
// g_cfNative: if either EmbedSource or EmbeddedObject
// is available AND we can offer OwnerLink
// g_cfOwnerLink: if ObjectDescriptor is available AND
// we can offer Native
// g_cfObjectLink: if LinkSource is available
//
// We will offer the formats in in the order above.
//
// History: dd-mmm-yy Author Comment
// 4/13/95 rogerg Bug#10731 Graph 5.0 does not Include
// its ObjectDescriptor in its enumerator
// 22-Feb-95 alexgo restored broken 16bit behavior to
// make Lotus Freelance work
// 11-Apr-94 alexgo author
//
// Notes: For every clipboard format, we could do a QueryGetData
// to see if RenderFormat would actually succeed. However,
// this relies on the QueryGetData from the app to be both
// fast and accurate (definitely an unwarranted assumption).
//
// Of course, by *not* doing a QueryGetData, we are assuming
// that the formats enumerated by the data object are all
// "legal" for clipboard transmission.
//
// The "real" purpose of g_cfOlePrivateData is to allow us to
// determine whether private clipboard formats where originally
// IStorage based. This is especially important for
// OleFlushClipboard and copy/pasting mutiple objects. Transfer
// of multiple objects relies on private clipboard formats, which,
// in some apps, are only available on IStorage.
//
// These same apps rely on the Formatetc enumerator (or
// QueryGetData) to tell them that the private format is available
// on a storage (since it wasn't always in 16bit OLE). Since we
// *can* offer it to them on a storage (see clipdata.cpp), it
// is important that we preserve the fact that the data originally
// came from a storage.
//
// Instead of always *testing* the data at enumerator or Query
// time, we simply save the formatetc now (with [potentially]
// modified fields such as ptd).
//
// Also note that RenderFormat uses the information in
// OlePrivateData when trying to render clipboard formats.
//
//--------------------------------------------------------------------------
HRESULT SetClipboardFormats( HWND hClipWnd, IDataObject *pDataObj )
{
IEnumFORMATETC * pIEnum;
HRESULT hresult;
FORMATETC formatetc;
HGLOBAL hglobal, hcopy;
FORMATETC * prgFormats;
FORMATETC * prgFormatsCopy;
DWORD cFormats = 0;
DWORD cFormatsCopy = 0;
BOOL fOfferNative = FALSE,
fOfferedNative = FALSE,
fOfferObjectLink = FALSE;
CLSID clsid;
DWORD dwStatus;
BOOL fHaveObjectDescriptor=FALSE;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN SetClipboardFormats ( %p )\n",
NULL, pDataObj));
// get the formatetc enumerator
hresult = pDataObj->EnumFormatEtc(DATADIR_GET, &pIEnum);
if( hresult != NOERROR )
{
goto errRtn;
}
// count up the available formats
while( (hresult = pIEnum->Next(1, &formatetc, NULL)) == NOERROR )
{
if( formatetc.ptd )
{
LEDebugOut((DEB_WARN, "WARNING: Non-NULL ptd!\n"));
PubMemFree(formatetc.ptd);
}
cFormats++;
if (g_cfObjectDescriptor == formatetc.cfFormat)
fHaveObjectDescriptor = TRUE;
}
pIEnum->Reset();
// Bug#10731 - If no ObjectDescriptor in Enumertor increment cFormats in case we have to add it
// when we add the EmbedSource
if (!fHaveObjectDescriptor)
cFormats++;
// for our locally cached copy of the formats
prgFormatsCopy = (FORMATETC *)PrivMemAlloc(
(cFormats + 1)*sizeof(FORMATETC));
if( prgFormatsCopy == NULL )
{
hresult = E_OUTOFMEMORY;
goto errRtn;
}
cFormatsCopy = cFormats;
// since some of these may be duplicated clipboard formats, we
// are potentially allocating more memory than we need, but that's
// OK.
hglobal = GlobalAlloc((GMEM_MOVEABLE | GMEM_DDESHARE),
(cFormats +1)*sizeof(FORMATETC));
prgFormats = (FORMATETC *)GlobalLock(hglobal);
if( prgFormats == NULL )
{
GlobalFree(hglobal);
pIEnum->Release();
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRtn;
}
_xmemset(prgFormats, 0, (cFormats + 1)*sizeof(FORMATETC));
cFormats = 0;
while( (hresult = pIEnum->Next(1, &formatetc, NULL)) ==
NOERROR )
{
// Excel5, 16bit, would offer data in it's enumerator
// that you couldn't actually fetch. Since this causes
// Paste Special behaviour to be broken, we have to fix it
// here.
if( IsWOWThread() )
{
hresult = pDataObj->QueryGetData(&formatetc);
if( hresult != NOERROR )
{
// free the target device (if there
// is one)
if( formatetc.ptd )
{
LEDebugOut((DEB_WARN,
"WARNING: Non-NULL ptd!\n"));
PubMemFree(formatetc.ptd);
}
continue;
}
}
if( formatetc.ptd )
{
LEDebugOut((DEB_WARN, "WARNING: Non-NULL ptd!\n"));
PubMemFree(formatetc.ptd);
formatetc.ptd = NULL;
}
// 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( formatetc.cfFormat >= CF_GDIOBJFIRST &&
formatetc.cfFormat <= CF_GDIOBJLAST )
{
LEDebugOut((DEB_WARN, "WARNING: caller attempted to "
"use a special GDI format (%lx)\n",
formatetc.cfFormat));
// keep going though and get the rest of the clipboard
// formats
continue;
}
// HACK ALERT!!!
if( IsWOWThread() )
{
// Word6 offers CF_BITMAP on HGLOBAL in it's enumerator
// but only succeeds the GetData call if TYMED_GDI is
// specified. So patch up the formatetc to reflect
// something more accurate.
if( (formatetc.cfFormat == CF_BITMAP ||
formatetc.cfFormat == CF_PALETTE ) &&
formatetc.tymed == TYMED_HGLOBAL )
{
formatetc.tymed = TYMED_GDI;
}
}
// determine if we should offer any OLE1 formats as well
if( formatetc.cfFormat == g_cfEmbeddedObject ||
formatetc.cfFormat == g_cfEmbedSource )
{
fOfferNative = TRUE;
}
else if( formatetc.cfFormat == g_cfLinkSource )
{
fOfferObjectLink = TRUE;
// if the app offers ObjectLink itself, then we'll
// consider a private clipboard format and set
// it up for delayed rendering as any other format.
// We'll check for this down below.
}
// if we haven't already setup this clipboard format, do
// so now.
if( !SSIsClipboardFormatAvailable(formatetc.cfFormat) )
{
// Bug#10731 If we are adding the EmbedSource but there was no Object Descriptor in
// the Enumerator, see if we can add the Object Descriptor now.
if ( (formatetc.cfFormat == g_cfEmbedSource) && !fHaveObjectDescriptor)
{
FORMATETC fetcObjDescriptor;
fetcObjDescriptor.cfFormat = g_cfObjectDescriptor;
fetcObjDescriptor.ptd = NULL;
fetcObjDescriptor.dwAspect = DVASPECT_CONTENT;
fetcObjDescriptor.lindex = -1;
fetcObjDescriptor.tymed = TYMED_HGLOBAL ;
if ( SUCCEEDED(pDataObj->QueryGetData(&fetcObjDescriptor)) )
{
SSSetClipboardData(g_cfObjectDescriptor, NULL);
prgFormats[cFormats] = fetcObjDescriptor;
cFormats++;
}
}
// Bug#18669 - if dwAspect was set to NULL the 16 bit dlls would
// set it to content.
if ( (NULL == formatetc.dwAspect) && IsWOWThread() )
{
formatetc.dwAspect = DVASPECT_CONTENT;
formatetc.lindex = -1; // CorelDraw also has a lindex of 0.
}
// no way to catch any errors
SSSetClipboardData(formatetc.cfFormat, NULL);
prgFormats[cFormats] = formatetc;
cFormats++;
}
// HACK ALERT!!!! Lotus Freelance 2.1 depends on getting
// cfNative *before* presentation formats (like CF_METAFILEPICT).
// Therefore, we emulate OLE16 behaviour and offer cfNative
// and cfOwnerLink immediately *after* cfEmbeddedObject or
// cfEmbedSource.
//
// NB! This hack destroys the exact ordering of formats
// offered by the data object given in OleSetClipboard
if( fOfferNative && !fOfferedNative )
{
// even if the calls below fail, don't put OLE1 formats
// the clipboard again.
fOfferedNative = TRUE;
// this call will fail if CF_OBJECTDESCRIPTOR is not
// available
hresult = GetDataFromDescriptor(pDataObj, &clsid,
g_cfObjectDescriptor,
USE_NORMAL_CLSID, NULL, NULL);
// we do not want static objects like metafiles and
// dib's to be treated as embeddings by OLE1 containers.
// They will be able to better handle the data as just
// a plain metafile
if( hresult == NOERROR &&
!IsEqualCLSID(clsid, CLSID_StaticMetafile) &&
!IsEqualCLSID(clsid, CLSID_StaticDib) &&
!IsEqualCLSID(clsid, CLSID_Picture_EnhMetafile))
{
SSSetClipboardData(g_cfNative, NULL);
SSSetClipboardData(g_cfOwnerLink, NULL);
}
}
}
// we don't need to do this now because we'll reset hresult
// to NOERROR below. Note that this does mean that we'll
// ignore any failure from the enumerator. We do this for two
// reasons:
// 1. The enumerator really should *not* fail on anything;
// all it should do is memcmp some stuff into the formatetc
// that we pass in. If it decides to fail at some point,
// then we'll just put on the clipboard whatever was
// enumerated 'til that point.
// 2. It is too late (88/28/94) to change for NT3.5 This
// behaviour (ingnoring failure) has been around for a while
// (see reason #1). It is possible that some apps are
// returning failure instead of S_FALSE to terminate.
// If we checked for failure and returned, we'd break those
// apps.
//
//if( hresult == ResultFromScode(S_FALSE) )
//{
// this is OK, means the enumerator terminated successfully
// hresult = NOERROR;
//}
pIEnum->Release();
// now keep a copy of the formats locally
_xmemcpy(prgFormatsCopy, prgFormats, (cFormatsCopy + 1)*sizeof(FORMATETC));
GlobalUnlock(hglobal);
// now set up any OLE1 formats we might need to offer.
// if the app offers ObjectLink itself, then we will have already
// set it up for delayed rendering in the enumerator loop above
if( fOfferObjectLink && !SSIsClipboardFormatAvailable(g_cfObjectLink) )
{
hresult = GetDataFromDescriptor(pDataObj, NULL,
g_cfLinkSrcDescriptor,
USE_NORMAL_CLSID, NULL, &dwStatus);
// there are some kinds of links that can't be linked to
// by OLE1 containers. Non-filename links (e.g. a progid
// moniker) and links to embeddings are common examples.
// Clipboard source providers indicate this state by
// setting the OLEMISC_CANLINKBYOLE1 bit in the status
// field of LinkSourceDescriptor
if( hresult == NOERROR && (dwStatus & OLEMISC_CANLINKBYOLE1) )
{
SSSetClipboardData(g_cfObjectLink, NULL);
}
}
// even if the calls to GetDataFromDescriptor failed above, it only
// means that we can't render OLE1 formats. This is OK.
hresult = NOERROR;
// now stuff the formatetc's on the clipboard and our private
// clipboard window (for use by RenderFormat).
if( !SetProp(hClipWnd, pwszClipPrivateData, (HANDLE)prgFormatsCopy) )
{
LEDebugOut((DEB_WARN, "WARNING: SetProp to clipboard "
"window failed!\n"));
GlobalFree(hglobal);
PrivMemFree(prgFormatsCopy);
hresult = ResultFromScode(E_FAIL);
goto errRtn;
}
if( !SSSetClipboardData(g_cfOlePrivateData, hglobal) )
{
GlobalFree(hglobal); // on success, the clipboard will
// take ownership of our hglobal.
LEDebugOut((DEB_WARN, "WARNING: Unable to set clipboard "
"formats!\n"));
}
errRtn:
LEDebugOut((DEB_ITRACE, "%p OUT SetClipboardFormats ( %lx )\n", NULL,
hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: VerifyCallerIsClipboardOwner (internal)
//
// Synopsis: Checks to make sure the caller is the clipboard owner
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: HWND to the private clipboard window (the owner of the
// clipboard) upon success
// NULL on failure.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 16-Mar-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
HWND VerifyCallerIsClipboardOwner( void )
{
HWND hClipWnd,
hWndClipOwner;
VDATEHEAP();
LEDebugOut((DEB_ITRACE, "%p _IN VerifyCallerIsClipboardOwner ( )\n",
NULL ));
// don't create a window if none exists
hClipWnd = GetPrivateClipboardWindow( CLIP_QUERY );
if( hClipWnd )
{
hWndClipOwner = SSGetClipboardOwner();
if( hClipWnd != hWndClipOwner )
{
// caller is not owner, return NULL
hClipWnd = NULL;
}
}
LEDebugOut((DEB_ITRACE, "%p OUT VerifyCallerIsClipboardOwner "
"( %lx )\n", NULL, hClipWnd));
return hClipWnd;
}