| /* | |
| * OLE 2 clipboard support | |
| * | |
| * Copyright 1999 Noel Borthwick <noel@macadamian.com> | |
| * Copyright 2000 Abey George <abey@macadamian.com> | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| * NOTES: | |
| * This file contains the implementation for the OLE Clipboard and its | |
| * internal interfaces. The OLE clipboard interacts with an IDataObject | |
| * interface via the OleSetClipboard, OleGetClipboard and | |
| * OleIsCurrentClipboard API's. An internal IDataObject delegates | |
| * to a client supplied IDataObject or the WIN32 clipboard API depending | |
| * on whether OleSetClipboard has been invoked. | |
| * Here are some operating scenarios: | |
| * | |
| * 1. OleSetClipboard called: In this case the internal IDataObject | |
| * delegates to the client supplied IDataObject. Additionally OLE takes | |
| * ownership of the Windows clipboard and any HGLOCBAL IDataObject | |
| * items are placed on the Windows clipboard. This allows non OLE aware | |
| * applications to access these. A local WinProc fields WM_RENDERFORMAT | |
| * and WM_RENDERALLFORMATS messages in this case. | |
| * | |
| * 2. OleGetClipboard called without previous OleSetClipboard. Here the internal | |
| * IDataObject functionality wraps around the WIN32 clipboard API. | |
| * | |
| * 3. OleGetClipboard called after previous OleSetClipboard. Here the internal | |
| * IDataObject delegates to the source IDataObjects functionality directly, | |
| * thereby bypassing the Windows clipboard. | |
| * | |
| * Implementation references : Inside OLE 2'nd edition by Kraig Brockschmidt | |
| * | |
| * TODO: | |
| * - Support for pasting between different processes. OLE clipboard support | |
| * currently works only for in process copy and paste. Since we internally | |
| * store a pointer to the source's IDataObject and delegate to that, this | |
| * will fail if the IDataObject client belongs to a different process. | |
| * - IDataObject::GetDataHere is not implemented | |
| * - OleFlushClipboard needs to additionally handle TYMED_IStorage media | |
| * by copying the storage into global memory. Subsequently the default | |
| * data object exposed through OleGetClipboard must convert this TYMED_HGLOBAL | |
| * back to TYMED_IStorage. | |
| * - OLE1 compatibility formats to be synthesized from OLE2 formats and put on | |
| * clipboard in OleSetClipboard. | |
| * | |
| */ | |
| #include <assert.h> | |
| #include <string.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winerror.h" | |
| #include "winnls.h" | |
| #include "ole2.h" | |
| #include "wine/debug.h" | |
| #include "olestd.h" | |
| #include "storage32.h" | |
| #define HANDLE_ERROR(err) { hr = err; TRACE("(HRESULT=%lx)\n", (HRESULT)err); goto CLEANUP; } | |
| /* For CoGetMalloc (MEMCTX_TASK is currently ignored) */ | |
| #ifndef MEMCTX_TASK | |
| #define MEMCTX_TASK -1 | |
| #endif | |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); | |
| /**************************************************************************** | |
| * OLEClipbrd | |
| * DO NOT add any members before the VTables declaration! | |
| */ | |
| struct OLEClipbrd | |
| { | |
| /* | |
| * List all interface VTables here | |
| */ | |
| ICOM_VTABLE(IDataObject)* lpvtbl1; /* IDataObject VTable */ | |
| /* | |
| * The hidden OLE clipboard window. This window is used as the bridge between the | |
| * the OLE and windows clipboard API. (Windows creates one such window per process) | |
| */ | |
| HWND hWndClipboard; | |
| /* | |
| * Pointer to the source data object (via OleSetClipboard) | |
| */ | |
| IDataObject* pIDataObjectSrc; | |
| /* | |
| * The registered DataObject clipboard format | |
| */ | |
| UINT cfDataObj; | |
| /* | |
| * The handle to our ourself | |
| */ | |
| UINT hSelf; | |
| /* | |
| * Reference count of this object | |
| */ | |
| ULONG ref; | |
| }; | |
| typedef struct OLEClipbrd OLEClipbrd; | |
| /**************************************************************************** | |
| * IEnumFORMATETC implementation | |
| * DO NOT add any members before the VTables declaration! | |
| */ | |
| typedef struct | |
| { | |
| /* IEnumFORMATETC VTable */ | |
| ICOM_VFIELD(IEnumFORMATETC); | |
| /* IEnumFORMATETC fields */ | |
| UINT posFmt; /* current enumerator position */ | |
| UINT countFmt; /* number of EnumFORMATETC's in array */ | |
| LPFORMATETC pFmt; /* array of EnumFORMATETC's */ | |
| /* | |
| * Reference count of this object | |
| */ | |
| DWORD ref; | |
| /* | |
| * IUnknown implementation of the parent data object. | |
| */ | |
| IUnknown* pUnkDataObj; | |
| } IEnumFORMATETCImpl; | |
| typedef struct PresentationDataHeader | |
| { | |
| BYTE unknown1[28]; | |
| DWORD dwObjectExtentX; | |
| DWORD dwObjectExtentY; | |
| DWORD dwSize; | |
| } PresentationDataHeader; | |
| /* | |
| * The one and only OLEClipbrd object which is created by OLEClipbrd_Initialize() | |
| */ | |
| static HGLOBAL hTheOleClipboard = 0; | |
| static OLEClipbrd* theOleClipboard = NULL; | |
| /* | |
| * Prototypes for the methods of the OLEClipboard class. | |
| */ | |
| extern void OLEClipbrd_Initialize(); | |
| extern void OLEClipbrd_UnInitialize(); | |
| static OLEClipbrd* OLEClipbrd_Construct(); | |
| static void OLEClipbrd_Destroy(OLEClipbrd* ptrToDestroy); | |
| static HWND OLEClipbrd_CreateWindow(); | |
| static void OLEClipbrd_DestroyWindow(HWND hwnd); | |
| LRESULT CALLBACK OLEClipbrd_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
| static HRESULT OLEClipbrd_RenderFormat( IDataObject *pIDataObject, LPFORMATETC pFormatetc ); | |
| static HGLOBAL OLEClipbrd_GlobalDupMem( HGLOBAL hGlobalSrc ); | |
| /* | |
| * Prototypes for the methods of the OLEClipboard class | |
| * that implement IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_QueryInterface( | |
| IDataObject* iface, | |
| REFIID riid, | |
| void** ppvObject); | |
| static ULONG WINAPI OLEClipbrd_IDataObject_AddRef( | |
| IDataObject* iface); | |
| static ULONG WINAPI OLEClipbrd_IDataObject_Release( | |
| IDataObject* iface); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetcIn, | |
| STGMEDIUM* pmedium); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetDataHere( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_QueryGetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetCanonicalFormatEtc( | |
| IDataObject* iface, | |
| LPFORMATETC pformatectIn, | |
| LPFORMATETC pformatetcOut); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_SetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium, | |
| BOOL fRelease); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_EnumFormatEtc( | |
| IDataObject* iface, | |
| DWORD dwDirection, | |
| IEnumFORMATETC** ppenumFormatEtc); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_DAdvise( | |
| IDataObject* iface, | |
| FORMATETC* pformatetc, | |
| DWORD advf, | |
| IAdviseSink* pAdvSink, | |
| DWORD* pdwConnection); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_DUnadvise( | |
| IDataObject* iface, | |
| DWORD dwConnection); | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_EnumDAdvise( | |
| IDataObject* iface, | |
| IEnumSTATDATA** ppenumAdvise); | |
| /* | |
| * Prototypes for the IEnumFORMATETC methods. | |
| */ | |
| static LPENUMFORMATETC OLEClipbrd_IEnumFORMATETC_Construct(UINT cfmt, const FORMATETC afmt[], | |
| LPUNKNOWN pUnkDataObj); | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_QueryInterface(LPENUMFORMATETC iface, REFIID riid, | |
| LPVOID* ppvObj); | |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_AddRef(LPENUMFORMATETC iface); | |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_Release(LPENUMFORMATETC iface); | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Next(LPENUMFORMATETC iface, ULONG celt, | |
| FORMATETC* rgelt, ULONG* pceltFethed); | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Skip(LPENUMFORMATETC iface, ULONG celt); | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Reset(LPENUMFORMATETC iface); | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Clone(LPENUMFORMATETC iface, LPENUMFORMATETC* ppenum); | |
| /* | |
| * Virtual function table for the OLEClipbrd's exposed IDataObject interface | |
| */ | |
| static ICOM_VTABLE(IDataObject) OLEClipbrd_IDataObject_VTable = | |
| { | |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE | |
| OLEClipbrd_IDataObject_QueryInterface, | |
| OLEClipbrd_IDataObject_AddRef, | |
| OLEClipbrd_IDataObject_Release, | |
| OLEClipbrd_IDataObject_GetData, | |
| OLEClipbrd_IDataObject_GetDataHere, | |
| OLEClipbrd_IDataObject_QueryGetData, | |
| OLEClipbrd_IDataObject_GetCanonicalFormatEtc, | |
| OLEClipbrd_IDataObject_SetData, | |
| OLEClipbrd_IDataObject_EnumFormatEtc, | |
| OLEClipbrd_IDataObject_DAdvise, | |
| OLEClipbrd_IDataObject_DUnadvise, | |
| OLEClipbrd_IDataObject_EnumDAdvise | |
| }; | |
| /* | |
| * Virtual function table for IEnumFORMATETC interface | |
| */ | |
| static struct ICOM_VTABLE(IEnumFORMATETC) efvt = | |
| { | |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE | |
| OLEClipbrd_IEnumFORMATETC_QueryInterface, | |
| OLEClipbrd_IEnumFORMATETC_AddRef, | |
| OLEClipbrd_IEnumFORMATETC_Release, | |
| OLEClipbrd_IEnumFORMATETC_Next, | |
| OLEClipbrd_IEnumFORMATETC_Skip, | |
| OLEClipbrd_IEnumFORMATETC_Reset, | |
| OLEClipbrd_IEnumFORMATETC_Clone | |
| }; | |
| /* | |
| * Name of our registered OLE clipboard window class | |
| */ | |
| CHAR OLEClipbrd_WNDCLASS[] = "CLIPBRDWNDCLASS"; | |
| /* | |
| * If we need to store state info we can store it here. | |
| * For now we dont need this functionality. | |
| * | |
| typedef struct tagClipboardWindowInfo | |
| { | |
| } ClipboardWindowInfo; | |
| */ | |
| /*---------------------------------------------------------------------* | |
| * Win32 OLE clipboard API | |
| *---------------------------------------------------------------------*/ | |
| /*********************************************************************** | |
| * OleSetClipboard [OLE32.127] | |
| * Places a pointer to the specified data object onto the clipboard, | |
| * making the data object accessible to the OleGetClipboard function. | |
| * | |
| * RETURNS: | |
| * | |
| * S_OK IDataObject pointer placed on the clipboard | |
| * CLIPBRD_E_CANT_OPEN OpenClipboard failed | |
| * CLIPBRD_E_CANT_EMPTY EmptyClipboard failed | |
| * CLIPBRD_E_CANT_CLOSE CloseClipboard failed | |
| * CLIPBRD_E_CANT_SET SetClipboard failed | |
| */ | |
| HRESULT WINAPI OleSetClipboard(IDataObject* pDataObj) | |
| { | |
| HRESULT hr = S_OK; | |
| IEnumFORMATETC* penumFormatetc = NULL; | |
| FORMATETC rgelt; | |
| BOOL bClipboardOpen = FALSE; | |
| /* | |
| HGLOBAL hDataObject = 0; | |
| OLEClipbrd **ppDataObject; | |
| */ | |
| TRACE("(%p)\n", pDataObj); | |
| /* | |
| * Make sure we have a clipboard object | |
| */ | |
| OLEClipbrd_Initialize(); | |
| /* | |
| * If the Ole clipboard window hasn't been created yet, create it now. | |
| */ | |
| if ( !theOleClipboard->hWndClipboard ) | |
| theOleClipboard->hWndClipboard = OLEClipbrd_CreateWindow(); | |
| if ( !theOleClipboard->hWndClipboard ) /* sanity check */ | |
| HANDLE_ERROR( E_FAIL ); | |
| /* | |
| * Open the Windows clipboard, associating it with our hidden window | |
| */ | |
| if ( !(bClipboardOpen = OpenClipboard(theOleClipboard->hWndClipboard)) ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_OPEN ); | |
| /* | |
| * Empty the current clipboard and make our window the clipboard owner | |
| * NOTE: This will trigger a WM_DESTROYCLIPBOARD message | |
| */ | |
| if ( !EmptyClipboard() ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_EMPTY ); | |
| /* | |
| * If we are already holding on to an IDataObject first release that. | |
| */ | |
| if ( theOleClipboard->pIDataObjectSrc ) | |
| { | |
| IDataObject_Release(theOleClipboard->pIDataObjectSrc); | |
| theOleClipboard->pIDataObjectSrc = NULL; | |
| } | |
| /* | |
| * AddRef the data object passed in and save its pointer. | |
| * A NULL value indicates that the clipboard should be emptied. | |
| */ | |
| theOleClipboard->pIDataObjectSrc = pDataObj; | |
| if ( pDataObj ) | |
| { | |
| IDataObject_AddRef(theOleClipboard->pIDataObjectSrc); | |
| } | |
| /* | |
| * Enumerate all HGLOBAL formats supported by the source and make | |
| * those formats available using delayed rendering using SetClipboardData. | |
| * Only global memory based data items may be made available to non-OLE | |
| * applications via the standard Windows clipboard API. Data based on other | |
| * mediums(non TYMED_HGLOBAL) can only be accessed via the Ole Clipboard API. | |
| * | |
| * TODO: Do we need to additionally handle TYMED_IStorage media by copying | |
| * the storage into global memory? | |
| */ | |
| if ( pDataObj ) | |
| { | |
| if ( FAILED(hr = IDataObject_EnumFormatEtc( pDataObj, | |
| DATADIR_GET, | |
| &penumFormatetc ))) | |
| { | |
| HANDLE_ERROR( hr ); | |
| } | |
| while ( S_OK == IEnumFORMATETC_Next(penumFormatetc, 1, &rgelt, NULL) ) | |
| { | |
| if ( rgelt.tymed == TYMED_HGLOBAL ) | |
| { | |
| CHAR szFmtName[80]; | |
| TRACE("(cfFormat=%d:%s)\n", rgelt.cfFormat, | |
| GetClipboardFormatNameA(rgelt.cfFormat, szFmtName, sizeof(szFmtName)-1) | |
| ? szFmtName : ""); | |
| SetClipboardData( rgelt.cfFormat, (HANDLE)NULL); | |
| } | |
| } | |
| IEnumFORMATETC_Release(penumFormatetc); | |
| } | |
| /* | |
| * Windows additionally creates a new "DataObject" clipboard format | |
| * and stores in on the clipboard. We could possibly store a pointer | |
| * to our internal IDataObject interface on the clipboard. I'm not | |
| * sure what the use of this is though. | |
| * Enable the code below for this functionality. | |
| */ | |
| /* | |
| theOleClipboard->cfDataObj = RegisterClipboardFormatA("DataObject"); | |
| hDataObject = GlobalAlloc( GMEM_DDESHARE|GMEM_MOVEABLE|GMEM_ZEROINIT, | |
| sizeof(OLEClipbrd *)); | |
| if (hDataObject==0) | |
| HANDLE_ERROR( E_OUTOFMEMORY ); | |
| ppDataObject = (OLEClipbrd**)GlobalLock(hDataObject); | |
| *ppDataObject = theOleClipboard; | |
| GlobalUnlock(hDataObject); | |
| if ( !SetClipboardData( theOleClipboard->cfDataObj, hDataObject ) ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_SET ); | |
| */ | |
| hr = S_OK; | |
| CLEANUP: | |
| /* | |
| * Close Windows clipboard (It remains associated with our window) | |
| */ | |
| if ( bClipboardOpen && !CloseClipboard() ) | |
| hr = CLIPBRD_E_CANT_CLOSE; | |
| /* | |
| * Release the source IDataObject if something failed | |
| */ | |
| if ( FAILED(hr) ) | |
| { | |
| if (theOleClipboard->pIDataObjectSrc) | |
| { | |
| IDataObject_Release(theOleClipboard->pIDataObjectSrc); | |
| theOleClipboard->pIDataObjectSrc = NULL; | |
| } | |
| } | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * OleGetClipboard [OLE32.105] | |
| * Returns a pointer to our internal IDataObject which represents the conceptual | |
| * state of the Windows clipboard. If the current clipboard already contains | |
| * an IDataObject, our internal IDataObject will delegate to this object. | |
| */ | |
| HRESULT WINAPI OleGetClipboard(IDataObject** ppDataObj) | |
| { | |
| HRESULT hr = S_OK; | |
| TRACE("()\n"); | |
| /* | |
| * Make sure we have a clipboard object | |
| */ | |
| OLEClipbrd_Initialize(); | |
| if (!theOleClipboard) | |
| return E_OUTOFMEMORY; | |
| /* Return a reference counted IDataObject */ | |
| hr = IDataObject_QueryInterface( (IDataObject*)&(theOleClipboard->lpvtbl1), | |
| &IID_IDataObject, (void**)ppDataObj); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * OleFlushClipboard [OLE2.76] | |
| */ | |
| HRESULT WINAPI OleFlushClipboard16(void) | |
| { | |
| return OleFlushClipboard(); | |
| } | |
| /****************************************************************************** | |
| * OleFlushClipboard [OLE32.103] | |
| * Renders the data from the source IDataObject into the windows clipboard | |
| * | |
| * TODO: OleFlushClipboard needs to additionally handle TYMED_IStorage media | |
| * by copying the storage into global memory. Subsequently the default | |
| * data object exposed through OleGetClipboard must convert this TYMED_HGLOBAL | |
| * back to TYMED_IStorage. | |
| */ | |
| HRESULT WINAPI OleFlushClipboard() | |
| { | |
| IEnumFORMATETC* penumFormatetc = NULL; | |
| FORMATETC rgelt; | |
| HRESULT hr = S_OK; | |
| BOOL bClipboardOpen = FALSE; | |
| IDataObject* pIDataObjectSrc = NULL; | |
| TRACE("()\n"); | |
| /* | |
| * Make sure we have a clipboard object | |
| */ | |
| OLEClipbrd_Initialize(); | |
| /* | |
| * Already flushed or no source DataObject? Nothing to do. | |
| */ | |
| if (!theOleClipboard->pIDataObjectSrc) | |
| return S_OK; | |
| /* | |
| * Addref and save the source data object we are holding on to temporarily, | |
| * since it will be released when we empty the clipboard. | |
| */ | |
| pIDataObjectSrc = theOleClipboard->pIDataObjectSrc; | |
| IDataObject_AddRef(pIDataObjectSrc); | |
| /* | |
| * Open the Windows clipboard | |
| */ | |
| if ( !(bClipboardOpen = OpenClipboard(theOleClipboard->hWndClipboard)) ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_OPEN ); | |
| /* | |
| * Empty the current clipboard | |
| */ | |
| if ( !EmptyClipboard() ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_EMPTY ); | |
| /* | |
| * Render all HGLOBAL formats supported by the source into | |
| * the windows clipboard. | |
| */ | |
| if ( FAILED( hr = IDataObject_EnumFormatEtc( pIDataObjectSrc, | |
| DATADIR_GET, | |
| &penumFormatetc) )) | |
| { | |
| HANDLE_ERROR( hr ); | |
| } | |
| while ( S_OK == IEnumFORMATETC_Next(penumFormatetc, 1, &rgelt, NULL) ) | |
| { | |
| if ( rgelt.tymed == TYMED_HGLOBAL ) | |
| { | |
| CHAR szFmtName[80]; | |
| TRACE("(cfFormat=%d:%s)\n", rgelt.cfFormat, | |
| GetClipboardFormatNameA(rgelt.cfFormat, szFmtName, sizeof(szFmtName)-1) | |
| ? szFmtName : ""); | |
| /* | |
| * Render the clipboard data | |
| */ | |
| if ( FAILED(OLEClipbrd_RenderFormat( pIDataObjectSrc, &rgelt )) ) | |
| continue; | |
| } | |
| } | |
| IEnumFORMATETC_Release(penumFormatetc); | |
| /* | |
| * Release the source data object we are holding on to | |
| */ | |
| IDataObject_Release(pIDataObjectSrc); | |
| CLEANUP: | |
| /* | |
| * Close Windows clipboard (It remains associated with our window) | |
| */ | |
| if ( bClipboardOpen && !CloseClipboard() ) | |
| hr = CLIPBRD_E_CANT_CLOSE; | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * OleIsCurrentClipboard [OLE32.110] | |
| */ | |
| HRESULT WINAPI OleIsCurrentClipboard ( IDataObject *pDataObject) | |
| { | |
| TRACE("()\n"); | |
| /* | |
| * Make sure we have a clipboard object | |
| */ | |
| OLEClipbrd_Initialize(); | |
| if (!theOleClipboard) | |
| return E_OUTOFMEMORY; | |
| return (pDataObject == theOleClipboard->pIDataObjectSrc) ? S_OK : S_FALSE; | |
| } | |
| /*---------------------------------------------------------------------* | |
| * Internal implementation methods for the OLE clipboard | |
| *---------------------------------------------------------------------*/ | |
| /*********************************************************************** | |
| * OLEClipbrd_Initialize() | |
| * Initializes the OLE clipboard. | |
| */ | |
| void OLEClipbrd_Initialize() | |
| { | |
| /* | |
| * Create the clipboard if necessary | |
| */ | |
| if ( !theOleClipboard ) | |
| { | |
| TRACE("()\n"); | |
| theOleClipboard = OLEClipbrd_Construct(); | |
| } | |
| } | |
| /*********************************************************************** | |
| * OLEClipbrd_UnInitialize() | |
| * Un-Initializes the OLE clipboard | |
| */ | |
| void OLEClipbrd_UnInitialize() | |
| { | |
| TRACE("()\n"); | |
| /* | |
| * Destroy the clipboard if no one holds a reference to us. | |
| * Note that the clipboard was created with a reference count of 1. | |
| */ | |
| if ( theOleClipboard && (theOleClipboard->ref <= 1) ) | |
| { | |
| OLEClipbrd_Destroy( theOleClipboard ); | |
| } | |
| else | |
| { | |
| WARN( "() : OLEClipbrd_UnInitialize called while client holds an IDataObject reference!\n"); | |
| } | |
| } | |
| /********************************************************* | |
| * Construct the OLEClipbrd class. | |
| */ | |
| static OLEClipbrd* OLEClipbrd_Construct() | |
| { | |
| OLEClipbrd* newObject = NULL; | |
| HGLOBAL hNewObject = 0; | |
| /* | |
| * Allocate space for the object. We use GlobalAlloc since we need | |
| * an HGLOBAL to expose our DataObject as a registered clipboard type. | |
| */ | |
| hNewObject = GlobalAlloc( GMEM_DDESHARE|GMEM_MOVEABLE|GMEM_ZEROINIT, | |
| sizeof(OLEClipbrd)); | |
| if (hNewObject==0) | |
| return NULL; | |
| /* | |
| * Lock the handle for the entire lifetime of the clipboard. | |
| */ | |
| newObject = GlobalLock(hNewObject); | |
| /* | |
| * Initialize the virtual function table. | |
| */ | |
| newObject->lpvtbl1 = &OLEClipbrd_IDataObject_VTable; | |
| /* | |
| * Start with one reference count. The caller of this function | |
| * must release the interface pointer when it is done. | |
| */ | |
| newObject->ref = 1; | |
| newObject->hSelf = hNewObject; | |
| /* | |
| * The Ole clipboard is a singleton - save the global handle and pointer | |
| */ | |
| theOleClipboard = newObject; | |
| hTheOleClipboard = hNewObject; | |
| return theOleClipboard; | |
| } | |
| static void OLEClipbrd_Destroy(OLEClipbrd* ptrToDestroy) | |
| { | |
| TRACE("()\n"); | |
| if ( !ptrToDestroy ) | |
| return; | |
| /* | |
| * Destroy the Ole clipboard window | |
| */ | |
| if ( ptrToDestroy->hWndClipboard ) | |
| OLEClipbrd_DestroyWindow(ptrToDestroy->hWndClipboard); | |
| /* | |
| * Free the actual OLE Clipboard structure. | |
| */ | |
| TRACE("() - Destroying clipboard data object.\n"); | |
| GlobalUnlock(ptrToDestroy->hSelf); | |
| GlobalFree(ptrToDestroy->hSelf); | |
| /* | |
| * The Ole clipboard is a singleton (ptrToDestroy == theOleClipboard) | |
| */ | |
| theOleClipboard = NULL; | |
| hTheOleClipboard = 0; | |
| } | |
| /*********************************************************************** | |
| * OLEClipbrd_CreateWindow() | |
| * Create the clipboard window | |
| */ | |
| static HWND OLEClipbrd_CreateWindow() | |
| { | |
| HWND hwnd = 0; | |
| WNDCLASSEXA wcex; | |
| /* | |
| * Register the clipboard window class if necessary | |
| */ | |
| ZeroMemory( &wcex, sizeof(WNDCLASSEXA)); | |
| wcex.cbSize = sizeof(WNDCLASSEXA); | |
| /* Windows creates this class with a style mask of 0 | |
| * We dont bother doing this since the FindClassByAtom code | |
| * would have to be changed to deal with this idiosyncracy. */ | |
| wcex.style = CS_GLOBALCLASS; | |
| wcex.lpfnWndProc = (WNDPROC)OLEClipbrd_WndProc; | |
| wcex.hInstance = 0; | |
| wcex.lpszClassName = OLEClipbrd_WNDCLASS; | |
| RegisterClassExA(&wcex); | |
| /* | |
| * Create a hidden window to receive OLE clipboard messages | |
| */ | |
| /* | |
| * If we need to store state info we can store it here. | |
| * For now we dont need this functionality. | |
| * ClipboardWindowInfo clipboardInfo; | |
| * ZeroMemory( &trackerInfo, sizeof(ClipboardWindowInfo)); | |
| */ | |
| hwnd = CreateWindowA(OLEClipbrd_WNDCLASS, | |
| "ClipboardWindow", | |
| WS_POPUP | WS_CLIPSIBLINGS | WS_OVERLAPPED, | |
| CW_USEDEFAULT, CW_USEDEFAULT, | |
| CW_USEDEFAULT, CW_USEDEFAULT, | |
| 0, | |
| 0, | |
| 0, | |
| 0 /*(LPVOID)&clipboardInfo */); | |
| return hwnd; | |
| } | |
| /*********************************************************************** | |
| * OLEClipbrd_DestroyWindow(HWND) | |
| * Destroy the clipboard window and unregister its class | |
| */ | |
| static void OLEClipbrd_DestroyWindow(HWND hwnd) | |
| { | |
| /* | |
| * Destroy clipboard window and unregister its WNDCLASS | |
| */ | |
| DestroyWindow(hwnd); | |
| UnregisterClassA( OLEClipbrd_WNDCLASS, 0 ); | |
| } | |
| /*********************************************************************** | |
| * OLEClipbrd_WndProc(HWND, unsigned, WORD, LONG) | |
| * Processes messages sent to the OLE clipboard window. | |
| * Note that we will intercept messages in our WndProc only when data | |
| * has been placed in the clipboard via OleSetClipboard(). | |
| * i.e. Only when OLE owns the windows clipboard. | |
| */ | |
| LRESULT CALLBACK OLEClipbrd_WndProc | |
| (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| { | |
| switch (message) | |
| { | |
| /* | |
| * WM_RENDERFORMAT | |
| * We receive this message to allow us to handle delayed rendering of | |
| * a specific clipboard format when an application requests data in | |
| * that format by calling GetClipboardData. | |
| * (Recall that in OleSetClipboard, we used SetClipboardData to | |
| * make all HGLOBAL formats supported by the source IDataObject | |
| * available using delayed rendering) | |
| * On receiving this mesage we must actually render the data in the | |
| * specified format and place it on the clipboard by calling the | |
| * SetClipboardData function. | |
| */ | |
| case WM_RENDERFORMAT: | |
| { | |
| FORMATETC rgelt; | |
| ZeroMemory( &rgelt, sizeof(FORMATETC)); | |
| /* | |
| * Initialize FORMATETC to a Windows clipboard friendly format | |
| */ | |
| rgelt.cfFormat = (UINT) wParam; | |
| rgelt.dwAspect = DVASPECT_CONTENT; | |
| rgelt.lindex = -1; | |
| rgelt.tymed = TYMED_HGLOBAL; | |
| TRACE("(): WM_RENDERFORMAT(cfFormat=%d)\n", rgelt.cfFormat); | |
| /* | |
| * Render the clipboard data. | |
| * (We must have a source data object or we wouldn't be in this WndProc) | |
| */ | |
| OLEClipbrd_RenderFormat( (IDataObject*)&(theOleClipboard->lpvtbl1), &rgelt ); | |
| break; | |
| } | |
| /* | |
| * WM_RENDERALLFORMATS | |
| * Sent before the clipboard owner window is destroyed. | |
| * We should receive this message only when OleUninitialize is called | |
| * while we have an IDataObject in the clipboard. | |
| * For the content of the clipboard to remain available to other | |
| * applications, we must render data in all the formats the source IDataObject | |
| * is capable of generating, and place the data on the clipboard by calling | |
| * SetClipboardData. | |
| */ | |
| case WM_RENDERALLFORMATS: | |
| { | |
| IEnumFORMATETC* penumFormatetc = NULL; | |
| FORMATETC rgelt; | |
| TRACE("(): WM_RENDERALLFORMATS\n"); | |
| /* | |
| * Render all HGLOBAL formats supported by the source into | |
| * the windows clipboard. | |
| */ | |
| if ( FAILED( IDataObject_EnumFormatEtc( (IDataObject*)&(theOleClipboard->lpvtbl1), | |
| DATADIR_GET, &penumFormatetc) ) ) | |
| { | |
| WARN("(): WM_RENDERALLFORMATS failed to retrieve EnumFormatEtc!\n"); | |
| return 0; | |
| } | |
| while ( S_OK == IEnumFORMATETC_Next(penumFormatetc, 1, &rgelt, NULL) ) | |
| { | |
| if ( rgelt.tymed == TYMED_HGLOBAL ) | |
| { | |
| /* | |
| * Render the clipboard data. | |
| */ | |
| if ( FAILED(OLEClipbrd_RenderFormat( (IDataObject*)&(theOleClipboard->lpvtbl1), &rgelt )) ) | |
| continue; | |
| TRACE("(): WM_RENDERALLFORMATS(cfFormat=%d)\n", rgelt.cfFormat); | |
| } | |
| } | |
| IEnumFORMATETC_Release(penumFormatetc); | |
| break; | |
| } | |
| /* | |
| * WM_DESTROYCLIPBOARD | |
| * This is sent by EmptyClipboard before the clipboard is emptied. | |
| * We should release any IDataObject we are holding onto when we receive | |
| * this message, since it indicates that the OLE clipboard should be empty | |
| * from this point on. | |
| */ | |
| case WM_DESTROYCLIPBOARD: | |
| { | |
| TRACE("(): WM_DESTROYCLIPBOARD\n"); | |
| /* | |
| * Release the data object we are holding on to | |
| */ | |
| if ( theOleClipboard->pIDataObjectSrc ) | |
| { | |
| IDataObject_Release(theOleClipboard->pIDataObjectSrc); | |
| theOleClipboard->pIDataObjectSrc = NULL; | |
| } | |
| break; | |
| } | |
| /* | |
| case WM_ASKCBFORMATNAME: | |
| case WM_CHANGECBCHAIN: | |
| case WM_DRAWCLIPBOARD: | |
| case WM_SIZECLIPBOARD: | |
| case WM_HSCROLLCLIPBOARD: | |
| case WM_VSCROLLCLIPBOARD: | |
| case WM_PAINTCLIPBOARD: | |
| */ | |
| default: | |
| return DefWindowProcA(hWnd, message, wParam, lParam); | |
| } | |
| return 0; | |
| } | |
| #define MAX_CLIPFORMAT_NAME 80 | |
| /*********************************************************************** | |
| * OLEClipbrd_RenderFormat(LPFORMATETC) | |
| * Render the clipboard data. Note that this call will delegate to the | |
| * source data object. | |
| * Note: This function assumes it is passed an HGLOBAL format to render. | |
| */ | |
| static HRESULT OLEClipbrd_RenderFormat(IDataObject *pIDataObject, LPFORMATETC pFormatetc) | |
| { | |
| STGMEDIUM std; | |
| HGLOBAL hDup; | |
| HRESULT hr = S_OK; | |
| char szFmtName[MAX_CLIPFORMAT_NAME]; | |
| ILockBytes *ptrILockBytes = 0; | |
| HGLOBAL hStorage = 0; | |
| GetClipboardFormatNameA(pFormatetc->cfFormat, szFmtName, MAX_CLIPFORMAT_NAME); | |
| /* If embed source */ | |
| if (!strcmp(szFmtName, CF_EMBEDSOURCE)) | |
| { | |
| memset(&std, 0, sizeof(STGMEDIUM)); | |
| std.tymed = pFormatetc->tymed = TYMED_ISTORAGE; | |
| hStorage = GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, 0); | |
| hr = CreateILockBytesOnHGlobal(hStorage, FALSE, &ptrILockBytes); | |
| hr = StgCreateDocfileOnILockBytes(ptrILockBytes, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &std.u.pstg); | |
| if (FAILED(hr = IDataObject_GetDataHere(theOleClipboard->pIDataObjectSrc, pFormatetc, &std))) | |
| { | |
| WARN("() : IDataObject_GetDataHere failed to render clipboard data! (%lx)\n", hr); | |
| return hr; | |
| } | |
| if (1) /* check whether the presentation data is already -not- present */ | |
| { | |
| FORMATETC fmt2; | |
| STGMEDIUM std2; | |
| METAFILEPICT *mfp = 0; | |
| fmt2.cfFormat = CF_METAFILEPICT; | |
| fmt2.ptd = 0; | |
| fmt2.dwAspect = DVASPECT_CONTENT; | |
| fmt2.lindex = -1; | |
| fmt2.tymed = TYMED_MFPICT; | |
| memset(&std2, 0, sizeof(STGMEDIUM)); | |
| std2.tymed = TYMED_MFPICT; | |
| /* Get the metafile picture out of it */ | |
| if (!FAILED(hr = IDataObject_GetData(theOleClipboard->pIDataObjectSrc, &fmt2, &std2))) | |
| { | |
| mfp = (METAFILEPICT *)GlobalLock(std2.u.hGlobal); | |
| } | |
| if (mfp) | |
| { | |
| OLECHAR name[]={ 2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0}; | |
| IStream *pStream = 0; | |
| void *mfBits; | |
| PresentationDataHeader pdh; | |
| INT nSize; | |
| CLSID clsID; | |
| LPOLESTR strProgID; | |
| CHAR strOleTypeName[51]; | |
| BYTE OlePresStreamHeader [] = | |
| { | |
| 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, | |
| 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, | |
| 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, | |
| 0x00, 0x00, 0x00, 0x00 | |
| }; | |
| nSize = GetMetaFileBitsEx(mfp->hMF, 0, NULL); | |
| memset(&pdh, 0, sizeof(PresentationDataHeader)); | |
| memcpy(&pdh, OlePresStreamHeader, sizeof(OlePresStreamHeader)); | |
| pdh.dwObjectExtentX = mfp->xExt; | |
| pdh.dwObjectExtentY = mfp->yExt; | |
| pdh.dwSize = nSize; | |
| hr = IStorage_CreateStream(std.u.pstg, name, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &pStream); | |
| hr = IStream_Write(pStream, &pdh, sizeof(PresentationDataHeader), NULL); | |
| mfBits = HeapAlloc(GetProcessHeap(), 0, nSize); | |
| nSize = GetMetaFileBitsEx(mfp->hMF, nSize, mfBits); | |
| hr = IStream_Write(pStream, mfBits, nSize, NULL); | |
| IStream_Release(pStream); | |
| HeapFree(GetProcessHeap(), 0, mfBits); | |
| GlobalUnlock(std2.u.hGlobal); | |
| ReadClassStg(std.u.pstg, &clsID); | |
| ProgIDFromCLSID(&clsID, &strProgID); | |
| WideCharToMultiByte( CP_ACP, 0, strProgID, -1, strOleTypeName, sizeof(strOleTypeName), NULL, NULL ); | |
| OLECONVERT_CreateOleStream(std.u.pstg); | |
| OLECONVERT_CreateCompObjStream(std.u.pstg, strOleTypeName); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| if (FAILED(hr = IDataObject_GetData(pIDataObject, pFormatetc, &std))) | |
| { | |
| WARN("() : IDataObject_GetData failed to render clipboard data! (%lx)\n", hr); | |
| return hr; | |
| } | |
| /* To put a copy back on the clipboard */ | |
| hStorage = std.u.hGlobal; | |
| } | |
| /* | |
| * Put a copy of the rendered data back on the clipboard | |
| */ | |
| if ( !(hDup = OLEClipbrd_GlobalDupMem(hStorage)) ) | |
| HANDLE_ERROR( E_OUTOFMEMORY ); | |
| if ( !SetClipboardData( pFormatetc->cfFormat, hDup ) ) | |
| { | |
| GlobalFree(hDup); | |
| WARN("() : Failed to set rendered clipboard data into clipboard!\n"); | |
| } | |
| CLEANUP: | |
| ReleaseStgMedium(&std); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * OLEClipbrd_GlobalDupMem( HGLOBAL ) | |
| * Helper method to duplicate an HGLOBAL chunk of memory | |
| */ | |
| static HGLOBAL OLEClipbrd_GlobalDupMem( HGLOBAL hGlobalSrc ) | |
| { | |
| HGLOBAL hGlobalDest; | |
| PVOID pGlobalSrc, pGlobalDest; | |
| DWORD cBytes; | |
| if ( !hGlobalSrc ) | |
| return 0; | |
| cBytes = GlobalSize(hGlobalSrc); | |
| if ( 0 == cBytes ) | |
| return 0; | |
| hGlobalDest = GlobalAlloc( GMEM_DDESHARE|GMEM_MOVEABLE, | |
| cBytes ); | |
| if ( !hGlobalDest ) | |
| return 0; | |
| pGlobalSrc = GlobalLock(hGlobalSrc); | |
| pGlobalDest = GlobalLock(hGlobalDest); | |
| if ( !pGlobalSrc || !pGlobalDest ) | |
| return 0; | |
| memcpy(pGlobalDest, pGlobalSrc, cBytes); | |
| GlobalUnlock(hGlobalSrc); | |
| GlobalUnlock(hGlobalDest); | |
| return hGlobalDest; | |
| } | |
| /*---------------------------------------------------------------------* | |
| * Implementation of the internal IDataObject interface exposed by | |
| * the OLE clipboard. | |
| *---------------------------------------------------------------------*/ | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_QueryInterface (IUnknown) | |
| * | |
| * See Windows documentation for more details on IUnknown methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_QueryInterface( | |
| IDataObject* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObject); | |
| /* | |
| * Perform a sanity check on the parameters. | |
| */ | |
| if ( (This==0) || (ppvObject==0) ) | |
| return E_INVALIDARG; | |
| /* | |
| * Initialize the return parameter. | |
| */ | |
| *ppvObject = 0; | |
| /* | |
| * Compare the riid with the interface IDs implemented by this object. | |
| */ | |
| if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) | |
| { | |
| *ppvObject = iface; | |
| } | |
| else if (memcmp(&IID_IDataObject, riid, sizeof(IID_IDataObject)) == 0) | |
| { | |
| *ppvObject = (IDataObject*)&(This->lpvtbl1); | |
| } | |
| else /* We only support IUnknown and IDataObject */ | |
| { | |
| WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| /* | |
| * Query Interface always increases the reference count by one when it is | |
| * successful. | |
| */ | |
| IUnknown_AddRef((IUnknown*)*ppvObject); | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_AddRef (IUnknown) | |
| * | |
| * See Windows documentation for more details on IUnknown methods. | |
| */ | |
| static ULONG WINAPI OLEClipbrd_IDataObject_AddRef( | |
| IDataObject* iface) | |
| { | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); | |
| This->ref++; | |
| return This->ref; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_Release (IUnknown) | |
| * | |
| * See Windows documentation for more details on IUnknown methods. | |
| */ | |
| static ULONG WINAPI OLEClipbrd_IDataObject_Release( | |
| IDataObject* iface) | |
| { | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); | |
| /* | |
| * Decrease the reference count on this object. | |
| */ | |
| This->ref--; | |
| /* | |
| * If the reference count goes down to 0, perform suicide. | |
| */ | |
| if (This->ref==0) | |
| { | |
| OLEClipbrd_Destroy(This); | |
| } | |
| return This->ref; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_GetData (IDataObject) | |
| * | |
| * The OLE Clipboard's implementation of this method delegates to | |
| * a data source if there is one or wraps around the windows clipboard | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetcIn, | |
| STGMEDIUM* pmedium) | |
| { | |
| HANDLE hData = 0; | |
| BOOL bClipboardOpen = FALSE; | |
| HRESULT hr = S_OK; | |
| LPVOID src; | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p,%p,%p)\n", iface, pformatetcIn, pmedium); | |
| if ( !pformatetcIn || !pmedium ) | |
| return E_INVALIDARG; | |
| /* | |
| * If we have a data source placed on the clipboard (via OleSetClipboard) | |
| * simply delegate to the source object's QueryGetData | |
| * NOTE: This code assumes that the IDataObject is in the same address space! | |
| * We will need to add marshalling support when Wine handles multiple processes. | |
| */ | |
| if ( This->pIDataObjectSrc ) | |
| { | |
| return IDataObject_GetData(This->pIDataObjectSrc, pformatetcIn, pmedium); | |
| } | |
| if ( pformatetcIn->lindex != -1 ) | |
| return DV_E_LINDEX; | |
| if ( (pformatetcIn->tymed & TYMED_HGLOBAL) != TYMED_HGLOBAL ) | |
| return DV_E_TYMED; | |
| /* | |
| if ( pformatetcIn->dwAspect != DVASPECT_CONTENT ) | |
| return DV_E_DVASPECT; | |
| */ | |
| /* | |
| * Otherwise, get the data from the windows clipboard using GetClipboardData | |
| */ | |
| if ( !(bClipboardOpen = OpenClipboard(theOleClipboard->hWndClipboard)) ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_OPEN ); | |
| hData = GetClipboardData(pformatetcIn->cfFormat); | |
| /* Must make a copy of global handle returned by GetClipboardData; it | |
| * is not valid after we call CloseClipboard | |
| * Application is responsible for freeing the memory (Forte Agent does this) | |
| */ | |
| src = GlobalLock(hData); | |
| if(src) { | |
| LPVOID dest; | |
| ULONG size; | |
| HANDLE hDest; | |
| size = GlobalSize(hData); | |
| hDest = GlobalAlloc(GHND, size); | |
| dest = GlobalLock(hDest); | |
| memcpy(dest, src, size); | |
| GlobalUnlock(hDest); | |
| GlobalUnlock(hData); | |
| hData = hDest; | |
| } | |
| /* | |
| * Return the clipboard data in the storage medium structure | |
| */ | |
| pmedium->tymed = (hData == 0) ? TYMED_NULL : TYMED_HGLOBAL; | |
| pmedium->u.hGlobal = (HGLOBAL)hData; | |
| pmedium->pUnkForRelease = NULL; | |
| hr = S_OK; | |
| CLEANUP: | |
| /* | |
| * Close Windows clipboard | |
| */ | |
| if ( bClipboardOpen && !CloseClipboard() ) | |
| hr = CLIPBRD_E_CANT_CLOSE; | |
| if ( FAILED(hr) ) | |
| return hr; | |
| return (hData == 0) ? DV_E_FORMATETC : S_OK; | |
| } | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetDataHere( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium) | |
| { | |
| FIXME(": Stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_QueryGetData (IDataObject) | |
| * | |
| * The OLE Clipboard's implementation of this method delegates to | |
| * a data source if there is one or wraps around the windows clipboard | |
| * function IsClipboardFormatAvailable() otherwise. | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_QueryGetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc) | |
| { | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p, %p)\n", iface, pformatetc); | |
| /* | |
| * If we have a data source placed on the clipboard (via OleSetClipboard) | |
| * simply delegate to the source object's QueryGetData | |
| */ | |
| if ( This->pIDataObjectSrc ) | |
| { | |
| return IDataObject_QueryGetData(This->pIDataObjectSrc, pformatetc); | |
| } | |
| if (!pformatetc) | |
| return E_INVALIDARG; | |
| /* | |
| if ( pformatetc->dwAspect != DVASPECT_CONTENT ) | |
| return DV_E_DVASPECT; | |
| */ | |
| if ( pformatetc->lindex != -1 ) | |
| return DV_E_LINDEX; | |
| /* TODO: Handle TYMED_IStorage media which were put on the clipboard | |
| * by copying the storage into global memory. We must convert this | |
| * TYMED_HGLOBAL back to TYMED_IStorage. | |
| */ | |
| if ( pformatetc->tymed != TYMED_HGLOBAL ) | |
| return DV_E_TYMED; | |
| /* | |
| * Delegate to the Windows clipboard function IsClipboardFormatAvailable | |
| */ | |
| return (IsClipboardFormatAvailable(pformatetc->cfFormat)) ? S_OK : DV_E_FORMATETC; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_GetCanonicalFormatEtc (IDataObject) | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_GetCanonicalFormatEtc( | |
| IDataObject* iface, | |
| LPFORMATETC pformatectIn, | |
| LPFORMATETC pformatetcOut) | |
| { | |
| TRACE("(%p, %p, %p)\n", iface, pformatectIn, pformatetcOut); | |
| if ( !pformatectIn || !pformatetcOut ) | |
| return E_INVALIDARG; | |
| memcpy(pformatetcOut, pformatectIn, sizeof(FORMATETC)); | |
| return DATA_S_SAMEFORMATETC; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_SetData (IDataObject) | |
| * | |
| * The OLE Clipboard's does not implement this method | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_SetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium, | |
| BOOL fRelease) | |
| { | |
| TRACE("\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_EnumFormatEtc (IDataObject) | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_EnumFormatEtc( | |
| IDataObject* iface, | |
| DWORD dwDirection, | |
| IEnumFORMATETC** ppenumFormatEtc) | |
| { | |
| HRESULT hr = S_OK; | |
| FORMATETC *afmt = NULL; | |
| int cfmt, i; | |
| UINT format; | |
| BOOL bClipboardOpen; | |
| /* | |
| * Declare "This" pointer | |
| */ | |
| ICOM_THIS(OLEClipbrd, iface); | |
| TRACE("(%p, %lx, %p)\n", iface, dwDirection, ppenumFormatEtc); | |
| /* | |
| * If we have a data source placed on the clipboard (via OleSetClipboard) | |
| * simply delegate to the source object's EnumFormatEtc | |
| */ | |
| if ( This->pIDataObjectSrc ) | |
| { | |
| return IDataObject_EnumFormatEtc(This->pIDataObjectSrc, | |
| dwDirection, ppenumFormatEtc); | |
| } | |
| /* | |
| * Otherwise we must provide our own enumerator which wraps around the | |
| * Windows clipboard function EnumClipboardFormats | |
| */ | |
| if ( !ppenumFormatEtc ) | |
| return E_INVALIDARG; | |
| if ( dwDirection != DATADIR_GET ) /* IDataObject_SetData not implemented */ | |
| return E_NOTIMPL; | |
| /* | |
| * Store all current clipboard formats in an array of FORMATETC's, | |
| * and create an IEnumFORMATETC enumerator from this list. | |
| */ | |
| cfmt = CountClipboardFormats(); | |
| afmt = (FORMATETC *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(FORMATETC) * cfmt); | |
| /* | |
| * Open the Windows clipboard, associating it with our hidden window | |
| */ | |
| if ( !(bClipboardOpen = OpenClipboard(This->hWndClipboard)) ) | |
| HANDLE_ERROR( CLIPBRD_E_CANT_OPEN ); | |
| /* | |
| * Store all current clipboard formats in an array of FORMATETC's | |
| * TODO: Handle TYMED_IStorage media which were put on the clipboard | |
| * by copying the storage into global memory. We must convert this | |
| * TYMED_HGLOBAL back to TYMED_IStorage. | |
| */ | |
| for (i = 0, format = 0; i < cfmt; i++) | |
| { | |
| format = EnumClipboardFormats(format); | |
| if (!format) /* Failed! */ | |
| { | |
| ERR("EnumClipboardFormats failed to return format!\n"); | |
| HANDLE_ERROR( E_FAIL ); | |
| } | |
| /* Init the FORMATETC struct */ | |
| afmt[i].cfFormat = format; | |
| afmt[i].ptd = NULL; | |
| afmt[i].dwAspect = DVASPECT_CONTENT; | |
| afmt[i].lindex = -1; | |
| afmt[i].tymed = TYMED_HGLOBAL; | |
| } | |
| /* | |
| * Create an EnumFORMATETC enumerator and return an | |
| * EnumFORMATETC after bumping up its ref count | |
| */ | |
| *ppenumFormatEtc = OLEClipbrd_IEnumFORMATETC_Construct( cfmt, afmt, (LPUNKNOWN)iface); | |
| if (!(*ppenumFormatEtc)) | |
| HANDLE_ERROR( E_OUTOFMEMORY ); | |
| if (FAILED( hr = IEnumFORMATETC_AddRef(*ppenumFormatEtc))) | |
| HANDLE_ERROR( hr ); | |
| hr = S_OK; | |
| CLEANUP: | |
| /* | |
| * Free the array of FORMATETC's | |
| */ | |
| if (afmt) | |
| HeapFree(GetProcessHeap(), 0, afmt); | |
| /* | |
| * Close Windows clipboard | |
| */ | |
| if ( bClipboardOpen && !CloseClipboard() ) | |
| hr = CLIPBRD_E_CANT_CLOSE; | |
| return hr; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_DAdvise (IDataObject) | |
| * | |
| * The OLE Clipboard's does not implement this method | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_DAdvise( | |
| IDataObject* iface, | |
| FORMATETC* pformatetc, | |
| DWORD advf, | |
| IAdviseSink* pAdvSink, | |
| DWORD* pdwConnection) | |
| { | |
| TRACE("\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_DUnadvise (IDataObject) | |
| * | |
| * The OLE Clipboard's does not implement this method | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_DUnadvise( | |
| IDataObject* iface, | |
| DWORD dwConnection) | |
| { | |
| TRACE("\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IDataObject_EnumDAdvise (IDataObject) | |
| * | |
| * The OLE Clipboard does not implement this method | |
| * | |
| * See Windows documentation for more details on IDataObject methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IDataObject_EnumDAdvise( | |
| IDataObject* iface, | |
| IEnumSTATDATA** ppenumAdvise) | |
| { | |
| TRACE("\n"); | |
| return E_NOTIMPL; | |
| } | |
| /*---------------------------------------------------------------------* | |
| * Implementation of the internal IEnumFORMATETC interface returned by | |
| * the OLE clipboard's IDataObject. | |
| *---------------------------------------------------------------------*/ | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Construct (UINT, const FORMATETC, LPUNKNOWN) | |
| * | |
| * Creates an IEnumFORMATETC enumerator from an array of FORMATETC | |
| * Structures. pUnkOuter is the outer unknown for reference counting only. | |
| * NOTE: this does not AddRef the interface. | |
| */ | |
| LPENUMFORMATETC OLEClipbrd_IEnumFORMATETC_Construct(UINT cfmt, const FORMATETC afmt[], | |
| LPUNKNOWN pUnkDataObj) | |
| { | |
| IEnumFORMATETCImpl* ef; | |
| DWORD size=cfmt * sizeof(FORMATETC); | |
| LPMALLOC pIMalloc; | |
| ef = (IEnumFORMATETCImpl*)HeapAlloc(GetProcessHeap(), | |
| HEAP_ZERO_MEMORY, | |
| sizeof(IEnumFORMATETCImpl)); | |
| if (!ef) | |
| return NULL; | |
| ef->ref = 0; | |
| ICOM_VTBL(ef) = &efvt; | |
| ef->pUnkDataObj = pUnkDataObj; | |
| ef->posFmt = 0; | |
| ef->countFmt = cfmt; | |
| if (FAILED(CoGetMalloc(MEMCTX_TASK, &pIMalloc))) | |
| return NULL; | |
| ef->pFmt = (LPFORMATETC)IMalloc_Alloc(pIMalloc, size); | |
| IMalloc_Release(pIMalloc); | |
| if (ef->pFmt) | |
| memcpy(ef->pFmt, afmt, size); | |
| TRACE("(%p)->()\n",ef); | |
| return (LPENUMFORMATETC)ef; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_QueryInterface (IUnknown) | |
| * | |
| * See Windows documentation for more details on IUnknown methods. | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_QueryInterface | |
| (LPENUMFORMATETC iface, REFIID riid, LPVOID* ppvObj) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj); | |
| /* | |
| * Since enumerators are separate objects from the parent data object | |
| * we only need to support the IUnknown and IEnumFORMATETC interfaces | |
| */ | |
| *ppvObj = NULL; | |
| if(IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *ppvObj = This; | |
| } | |
| else if(IsEqualIID(riid, &IID_IEnumFORMATETC)) | |
| { | |
| *ppvObj = (IDataObject*)This; | |
| } | |
| if(*ppvObj) | |
| { | |
| IEnumFORMATETC_AddRef((IEnumFORMATETC*)*ppvObj); | |
| TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); | |
| return S_OK; | |
| } | |
| TRACE("-- Interface: E_NOINTERFACE\n"); | |
| return E_NOINTERFACE; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_AddRef (IUnknown) | |
| * | |
| * Since enumerating formats only makes sense when our data object is around, | |
| * we insure that it stays as long as we stay by calling our parents IUnknown | |
| * for AddRef and Release. But since we are not controlled by the lifetime of | |
| * the outer object, we still keep our own reference count in order to | |
| * free ourselves. | |
| */ | |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_AddRef(LPENUMFORMATETC iface) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); | |
| if (This->pUnkDataObj) | |
| IUnknown_AddRef(This->pUnkDataObj); | |
| return ++(This->ref); | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Release (IUnknown) | |
| * | |
| * See Windows documentation for more details on IUnknown methods. | |
| */ | |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_Release(LPENUMFORMATETC iface) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| LPMALLOC pIMalloc; | |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); | |
| if (This->pUnkDataObj) | |
| IUnknown_Release(This->pUnkDataObj); /* Release parent data object */ | |
| if (!--(This->ref)) | |
| { | |
| TRACE("() - destroying IEnumFORMATETC(%p)\n",This); | |
| if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pIMalloc))) | |
| { | |
| IMalloc_Free(pIMalloc, This->pFmt); | |
| IMalloc_Release(pIMalloc); | |
| } | |
| HeapFree(GetProcessHeap(),0,This); | |
| return 0; | |
| } | |
| return This->ref; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Next (IEnumFORMATETC) | |
| * | |
| * Standard enumerator members for IEnumFORMATETC | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Next | |
| (LPENUMFORMATETC iface, ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| UINT cfetch; | |
| HRESULT hres = S_FALSE; | |
| TRACE("(%p)->(pos=%u)\n", This, This->posFmt); | |
| if (This->posFmt < This->countFmt) | |
| { | |
| cfetch = This->countFmt - This->posFmt; | |
| if (cfetch >= celt) | |
| { | |
| cfetch = celt; | |
| hres = S_OK; | |
| } | |
| memcpy(rgelt, &This->pFmt[This->posFmt], cfetch * sizeof(FORMATETC)); | |
| This->posFmt += cfetch; | |
| } | |
| else | |
| { | |
| cfetch = 0; | |
| } | |
| if (pceltFethed) | |
| { | |
| *pceltFethed = cfetch; | |
| } | |
| return hres; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Skip (IEnumFORMATETC) | |
| * | |
| * Standard enumerator members for IEnumFORMATETC | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Skip(LPENUMFORMATETC iface, ULONG celt) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| TRACE("(%p)->(num=%lu)\n", This, celt); | |
| This->posFmt += celt; | |
| if (This->posFmt > This->countFmt) | |
| { | |
| This->posFmt = This->countFmt; | |
| return S_FALSE; | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Reset (IEnumFORMATETC) | |
| * | |
| * Standard enumerator members for IEnumFORMATETC | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Reset(LPENUMFORMATETC iface) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| TRACE("(%p)->()\n", This); | |
| This->posFmt = 0; | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * OLEClipbrd_IEnumFORMATETC_Clone (IEnumFORMATETC) | |
| * | |
| * Standard enumerator members for IEnumFORMATETC | |
| */ | |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Clone | |
| (LPENUMFORMATETC iface, LPENUMFORMATETC* ppenum) | |
| { | |
| ICOM_THIS(IEnumFORMATETCImpl,iface); | |
| HRESULT hr = S_OK; | |
| TRACE("(%p)->(ppenum=%p)\n", This, ppenum); | |
| if ( !ppenum ) | |
| return E_INVALIDARG; | |
| *ppenum = OLEClipbrd_IEnumFORMATETC_Construct(This->countFmt, | |
| This->pFmt, | |
| This->pUnkDataObj); | |
| if (FAILED( hr = IEnumFORMATETC_AddRef(*ppenum))) | |
| return ( hr ); | |
| return (*ppenum) ? S_OK : E_OUTOFMEMORY; | |
| } | |