| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 <stdarg.h> |
| #include <string.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| |
| #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 |
| */ |
| const IDataObjectVtbl* 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 ourself |
| */ |
| HGLOBAL hSelf; |
| |
| /* |
| * Reference count of this object |
| */ |
| LONG ref; |
| }; |
| |
| typedef struct OLEClipbrd OLEClipbrd; |
| |
| |
| /**************************************************************************** |
| * IEnumFORMATETC implementation |
| * DO NOT add any members before the VTables declaration! |
| */ |
| typedef struct |
| { |
| /* IEnumFORMATETC VTable */ |
| const IEnumFORMATETCVtbl *lpVtbl; |
| |
| /* 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 |
| */ |
| LONG 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. |
| */ |
| void OLEClipbrd_Initialize(void); |
| void OLEClipbrd_UnInitialize(void); |
| static OLEClipbrd* OLEClipbrd_Construct(void); |
| static void OLEClipbrd_Destroy(OLEClipbrd* ptrToDestroy); |
| static HWND OLEClipbrd_CreateWindow(void); |
| 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 const IDataObjectVtbl OLEClipbrd_IDataObject_VTable = |
| { |
| 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 const IEnumFORMATETCVtbl efvt = |
| { |
| 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 don't need this functionality. |
| * |
| typedef struct tagClipboardWindowInfo |
| { |
| } ClipboardWindowInfo; |
| */ |
| |
| /*---------------------------------------------------------------------* |
| * Win32 OLE clipboard API |
| *---------------------------------------------------------------------*/ |
| |
| /*********************************************************************** |
| * OleSetClipboard [OLE32.@] |
| * 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, 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.@] |
| * 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 [OLE32.@] |
| * 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.@] |
| */ |
| 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(void) |
| { |
| /* |
| * Create the clipboard if necessary |
| */ |
| if ( !theOleClipboard ) |
| { |
| TRACE("()\n"); |
| theOleClipboard = OLEClipbrd_Construct(); |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * OLEClipbrd_UnInitialize() |
| * Un-Initializes the OLE clipboard |
| */ |
| void OLEClipbrd_UnInitialize(void) |
| { |
| 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 don't bother doing this since the FindClassByAtom code |
| * would have to be changed to deal with this idiosyncrasy. */ |
| wcex.style = CS_GLOBALCLASS; |
| wcex.lpfnWndProc = 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 don't 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 message 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); |
| if (hStorage == NULL) |
| HANDLE_ERROR( E_OUTOFMEMORY ); |
| 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); |
| GlobalFree(hStorage); |
| 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); |
| GlobalFree(hStorage); |
| 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 ) |
| { |
| GlobalFree(hGlobalDest); |
| 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 |
| */ |
| OLEClipbrd *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 |
| */ |
| OLEClipbrd *This = (OLEClipbrd *)iface; |
| |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); |
| |
| return InterlockedIncrement(&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 |
| */ |
| OLEClipbrd *This = (OLEClipbrd *)iface; |
| ULONG ref; |
| |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); |
| |
| /* |
| * Decrease the reference count on this object. |
| */ |
| ref = InterlockedDecrement(&This->ref); |
| |
| /* |
| * If the reference count goes down to 0, perform suicide. |
| */ |
| if (ref == 0) |
| { |
| OLEClipbrd_Destroy(This); |
| } |
| |
| return 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 |
| */ |
| OLEClipbrd *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 |
| */ |
| OLEClipbrd *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 |
| */ |
| OLEClipbrd *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 = 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 |
| */ |
| 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumFORMATETCImpl)); |
| if (!ef) |
| return NULL; |
| |
| ef->ref = 0; |
| ef->lpVtbl = &efvt; |
| ef->pUnkDataObj = pUnkDataObj; |
| |
| ef->posFmt = 0; |
| ef->countFmt = cfmt; |
| if (FAILED(CoGetMalloc(MEMCTX_TASK, &pIMalloc))) { |
| HeapFree(GetProcessHeap(), 0, ef); |
| 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) |
| { |
| IEnumFORMATETCImpl *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) |
| { |
| IEnumFORMATETCImpl *This = (IEnumFORMATETCImpl *)iface; |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); |
| |
| if (This->pUnkDataObj) |
| IUnknown_AddRef(This->pUnkDataObj); |
| |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| /************************************************************************ |
| * OLEClipbrd_IEnumFORMATETC_Release (IUnknown) |
| * |
| * See Windows documentation for more details on IUnknown methods. |
| */ |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_Release(LPENUMFORMATETC iface) |
| { |
| IEnumFORMATETCImpl *This = (IEnumFORMATETCImpl *)iface; |
| LPMALLOC pIMalloc; |
| ULONG ref; |
| |
| TRACE("(%p)->(count=%lu)\n",This, This->ref); |
| |
| if (This->pUnkDataObj) |
| IUnknown_Release(This->pUnkDataObj); /* Release parent data object */ |
| |
| ref = InterlockedDecrement(&This->ref); |
| if (!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 ref; |
| } |
| |
| /************************************************************************ |
| * OLEClipbrd_IEnumFORMATETC_Next (IEnumFORMATETC) |
| * |
| * Standard enumerator members for IEnumFORMATETC |
| */ |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Next |
| (LPENUMFORMATETC iface, ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed) |
| { |
| IEnumFORMATETCImpl *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) |
| { |
| IEnumFORMATETCImpl *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) |
| { |
| IEnumFORMATETCImpl *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) |
| { |
| IEnumFORMATETCImpl *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; |
| } |