| /* |
| * 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> |
| #include <stdio.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| |
| #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" |
| |
| #include "compobj_private.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); |
| |
| /* Structure of 'Ole Private Data' clipboard format */ |
| typedef struct |
| { |
| FORMATETC fmtetc; |
| DWORD first_use; /* Has this cf been added to the list already */ |
| DWORD unk[2]; |
| } ole_priv_data_entry; |
| |
| typedef struct |
| { |
| DWORD unk1; |
| DWORD size; /* in bytes of the entire structure */ |
| DWORD unk2; |
| DWORD count; /* no. of format entries */ |
| DWORD unk3[2]; |
| ole_priv_data_entry entries[1]; /* array of size count */ |
| /* then follows any DVTARGETDEVICE structures referenced in the FORMATETCs */ |
| } ole_priv_data; |
| |
| /***************************************************************************** |
| * td_offs_to_ptr |
| * |
| * Returns a ptr to a target device at a given offset from the |
| * start of the ole_priv_data. |
| * |
| * Used when unpacking ole private data from the clipboard. |
| */ |
| static inline DVTARGETDEVICE *td_offs_to_ptr(ole_priv_data *data, DWORD_PTR off) |
| { |
| if(off == 0) return NULL; |
| return (DVTARGETDEVICE*)((char*)data + off); |
| } |
| |
| /***************************************************************************** |
| * td_get_offs |
| * |
| * Get the offset from the start of the ole_priv_data of the idx'th |
| * target device. |
| * |
| * Used when packing ole private data to the clipboard. |
| */ |
| static inline DWORD_PTR td_get_offs(ole_priv_data *data, DWORD idx) |
| { |
| if(data->entries[idx].fmtetc.ptd == NULL) return 0; |
| return (char*)data->entries[idx].fmtetc.ptd - (char*)data; |
| } |
| |
| /**************************************************************************** |
| * Consumer snapshot. Represents the state of the ole clipboard |
| * returned by OleGetClipboard(). |
| */ |
| typedef struct snapshot |
| { |
| IDataObject IDataObject_iface; |
| LONG ref; |
| |
| DWORD seq_no; /* Clipboard sequence number corresponding to this snapshot */ |
| |
| IDataObject *data; /* If we unmarshal a remote data object we hold a ref here */ |
| } snapshot; |
| |
| /**************************************************************************** |
| * ole_clipbrd |
| */ |
| typedef struct ole_clipbrd |
| { |
| snapshot *latest_snapshot; /* Latest consumer snapshot */ |
| |
| HWND window; /* Hidden clipboard window */ |
| IDataObject *src_data; /* Source object passed to OleSetClipboard */ |
| ole_priv_data *cached_enum; /* Cached result from the enumeration of src data object */ |
| IStream *marshal_data; /* Stream onto which to marshal src_data */ |
| } ole_clipbrd; |
| |
| static inline snapshot *impl_from_IDataObject(IDataObject *iface) |
| { |
| return CONTAINING_RECORD(iface, snapshot, IDataObject_iface); |
| } |
| |
| typedef struct PresentationDataHeader |
| { |
| BYTE unknown1[28]; |
| DWORD dwObjectExtentX; |
| DWORD dwObjectExtentY; |
| DWORD dwSize; |
| } PresentationDataHeader; |
| |
| /* |
| * The one and only ole_clipbrd object which is created by OLEClipbrd_Initialize() |
| */ |
| static ole_clipbrd* theOleClipboard; |
| |
| static CRITICAL_SECTION latest_snapshot_cs; |
| static CRITICAL_SECTION_DEBUG latest_snapshot_cs_debug = |
| { |
| 0, 0, &latest_snapshot_cs, |
| { &latest_snapshot_cs_debug.ProcessLocksList, &latest_snapshot_cs_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": clipboard last snapshot") } |
| }; |
| static CRITICAL_SECTION latest_snapshot_cs = { &latest_snapshot_cs_debug, -1, 0, 0, 0, 0 }; |
| |
| static inline HRESULT get_ole_clipbrd(ole_clipbrd **clipbrd) |
| { |
| struct oletls *info = COM_CurrentInfo(); |
| *clipbrd = NULL; |
| |
| if(!info->ole_inits) |
| return CO_E_NOTINITIALIZED; |
| *clipbrd = theOleClipboard; |
| |
| return S_OK; |
| } |
| |
| /* |
| * Name of our registered OLE clipboard window class |
| */ |
| static const WCHAR clipbrd_wndclass[] = {'C','L','I','P','B','R','D','W','N','D','C','L','A','S','S',0}; |
| |
| UINT ownerlink_clipboard_format = 0; |
| UINT filename_clipboard_format = 0; |
| UINT filenameW_clipboard_format = 0; |
| UINT dataobject_clipboard_format = 0; |
| UINT embedded_object_clipboard_format = 0; |
| UINT embed_source_clipboard_format = 0; |
| UINT custom_link_source_clipboard_format = 0; |
| UINT link_source_clipboard_format = 0; |
| UINT object_descriptor_clipboard_format = 0; |
| UINT link_source_descriptor_clipboard_format = 0; |
| UINT ole_private_data_clipboard_format = 0; |
| |
| static UINT wine_marshal_clipboard_format; |
| |
| static inline const char *dump_fmtetc(FORMATETC *fmt) |
| { |
| if (!fmt) return "(null)"; |
| return wine_dbg_sprintf("cf %04x ptd %p aspect %x lindex %d tymed %x", |
| fmt->cfFormat, fmt->ptd, fmt->dwAspect, fmt->lindex, fmt->tymed); |
| } |
| |
| /*---------------------------------------------------------------------* |
| * Implementation of the internal IEnumFORMATETC interface returned by |
| * the OLE clipboard's IDataObject. |
| *---------------------------------------------------------------------*/ |
| |
| typedef struct enum_fmtetc |
| { |
| IEnumFORMATETC IEnumFORMATETC_iface; |
| LONG ref; |
| |
| UINT pos; /* current enumerator position */ |
| ole_priv_data *data; |
| } enum_fmtetc; |
| |
| static inline enum_fmtetc *impl_from_IEnumFORMATETC(IEnumFORMATETC *iface) |
| { |
| return CONTAINING_RECORD(iface, enum_fmtetc, IEnumFORMATETC_iface); |
| } |
| |
| /************************************************************************ |
| * 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) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| |
| TRACE("(%p)->(IID: %s, %p)\n", This, debugstr_guid(riid), ppvObj); |
| |
| *ppvObj = NULL; |
| |
| if(IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IEnumFORMATETC)) |
| { |
| *ppvObj = iface; |
| } |
| |
| 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) |
| * |
| */ |
| static ULONG WINAPI OLEClipbrd_IEnumFORMATETC_AddRef(LPENUMFORMATETC iface) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| TRACE("(%p)->(count=%u)\n",This, This->ref); |
| |
| 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) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| ULONG ref; |
| |
| TRACE("(%p)->(count=%u)\n",This, This->ref); |
| |
| ref = InterlockedDecrement(&This->ref); |
| if (!ref) |
| { |
| TRACE("() - destroying IEnumFORMATETC(%p)\n",This); |
| HeapFree(GetProcessHeap(), 0, This->data); |
| 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) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| UINT cfetch, i; |
| HRESULT hres = S_FALSE; |
| |
| TRACE("(%p)->(pos=%u)\n", This, This->pos); |
| |
| if (This->pos < This->data->count) |
| { |
| cfetch = This->data->count - This->pos; |
| if (cfetch >= celt) |
| { |
| cfetch = celt; |
| hres = S_OK; |
| } |
| |
| for(i = 0; i < cfetch; i++) |
| { |
| hres = copy_formatetc(rgelt + i, &This->data->entries[This->pos++].fmtetc); |
| if(FAILED(hres)) return hres; |
| } |
| } |
| 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) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| TRACE("(%p)->(num=%u)\n", This, celt); |
| |
| This->pos += celt; |
| if (This->pos > This->data->count) |
| { |
| This->pos = This->data->count; |
| return S_FALSE; |
| } |
| return S_OK; |
| } |
| |
| /************************************************************************ |
| * OLEClipbrd_IEnumFORMATETC_Reset (IEnumFORMATETC) |
| * |
| * Standard enumerator members for IEnumFORMATETC |
| */ |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Reset(LPENUMFORMATETC iface) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| TRACE("(%p)->()\n", This); |
| |
| This->pos = 0; |
| return S_OK; |
| } |
| |
| static HRESULT enum_fmtetc_construct(ole_priv_data *data, UINT pos, IEnumFORMATETC **obj); |
| |
| /************************************************************************ |
| * OLEClipbrd_IEnumFORMATETC_Clone (IEnumFORMATETC) |
| * |
| * Standard enumerator members for IEnumFORMATETC |
| */ |
| static HRESULT WINAPI OLEClipbrd_IEnumFORMATETC_Clone |
| (LPENUMFORMATETC iface, LPENUMFORMATETC* obj) |
| { |
| enum_fmtetc *This = impl_from_IEnumFORMATETC(iface); |
| ole_priv_data *new_data; |
| DWORD i; |
| |
| TRACE("(%p)->(%p)\n", This, obj); |
| |
| if ( !obj ) return E_INVALIDARG; |
| *obj = NULL; |
| |
| new_data = HeapAlloc(GetProcessHeap(), 0, This->data->size); |
| if(!new_data) return E_OUTOFMEMORY; |
| memcpy(new_data, This->data, This->data->size); |
| |
| /* Fixup any target device ptrs */ |
| for(i = 0; i < This->data->count; i++) |
| new_data->entries[i].fmtetc.ptd = |
| td_offs_to_ptr(new_data, td_get_offs(This->data, i)); |
| |
| return enum_fmtetc_construct(new_data, This->pos, obj); |
| } |
| |
| 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 |
| }; |
| |
| /************************************************************************ |
| * enum_fmtetc_construct |
| * |
| * Creates an IEnumFORMATETC enumerator from ole_priv_data which it then owns. |
| */ |
| static HRESULT enum_fmtetc_construct(ole_priv_data *data, UINT pos, IEnumFORMATETC **obj) |
| { |
| enum_fmtetc* ef; |
| |
| *obj = NULL; |
| ef = HeapAlloc(GetProcessHeap(), 0, sizeof(*ef)); |
| if (!ef) return E_OUTOFMEMORY; |
| |
| ef->ref = 1; |
| ef->IEnumFORMATETC_iface.lpVtbl = &efvt; |
| ef->data = data; |
| ef->pos = pos; |
| |
| TRACE("(%p)->()\n", ef); |
| *obj = &ef->IEnumFORMATETC_iface; |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * dup_global_mem |
| * |
| * Helper method to duplicate an HGLOBAL chunk of memory |
| */ |
| static HRESULT dup_global_mem( HGLOBAL src, DWORD flags, HGLOBAL *dst ) |
| { |
| void *src_ptr, *dst_ptr; |
| DWORD size; |
| |
| *dst = NULL; |
| if ( !src ) return S_FALSE; |
| |
| size = GlobalSize(src); |
| |
| *dst = GlobalAlloc( flags, size ); |
| if ( !*dst ) return E_OUTOFMEMORY; |
| |
| src_ptr = GlobalLock(src); |
| dst_ptr = GlobalLock(*dst); |
| |
| memcpy(dst_ptr, src_ptr, size); |
| |
| GlobalUnlock(*dst); |
| GlobalUnlock(src); |
| |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * dup_metafilepict |
| * |
| * Helper function to duplicate a handle to a METAFILEPICT, and the |
| * contained HMETAFILE. |
| */ |
| static HRESULT dup_metafilepict(HGLOBAL src, HGLOBAL *pdest) |
| { |
| HRESULT hr; |
| HGLOBAL dest; |
| METAFILEPICT *dest_ptr; |
| |
| *pdest = NULL; |
| |
| /* Copy the METAFILEPICT structure. */ |
| hr = dup_global_mem(src, GMEM_DDESHARE|GMEM_MOVEABLE, &dest); |
| if (FAILED(hr)) return hr; |
| |
| dest_ptr = GlobalLock(dest); |
| if (!dest_ptr) return E_FAIL; |
| |
| /* Give the new METAFILEPICT a separate HMETAFILE. */ |
| dest_ptr->hMF = CopyMetaFileW(dest_ptr->hMF, NULL); |
| if (dest_ptr->hMF) |
| { |
| GlobalUnlock(dest); |
| *pdest = dest; |
| return S_OK; |
| } |
| else |
| { |
| GlobalUnlock(dest); |
| GlobalFree(dest); |
| return E_FAIL; |
| } |
| } |
| |
| /*********************************************************************** |
| * free_metafilepict |
| * |
| * Helper function to GlobalFree a handle to a METAFILEPICT, and also |
| * free the contained HMETAFILE. |
| */ |
| static void free_metafilepict(HGLOBAL src) |
| { |
| METAFILEPICT *src_ptr; |
| |
| src_ptr = GlobalLock(src); |
| if (src_ptr) |
| { |
| DeleteMetaFile(src_ptr->hMF); |
| GlobalUnlock(src); |
| } |
| GlobalFree(src); |
| } |
| |
| /*********************************************************************** |
| * dup_bitmap |
| * |
| * Helper function to duplicate an HBITMAP. |
| */ |
| static HRESULT dup_bitmap(HBITMAP src, HBITMAP *pdest) |
| { |
| HDC src_dc; |
| HGDIOBJ orig_src_bitmap; |
| BITMAP bm; |
| HBITMAP dest; |
| |
| src_dc = CreateCompatibleDC(NULL); |
| orig_src_bitmap = SelectObject(src_dc, src); |
| GetObjectW(src, sizeof bm, &bm); |
| dest = CreateCompatibleBitmap(src_dc, bm.bmWidth, bm.bmHeight); |
| if (dest) |
| { |
| HDC dest_dc = CreateCompatibleDC(NULL); |
| HGDIOBJ orig_dest_bitmap = SelectObject(dest_dc, dest); |
| BitBlt(dest_dc, 0, 0, bm.bmWidth, bm.bmHeight, src_dc, 0, 0, SRCCOPY); |
| SelectObject(dest_dc, orig_dest_bitmap); |
| DeleteDC(dest_dc); |
| } |
| SelectObject(src_dc, orig_src_bitmap); |
| DeleteDC(src_dc); |
| *pdest = dest; |
| return dest ? S_OK : E_FAIL; |
| } |
| |
| /************************************************************ |
| * render_embed_source_hack |
| * |
| * This is clearly a hack and has no place in the clipboard code. |
| * |
| */ |
| static HRESULT render_embed_source_hack(IDataObject *data, LPFORMATETC fmt) |
| { |
| STGMEDIUM std; |
| HGLOBAL hStorage = 0; |
| HRESULT hr = S_OK; |
| ILockBytes *ptrILockBytes; |
| |
| memset(&std, 0, sizeof(STGMEDIUM)); |
| std.tymed = fmt->tymed = TYMED_ISTORAGE; |
| |
| hStorage = GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, 0); |
| if (hStorage == NULL) return E_OUTOFMEMORY; |
| hr = CreateILockBytesOnHGlobal(hStorage, FALSE, &ptrILockBytes); |
| if (FAILED(hr)) |
| { |
| GlobalFree(hStorage); |
| return hr; |
| } |
| |
| hr = StgCreateDocfileOnILockBytes(ptrILockBytes, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &std.u.pstg); |
| ILockBytes_Release(ptrILockBytes); |
| |
| if (FAILED(hr = IDataObject_GetDataHere(theOleClipboard->src_data, fmt, &std))) |
| { |
| WARN("() : IDataObject_GetDataHere failed to render clipboard data! (%x)\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 (SUCCEEDED(hr = IDataObject_GetData(theOleClipboard->src_data, &fmt2, &std2))) |
| { |
| mfp = 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); |
| ReleaseStgMedium(&std2); |
| |
| ReadClassStg(std.u.pstg, &clsID); |
| ProgIDFromCLSID(&clsID, &strProgID); |
| |
| WideCharToMultiByte( CP_ACP, 0, strProgID, -1, strOleTypeName, sizeof(strOleTypeName), NULL, NULL ); |
| STORAGE_CreateOleStream(std.u.pstg, 0); |
| OLECONVERT_CreateCompObjStream(std.u.pstg, strOleTypeName); |
| CoTaskMemFree(strProgID); |
| } |
| } |
| |
| if ( !SetClipboardData( fmt->cfFormat, hStorage ) ) |
| { |
| WARN("() : Failed to set rendered clipboard data into clipboard!\n"); |
| GlobalFree(hStorage); |
| hr = CLIPBRD_E_CANT_SET; |
| } |
| |
| ReleaseStgMedium(&std); |
| return hr; |
| } |
| |
| /************************************************************************ |
| * find_format_in_list |
| * |
| * Returns the first entry that matches the provided clipboard format. |
| */ |
| static inline ole_priv_data_entry *find_format_in_list(ole_priv_data_entry *entries, DWORD num, UINT cf) |
| { |
| DWORD i; |
| for(i = 0; i < num; i++) |
| if(entries[i].fmtetc.cfFormat == cf) |
| return &entries[i]; |
| |
| return NULL; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_storage |
| * |
| * Returns storage data in an HGLOBAL. |
| */ |
| static HRESULT get_data_from_storage(IDataObject *data, FORMATETC *fmt, HGLOBAL *mem) |
| { |
| HGLOBAL h; |
| IStorage *stg; |
| HRESULT hr; |
| FORMATETC stg_fmt; |
| STGMEDIUM med; |
| ILockBytes *lbs; |
| |
| *mem = NULL; |
| |
| h = GlobalAlloc( GMEM_DDESHARE|GMEM_MOVEABLE, 0 ); |
| if(!h) return E_OUTOFMEMORY; |
| |
| hr = CreateILockBytesOnHGlobal(h, FALSE, &lbs); |
| if(SUCCEEDED(hr)) |
| { |
| hr = StgCreateDocfileOnILockBytes(lbs, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stg); |
| ILockBytes_Release(lbs); |
| } |
| if(FAILED(hr)) |
| { |
| GlobalFree(h); |
| return hr; |
| } |
| |
| stg_fmt = *fmt; |
| med.tymed = stg_fmt.tymed = TYMED_ISTORAGE; |
| med.u.pstg = stg; |
| med.pUnkForRelease = NULL; |
| |
| hr = IDataObject_GetDataHere(data, &stg_fmt, &med); |
| if(FAILED(hr)) |
| { |
| memset(&med, 0, sizeof(med)); |
| hr = IDataObject_GetData(data, &stg_fmt, &med); |
| if(FAILED(hr)) goto end; |
| |
| hr = IStorage_CopyTo(med.u.pstg, 0, NULL, NULL, stg); |
| ReleaseStgMedium(&med); |
| if(FAILED(hr)) goto end; |
| } |
| *mem = h; |
| |
| end: |
| IStorage_Release(stg); |
| if(FAILED(hr)) GlobalFree(h); |
| return hr; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_stream |
| * |
| * Returns stream data in an HGLOBAL. |
| */ |
| static HRESULT get_data_from_stream(IDataObject *data, FORMATETC *fmt, HGLOBAL *mem) |
| { |
| HGLOBAL h; |
| IStream *stm = NULL; |
| HRESULT hr; |
| FORMATETC stm_fmt; |
| STGMEDIUM med; |
| |
| *mem = NULL; |
| |
| h = GlobalAlloc( GMEM_DDESHARE|GMEM_MOVEABLE, 0 ); |
| if(!h) return E_OUTOFMEMORY; |
| |
| hr = CreateStreamOnHGlobal(h, FALSE, &stm); |
| if(FAILED(hr)) goto error; |
| |
| stm_fmt = *fmt; |
| med.tymed = stm_fmt.tymed = TYMED_ISTREAM; |
| med.u.pstm = stm; |
| med.pUnkForRelease = NULL; |
| |
| hr = IDataObject_GetDataHere(data, &stm_fmt, &med); |
| if(FAILED(hr)) |
| { |
| LARGE_INTEGER offs; |
| ULARGE_INTEGER pos; |
| |
| memset(&med, 0, sizeof(med)); |
| hr = IDataObject_GetData(data, &stm_fmt, &med); |
| if(FAILED(hr)) goto error; |
| |
| offs.QuadPart = 0; |
| IStream_Seek(med.u.pstm, offs, STREAM_SEEK_CUR, &pos); |
| IStream_Seek(med.u.pstm, offs, STREAM_SEEK_SET, NULL); |
| hr = IStream_CopyTo(med.u.pstm, stm, pos, NULL, NULL); |
| ReleaseStgMedium(&med); |
| if(FAILED(hr)) goto error; |
| } |
| *mem = h; |
| IStream_Release(stm); |
| return S_OK; |
| |
| error: |
| if(stm) IStream_Release(stm); |
| GlobalFree(h); |
| return hr; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_global |
| * |
| * Returns global data in an HGLOBAL. |
| */ |
| static HRESULT get_data_from_global(IDataObject *data, FORMATETC *fmt, HGLOBAL *mem) |
| { |
| HGLOBAL h; |
| HRESULT hr; |
| FORMATETC mem_fmt; |
| STGMEDIUM med; |
| |
| *mem = NULL; |
| |
| mem_fmt = *fmt; |
| mem_fmt.tymed = TYMED_HGLOBAL; |
| memset(&med, 0, sizeof(med)); |
| |
| hr = IDataObject_GetData(data, &mem_fmt, &med); |
| if(FAILED(hr)) return hr; |
| |
| hr = dup_global_mem(med.u.hGlobal, GMEM_DDESHARE|GMEM_MOVEABLE, &h); |
| |
| if(SUCCEEDED(hr)) *mem = h; |
| |
| ReleaseStgMedium(&med); |
| |
| return hr; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_enhmetafile |
| */ |
| static HRESULT get_data_from_enhmetafile(IDataObject *data, FORMATETC *fmt, HGLOBAL *mem) |
| { |
| HENHMETAFILE copy; |
| HRESULT hr; |
| FORMATETC mem_fmt; |
| STGMEDIUM med; |
| |
| *mem = NULL; |
| |
| mem_fmt = *fmt; |
| mem_fmt.tymed = TYMED_ENHMF; |
| memset(&med, 0, sizeof(med)); |
| |
| hr = IDataObject_GetData(data, &mem_fmt, &med); |
| if(FAILED(hr)) return hr; |
| |
| copy = CopyEnhMetaFileW(med.u.hEnhMetaFile, NULL); |
| if(copy) *mem = (HGLOBAL)copy; |
| else hr = E_FAIL; |
| |
| ReleaseStgMedium(&med); |
| |
| return hr; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_metafilepict |
| */ |
| static HRESULT get_data_from_metafilepict(IDataObject *data, FORMATETC *fmt, HGLOBAL *mem) |
| { |
| HGLOBAL copy; |
| HRESULT hr; |
| FORMATETC mem_fmt; |
| STGMEDIUM med; |
| |
| *mem = NULL; |
| |
| mem_fmt = *fmt; |
| mem_fmt.tymed = TYMED_MFPICT; |
| memset(&med, 0, sizeof(med)); |
| |
| hr = IDataObject_GetData(data, &mem_fmt, &med); |
| if(FAILED(hr)) return hr; |
| |
| hr = dup_metafilepict(med.u.hMetaFilePict, ©); |
| |
| if(SUCCEEDED(hr)) *mem = copy; |
| |
| ReleaseStgMedium(&med); |
| |
| return hr; |
| } |
| |
| /*************************************************************************** |
| * get_data_from_bitmap |
| * |
| * Returns bitmap in an HBITMAP. |
| */ |
| static HRESULT get_data_from_bitmap(IDataObject *data, FORMATETC *fmt, HBITMAP *hbm) |
| { |
| HBITMAP copy; |
| HRESULT hr; |
| FORMATETC mem_fmt; |
| STGMEDIUM med; |
| |
| *hbm = NULL; |
| |
| mem_fmt = *fmt; |
| mem_fmt.tymed = TYMED_GDI; |
| memset(&med, 0, sizeof(med)); |
| |
| hr = IDataObject_GetData(data, &mem_fmt, &med); |
| if(FAILED(hr)) return hr; |
| |
| hr = dup_bitmap(med.u.hBitmap, ©); |
| |
| if(SUCCEEDED(hr)) *hbm = copy; |
| |
| ReleaseStgMedium(&med); |
| |
| return hr; |
| } |
| |
| /*********************************************************************** |
| * render_format |
| * |
| * Render the clipboard data. Note that this call will delegate to the |
| * source data object. |
| */ |
| static HRESULT render_format(IDataObject *data, LPFORMATETC fmt) |
| { |
| HANDLE clip_data = NULL; /* HGLOBAL unless otherwise specified */ |
| HRESULT hr; |
| |
| /* Embed source hack */ |
| if(fmt->cfFormat == embed_source_clipboard_format) |
| { |
| return render_embed_source_hack(data, fmt); |
| } |
| |
| if(fmt->tymed & TYMED_ISTORAGE) |
| { |
| hr = get_data_from_storage(data, fmt, &clip_data); |
| } |
| else if(fmt->tymed & TYMED_ISTREAM) |
| { |
| hr = get_data_from_stream(data, fmt, &clip_data); |
| } |
| else if(fmt->tymed & TYMED_HGLOBAL) |
| { |
| hr = get_data_from_global(data, fmt, &clip_data); |
| } |
| else if(fmt->tymed & TYMED_ENHMF) |
| { |
| hr = get_data_from_enhmetafile(data, fmt, &clip_data); |
| } |
| else if(fmt->tymed & TYMED_MFPICT) |
| { |
| /* Returns global handle to METAFILEPICT, containing a copied HMETAFILE */ |
| hr = get_data_from_metafilepict(data, fmt, &clip_data); |
| } |
| else if(fmt->tymed & TYMED_GDI) |
| { |
| /* Returns HBITMAP not HGLOBAL */ |
| hr = get_data_from_bitmap(data, fmt, (HBITMAP *)&clip_data); |
| } |
| else |
| { |
| FIXME("Unhandled tymed %x\n", fmt->tymed); |
| hr = DV_E_FORMATETC; |
| } |
| |
| if(SUCCEEDED(hr)) |
| { |
| if ( !SetClipboardData(fmt->cfFormat, clip_data) ) |
| { |
| WARN("() : Failed to set rendered clipboard data into clipboard!\n"); |
| if(fmt->tymed & TYMED_MFPICT) |
| free_metafilepict(clip_data); |
| else if(fmt->tymed & TYMED_GDI) |
| DeleteObject(clip_data); |
| else |
| GlobalFree(clip_data); |
| hr = CLIPBRD_E_CANT_SET; |
| } |
| } |
| |
| return hr; |
| } |
| |
| /*---------------------------------------------------------------------* |
| * Implementation of the internal IDataObject interface exposed by |
| * the OLE clipboard. |
| *---------------------------------------------------------------------*/ |
| |
| |
| /************************************************************************ |
| * snapshot_QueryInterface |
| */ |
| static HRESULT WINAPI snapshot_QueryInterface(IDataObject *iface, |
| REFIID riid, void **ppvObject) |
| { |
| snapshot *This = impl_from_IDataObject(iface); |
| TRACE("(%p)->(IID:%s, %p)\n", This, debugstr_guid(riid), ppvObject); |
| |
| if ( (This==0) || (ppvObject==0) ) |
| return E_INVALIDARG; |
| |
| *ppvObject = 0; |
| |
| if (IsEqualIID(&IID_IUnknown, riid) || |
| IsEqualIID(&IID_IDataObject, riid)) |
| { |
| *ppvObject = iface; |
| } |
| else |
| { |
| WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppvObject); |
| |
| return S_OK; |
| } |
| |
| /************************************************************************ |
| * snapshot_AddRef |
| */ |
| static ULONG WINAPI snapshot_AddRef(IDataObject *iface) |
| { |
| snapshot *This = impl_from_IDataObject(iface); |
| |
| TRACE("(%p)->(count=%u)\n", This, This->ref); |
| |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| /************************************************************************ |
| * snapshot_Release |
| */ |
| static ULONG WINAPI snapshot_Release(IDataObject *iface) |
| { |
| snapshot *This = impl_from_IDataObject(iface); |
| ULONG ref; |
| |
| TRACE("(%p)->(count=%u)\n", This, This->ref); |
| |
| ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) |
| { |
| EnterCriticalSection(&latest_snapshot_cs); |
| if (This->ref) |
| { |
| LeaveCriticalSection(&latest_snapshot_cs); |
| return ref; |
| } |
| if (theOleClipboard->latest_snapshot == This) |
| theOleClipboard->latest_snapshot = NULL; |
| LeaveCriticalSection(&latest_snapshot_cs); |
| |
| if(This->data) IDataObject_Release(This->data); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| /************************************************************ |
| * get_current_ole_clip_window |
| * |
| * Return the window that owns the ole clipboard. |
| * |
| * If the clipboard is flushed or not owned by ole this will |
| * return NULL. |
| */ |
| static HWND get_current_ole_clip_window(void) |
| { |
| HGLOBAL h; |
| HWND *ptr, wnd; |
| |
| h = GetClipboardData(dataobject_clipboard_format); |
| if(!h) return NULL; |
| ptr = GlobalLock(h); |
| if(!ptr) return NULL; |
| wnd = *ptr; |
| GlobalUnlock(h); |
| return wnd; |
| } |
| |
| /************************************************************ |
| * get_current_dataobject |
| * |
| * Return an unmarshalled IDataObject if there is a current |
| * (ie non-flushed) object on the ole clipboard. |
| */ |
| static HRESULT get_current_dataobject(IDataObject **data) |
| { |
| HRESULT hr = S_FALSE; |
| HWND wnd = get_current_ole_clip_window(); |
| HGLOBAL h; |
| void *ptr; |
| IStream *stm; |
| LARGE_INTEGER pos; |
| |
| *data = NULL; |
| if(!wnd) return S_FALSE; |
| |
| h = GetClipboardData(wine_marshal_clipboard_format); |
| if(!h) return S_FALSE; |
| if(GlobalSize(h) <= 1) return S_FALSE; |
| ptr = GlobalLock(h); |
| if(!ptr) return S_FALSE; |
| |
| hr = CreateStreamOnHGlobal(NULL, TRUE, &stm); |
| if(FAILED(hr)) goto end; |
| |
| hr = IStream_Write(stm, ptr, GlobalSize(h), NULL); |
| if(SUCCEEDED(hr)) |
| { |
| pos.QuadPart = 0; |
| IStream_Seek(stm, pos, STREAM_SEEK_SET, NULL); |
| hr = CoUnmarshalInterface(stm, &IID_IDataObject, (void**)data); |
| } |
| IStream_Release(stm); |
| |
| end: |
| GlobalUnlock(h); |
| return hr; |
| } |
| |
| static DWORD get_tymed_from_nonole_cf(UINT cf) |
| { |
| if(cf >= 0xc000) return TYMED_ISTREAM | TYMED_HGLOBAL; |
| |
| switch(cf) |
| { |
| case CF_TEXT: |
| case CF_OEMTEXT: |
| case CF_UNICODETEXT: |
| return TYMED_ISTREAM | TYMED_HGLOBAL; |
| case CF_ENHMETAFILE: |
| return TYMED_ENHMF; |
| case CF_METAFILEPICT: |
| return TYMED_MFPICT; |
| case CF_BITMAP: |
| return TYMED_GDI; |
| default: |
| FIXME("returning TYMED_NULL for cf %04x\n", cf); |
| return TYMED_NULL; |
| } |
| } |
| |
| /*********************************************************** |
| * get_priv_data |
| * |
| * Returns a copy of the Ole Private Data |
| */ |
| static HRESULT get_priv_data(ole_priv_data **data) |
| { |
| HGLOBAL handle; |
| HRESULT hr = S_OK; |
| ole_priv_data *ret = NULL; |
| |
| *data = NULL; |
| |
| handle = GetClipboardData( ole_private_data_clipboard_format ); |
| if(handle) |
| { |
| ole_priv_data *src = GlobalLock(handle); |
| if(src) |
| { |
| DWORD i; |
| |
| /* FIXME: sanity check on size */ |
| ret = HeapAlloc(GetProcessHeap(), 0, src->size); |
| if(!ret) |
| { |
| GlobalUnlock(handle); |
| return E_OUTOFMEMORY; |
| } |
| memcpy(ret, src, src->size); |
| GlobalUnlock(handle); |
| |
| /* Fixup any target device offsets to ptrs */ |
| for(i = 0; i < ret->count; i++) |
| ret->entries[i].fmtetc.ptd = |
| td_offs_to_ptr(ret, (DWORD_PTR) ret->entries[i].fmtetc.ptd); |
| } |
| } |
| |
| if(!ret) /* Non-ole data */ |
| { |
| UINT cf; |
| DWORD count = 0, idx, size = FIELD_OFFSET(ole_priv_data, entries); |
| |
| for(cf = 0; (cf = EnumClipboardFormats(cf)) != 0; count++) |
| { |
| WCHAR buf[256]; |
| if (GetClipboardFormatNameW(cf, buf, sizeof(buf) / sizeof(WCHAR))) |
| TRACE("cf %04x %s\n", cf, debugstr_w(buf)); |
| else |
| TRACE("cf %04x\n", cf); |
| } |
| TRACE("count %d\n", count); |
| size += count * sizeof(ret->entries[0]); |
| |
| /* There are holes in fmtetc so zero init */ |
| ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); |
| if(!ret) return E_OUTOFMEMORY; |
| ret->size = size; |
| ret->count = count; |
| |
| for(cf = 0, idx = 0; (cf = EnumClipboardFormats(cf)) != 0; idx++) |
| { |
| ret->entries[idx].fmtetc.cfFormat = cf; |
| ret->entries[idx].fmtetc.ptd = NULL; |
| ret->entries[idx].fmtetc.dwAspect = DVASPECT_CONTENT; |
| ret->entries[idx].fmtetc.lindex = -1; |
| ret->entries[idx].fmtetc.tymed = get_tymed_from_nonole_cf(cf); |
| ret->entries[idx].first_use = 1; |
| } |
| } |
| |
| *data = ret; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * get_stgmed_for_global |
| * |
| * Returns a stg medium with a copy of the global handle |
| */ |
| static HRESULT get_stgmed_for_global(HGLOBAL h, STGMEDIUM *med) |
| { |
| HRESULT hr; |
| |
| med->pUnkForRelease = NULL; |
| med->tymed = TYMED_NULL; |
| |
| hr = dup_global_mem(h, GMEM_MOVEABLE, &med->u.hGlobal); |
| |
| if(SUCCEEDED(hr)) med->tymed = TYMED_HGLOBAL; |
| |
| return hr; |
| } |
| |
| /************************************************************************ |
| * get_stgmed_for_stream |
| * |
| * Returns a stg medium with a stream based on the handle |
| */ |
| static HRESULT get_stgmed_for_stream(HGLOBAL h, STGMEDIUM *med) |
| { |
| HRESULT hr; |
| HGLOBAL dst; |
| |
| med->pUnkForRelease = NULL; |
| med->tymed = TYMED_NULL; |
| |
| hr = dup_global_mem(h, GMEM_MOVEABLE, &dst); |
| if(FAILED(hr)) return hr; |
| |
| hr = CreateStreamOnHGlobal(dst, TRUE, &med->u.pstm); |
| if(FAILED(hr)) |
| { |
| GlobalFree(dst); |
| return hr; |
| } |
| |
| med->tymed = TYMED_ISTREAM; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * get_stgmed_for_storage |
| * |
| * Returns a stg medium with a storage based on the handle |
| */ |
| static HRESULT get_stgmed_for_storage(HGLOBAL h, STGMEDIUM *med) |
| { |
| HRESULT hr; |
| HGLOBAL dst; |
| ILockBytes *lbs; |
| |
| med->pUnkForRelease = NULL; |
| med->tymed = TYMED_NULL; |
| |
| hr = dup_global_mem(h, GMEM_MOVEABLE, &dst); |
| if(FAILED(hr)) return hr; |
| |
| hr = CreateILockBytesOnHGlobal(dst, TRUE, &lbs); |
| if(FAILED(hr)) |
| { |
| GlobalFree(dst); |
| return hr; |
| } |
| |
| hr = StgOpenStorageOnILockBytes(lbs, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, NULL, 0, &med->u.pstg); |
| ILockBytes_Release(lbs); |
| if(FAILED(hr)) |
| { |
| GlobalFree(dst); |
| return hr; |
| } |
| |
| med->tymed = TYMED_ISTORAGE; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * get_stgmed_for_emf |
| * |
| * Returns a stg medium with an enhanced metafile based on the handle |
| */ |
| static HRESULT get_stgmed_for_emf(HENHMETAFILE hemf, STGMEDIUM *med) |
| { |
| med->pUnkForRelease = NULL; |
| med->tymed = TYMED_NULL; |
| |
| med->u.hEnhMetaFile = CopyEnhMetaFileW(hemf, NULL); |
| if(!med->u.hEnhMetaFile) return E_OUTOFMEMORY; |
| med->tymed = TYMED_ENHMF; |
| return S_OK; |
| } |
| |
| /************************************************************************ |
| * get_stgmed_for_bitmap |
| * |
| * Returns a stg medium with a bitmap based on the handle |
| */ |
| static HRESULT get_stgmed_for_bitmap(HBITMAP hbmp, STGMEDIUM *med) |
| { |
| HRESULT hr; |
| |
| med->pUnkForRelease = NULL; |
| med->tymed = TYMED_NULL; |
| |
| hr = dup_bitmap(hbmp, &med->u.hBitmap); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| med->tymed = TYMED_GDI; |
| return S_OK; |
| } |
| |
| static inline BOOL string_off_equal(const DVTARGETDEVICE *t1, WORD off1, const DVTARGETDEVICE *t2, WORD off2) |
| { |
| const WCHAR *str1, *str2; |
| |
| if(off1 == 0 && off2 == 0) return TRUE; |
| if(off1 == 0 || off2 == 0) return FALSE; |
| |
| str1 = (const WCHAR*)((const char*)t1 + off1); |
| str2 = (const WCHAR*)((const char*)t2 + off2); |
| |
| return !lstrcmpW(str1, str2); |
| } |
| |
| static inline BOOL td_equal(const DVTARGETDEVICE *t1, const DVTARGETDEVICE *t2) |
| { |
| if(t1 == NULL && t2 == NULL) return TRUE; |
| if(t1 == NULL || t2 == NULL) return FALSE; |
| |
| if(!string_off_equal(t1, t1->tdDriverNameOffset, t2, t2->tdDriverNameOffset)) |
| return FALSE; |
| if(!string_off_equal(t1, t1->tdDeviceNameOffset, t2, t2->tdDeviceNameOffset)) |
| return FALSE; |
| if(!string_off_equal(t1, t1->tdPortNameOffset, t2, t2->tdPortNameOffset)) |
| return FALSE; |
| |
| /* FIXME check devmode? */ |
| |
| return TRUE; |
| } |
| |
| /************************************************************************ |
| * snapshot_GetData |
| */ |
| static HRESULT WINAPI snapshot_GetData(IDataObject *iface, FORMATETC *fmt, |
| STGMEDIUM *med) |
| { |
| snapshot *This = impl_from_IDataObject(iface); |
| HANDLE h; |
| HRESULT hr; |
| ole_priv_data *enum_data = NULL; |
| ole_priv_data_entry *entry; |
| DWORD mask; |
| |
| TRACE("(%p, %p {%s}, %p)\n", iface, fmt, dump_fmtetc(fmt), med); |
| |
| if ( !fmt || !med ) return E_INVALIDARG; |
| |
| memset(med, 0, sizeof(*med)); |
| |
| if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; |
| |
| if(!This->data) |
| hr = get_current_dataobject(&This->data); |
| |
| if(This->data) |
| { |
| hr = IDataObject_GetData(This->data, fmt, med); |
| CloseClipboard(); |
| return hr; |
| } |
| |
| h = GetClipboardData(fmt->cfFormat); |
| if(!h) |
| { |
| hr = DV_E_FORMATETC; |
| goto end; |
| } |
| |
| hr = get_priv_data(&enum_data); |
| if(FAILED(hr)) goto end; |
| |
| entry = find_format_in_list(enum_data->entries, enum_data->count, fmt->cfFormat); |
| if(entry) |
| { |
| if(!td_equal(fmt->ptd, entry->fmtetc.ptd)) |
| { |
| hr = DV_E_FORMATETC; |
| goto end; |
| } |
| mask = fmt->tymed & entry->fmtetc.tymed; |
| if(!mask) mask = fmt->tymed & (TYMED_ISTREAM | TYMED_HGLOBAL); |
| } |
| else /* non-Ole format */ |
| mask = fmt->tymed & TYMED_HGLOBAL; |
| |
| if(mask & TYMED_ISTORAGE) |
| hr = get_stgmed_for_storage(h, med); |
| else if(mask & TYMED_HGLOBAL) |
| hr = get_stgmed_for_global(h, med); |
| else if(mask & TYMED_ISTREAM) |
| hr = get_stgmed_for_stream(h, med); |
| else if(mask & TYMED_ENHMF) |
| hr = get_stgmed_for_emf((HENHMETAFILE)h, med); |
| else if(mask & TYMED_GDI) |
| hr = get_stgmed_for_bitmap((HBITMAP)h, med); |
| else |
| { |
| FIXME("Unhandled tymed - mask %x req tymed %x\n", mask, fmt->tymed); |
| hr = E_FAIL; |
| goto end; |
| } |
| |
| end: |
| HeapFree(GetProcessHeap(), 0, enum_data); |
| if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * snapshot_GetDataHere |
| */ |
| static HRESULT WINAPI snapshot_GetDataHere(IDataObject *iface, FORMATETC *fmt, |
| STGMEDIUM *med) |
| { |
| snapshot *This = impl_from_IDataObject(iface); |
| HANDLE h; |
| HRESULT hr; |
| ole_priv_data *enum_data = NULL; |
| ole_priv_data_entry *entry; |
| TYMED supported; |
| |
| if ( !fmt || !med ) return E_INVALIDARG; |
| |
| TRACE("(%p, %p {%s}, %p (tymed %x)\n", iface, fmt, dump_fmtetc(fmt), med, med->tymed); |
| |
| if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; |
| |
| if(!This->data) |
| hr = get_current_dataobject(&This->data); |
| |
| if(This->data) |
| { |
| hr = IDataObject_GetDataHere(This->data, fmt, med); |
| if(SUCCEEDED(hr)) |
| { |
| CloseClipboard(); |
| return hr; |
| } |
| } |
| |
| h = GetClipboardData(fmt->cfFormat); |
| if(!h) |
| { |
| hr = DV_E_FORMATETC; |
| goto end; |
| } |
| |
| hr = get_priv_data(&enum_data); |
| if(FAILED(hr)) goto end; |
| |
| entry = find_format_in_list(enum_data->entries, enum_data->count, fmt->cfFormat); |
| if(entry) |
| { |
| if(!td_equal(fmt->ptd, entry->fmtetc.ptd)) |
| { |
| hr = DV_E_FORMATETC; |
| goto end; |
| } |
| supported = entry->fmtetc.tymed; |
| } |
| else /* non-Ole format */ |
| supported = TYMED_HGLOBAL; |
| |
| switch(med->tymed) |
| { |
| case TYMED_HGLOBAL: |
| { |
| DWORD src_size = GlobalSize(h); |
| DWORD dst_size = GlobalSize(med->u.hGlobal); |
| hr = E_FAIL; |
| if(dst_size >= src_size) |
| { |
| void *src = GlobalLock(h); |
| void *dst = GlobalLock(med->u.hGlobal); |
| |
| memcpy(dst, src, src_size); |
| GlobalUnlock(med->u.hGlobal); |
| GlobalUnlock(h); |
| hr = S_OK; |
| } |
| break; |
| } |
| case TYMED_ISTREAM: |
| { |
| DWORD src_size = GlobalSize(h); |
| void *src = GlobalLock(h); |
| hr = IStream_Write(med->u.pstm, src, src_size, NULL); |
| GlobalUnlock(h); |
| break; |
| } |
| case TYMED_ISTORAGE: |
| { |
| STGMEDIUM copy; |
| if(!(supported & TYMED_ISTORAGE)) |
| { |
| hr = E_FAIL; |
| goto end; |
| } |
| hr = get_stgmed_for_storage(h, ©); |
| if(SUCCEEDED(hr)) |
| { |
| hr = IStorage_CopyTo(copy.u.pstg, 0, NULL, NULL, med->u.pstg); |
| ReleaseStgMedium(©); |
| } |
| break; |
| } |
| default: |
| FIXME("Unhandled tymed - supported %x req tymed %x\n", supported, med->tymed); |
| hr = E_FAIL; |
| goto end; |
| } |
| |
| end: |
| HeapFree(GetProcessHeap(), 0, enum_data); |
| if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * snapshot_QueryGetData |
| * |
| * 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. |
| * |
| */ |
| static HRESULT WINAPI snapshot_QueryGetData(IDataObject *iface, FORMATETC *fmt) |
| { |
| FIXME("(%p, %p {%s})\n", iface, fmt, dump_fmtetc(fmt)); |
| |
| if (!fmt) return E_INVALIDARG; |
| |
| if ( fmt->dwAspect != DVASPECT_CONTENT ) return DV_E_FORMATETC; |
| |
| if ( fmt->lindex != -1 ) return DV_E_FORMATETC; |
| |
| return (IsClipboardFormatAvailable(fmt->cfFormat)) ? S_OK : DV_E_CLIPFORMAT; |
| } |
| |
| /************************************************************************ |
| * snapshot_GetCanonicalFormatEtc |
| */ |
| static HRESULT WINAPI snapshot_GetCanonicalFormatEtc(IDataObject *iface, FORMATETC *fmt_in, |
| FORMATETC *fmt_out) |
| { |
| TRACE("(%p, %p, %p)\n", iface, fmt_in, fmt_out); |
| |
| if ( !fmt_in || !fmt_out ) return E_INVALIDARG; |
| |
| *fmt_out = *fmt_in; |
| return DATA_S_SAMEFORMATETC; |
| } |
| |
| /************************************************************************ |
| * snapshot_SetData |
| * |
| * The OLE Clipboard does not implement this method |
| */ |
| static HRESULT WINAPI snapshot_SetData(IDataObject *iface, FORMATETC *fmt, |
| STGMEDIUM *med, BOOL release) |
| { |
| TRACE("(%p, %p, %p, %d): not implemented\n", iface, fmt, med, release); |
| return E_NOTIMPL; |
| } |
| |
| /************************************************************************ |
| * snapshot_EnumFormatEtc |
| * |
| */ |
| static HRESULT WINAPI snapshot_EnumFormatEtc(IDataObject *iface, DWORD dir, |
| IEnumFORMATETC **enum_fmt) |
| { |
| HRESULT hr; |
| ole_priv_data *data = NULL; |
| |
| TRACE("(%p, %x, %p)\n", iface, dir, enum_fmt); |
| |
| *enum_fmt = NULL; |
| |
| if ( dir != DATADIR_GET ) return E_NOTIMPL; |
| if ( !OpenClipboard(NULL) ) return CLIPBRD_E_CANT_OPEN; |
| |
| hr = get_priv_data(&data); |
| |
| if(FAILED(hr)) goto end; |
| |
| hr = enum_fmtetc_construct( data, 0, enum_fmt ); |
| |
| end: |
| if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; |
| return hr; |
| } |
| |
| /************************************************************************ |
| * snapshot_DAdvise |
| * |
| * The OLE Clipboard does not implement this method |
| */ |
| static HRESULT WINAPI snapshot_DAdvise(IDataObject *iface, FORMATETC *fmt, |
| DWORD flags, IAdviseSink *sink, |
| DWORD *conn) |
| { |
| TRACE("(%p, %p, %x, %p, %p): not implemented\n", iface, fmt, flags, sink, conn); |
| return E_NOTIMPL; |
| } |
| |
| /************************************************************************ |
| * snapshot_DUnadvise |
| * |
| * The OLE Clipboard does not implement this method |
| */ |
| static HRESULT WINAPI snapshot_DUnadvise(IDataObject* iface, DWORD conn) |
| { |
| TRACE("(%p, %d): not implemented\n", iface, conn); |
| return E_NOTIMPL; |
| } |
| |
| /************************************************************************ |
| * snapshot_EnumDAdvise |
| * |
| * The OLE Clipboard does not implement this method |
| */ |
| static HRESULT WINAPI snapshot_EnumDAdvise(IDataObject* iface, |
| IEnumSTATDATA** enum_advise) |
| { |
| TRACE("(%p, %p): not implemented\n", iface, enum_advise); |
| return E_NOTIMPL; |
| } |
| |
| static const IDataObjectVtbl snapshot_vtable = |
| { |
| snapshot_QueryInterface, |
| snapshot_AddRef, |
| snapshot_Release, |
| snapshot_GetData, |
| snapshot_GetDataHere, |
| snapshot_QueryGetData, |
| snapshot_GetCanonicalFormatEtc, |
| snapshot_SetData, |
| snapshot_EnumFormatEtc, |
| snapshot_DAdvise, |
| snapshot_DUnadvise, |
| snapshot_EnumDAdvise |
| }; |
| |
| /*---------------------------------------------------------------------* |
| * Internal implementation methods for the OLE clipboard |
| *---------------------------------------------------------------------*/ |
| |
| static snapshot *snapshot_construct(DWORD seq_no) |
| { |
| snapshot *This; |
| |
| This = HeapAlloc( GetProcessHeap(), 0, sizeof(*This) ); |
| if (!This) return NULL; |
| |
| This->IDataObject_iface.lpVtbl = &snapshot_vtable; |
| This->ref = 0; |
| This->seq_no = seq_no; |
| This->data = NULL; |
| |
| return This; |
| } |
| |
| /********************************************************* |
| * register_clipboard_formats |
| */ |
| static void register_clipboard_formats(void) |
| { |
| static const WCHAR OwnerLink[] = {'O','w','n','e','r','L','i','n','k',0}; |
| static const WCHAR FileName[] = {'F','i','l','e','N','a','m','e',0}; |
| static const WCHAR FileNameW[] = {'F','i','l','e','N','a','m','e','W',0}; |
| static const WCHAR DataObject[] = {'D','a','t','a','O','b','j','e','c','t',0}; |
| static const WCHAR EmbeddedObject[] = {'E','m','b','e','d','d','e','d',' ','O','b','j','e','c','t',0}; |
| static const WCHAR EmbedSource[] = {'E','m','b','e','d',' ','S','o','u','r','c','e',0}; |
| static const WCHAR CustomLinkSource[] = {'C','u','s','t','o','m',' ','L','i','n','k',' ','S','o','u','r','c','e',0}; |
| static const WCHAR LinkSource[] = {'L','i','n','k',' ','S','o','u','r','c','e',0}; |
| static const WCHAR ObjectDescriptor[] = {'O','b','j','e','c','t',' ','D','e','s','c','r','i','p','t','o','r',0}; |
| static const WCHAR LinkSourceDescriptor[] = {'L','i','n','k',' ','S','o','u','r','c','e',' ', |
| 'D','e','s','c','r','i','p','t','o','r',0}; |
| static const WCHAR OlePrivateData[] = {'O','l','e',' ','P','r','i','v','a','t','e',' ','D','a','t','a',0}; |
| |
| static const WCHAR WineMarshalledDataObject[] = {'W','i','n','e',' ','M','a','r','s','h','a','l','l','e','d',' ', |
| 'D','a','t','a','O','b','j','e','c','t',0}; |
| |
| ownerlink_clipboard_format = RegisterClipboardFormatW(OwnerLink); |
| filename_clipboard_format = RegisterClipboardFormatW(FileName); |
| filenameW_clipboard_format = RegisterClipboardFormatW(FileNameW); |
| dataobject_clipboard_format = RegisterClipboardFormatW(DataObject); |
| embedded_object_clipboard_format = RegisterClipboardFormatW(EmbeddedObject); |
| embed_source_clipboard_format = RegisterClipboardFormatW(EmbedSource); |
| custom_link_source_clipboard_format = RegisterClipboardFormatW(CustomLinkSource); |
| link_source_clipboard_format = RegisterClipboardFormatW(LinkSource); |
| object_descriptor_clipboard_format = RegisterClipboardFormatW(ObjectDescriptor); |
| link_source_descriptor_clipboard_format = RegisterClipboardFormatW(LinkSourceDescriptor); |
| ole_private_data_clipboard_format = RegisterClipboardFormatW(OlePrivateData); |
| |
| wine_marshal_clipboard_format = RegisterClipboardFormatW(WineMarshalledDataObject); |
| } |
| |
| /*********************************************************************** |
| * OLEClipbrd_Initialize() |
| * Initializes the OLE clipboard. |
| */ |
| void OLEClipbrd_Initialize(void) |
| { |
| register_clipboard_formats(); |
| |
| if ( !theOleClipboard ) |
| { |
| ole_clipbrd* clipbrd; |
| HGLOBAL h; |
| |
| TRACE("()\n"); |
| |
| clipbrd = HeapAlloc( GetProcessHeap(), 0, sizeof(*clipbrd) ); |
| if (!clipbrd) return; |
| |
| clipbrd->latest_snapshot = NULL; |
| clipbrd->window = NULL; |
| clipbrd->src_data = NULL; |
| clipbrd->cached_enum = NULL; |
| |
| h = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, 0); |
| if(!h) |
| { |
| HeapFree(GetProcessHeap(), 0, clipbrd); |
| return; |
| } |
| |
| if(FAILED(CreateStreamOnHGlobal(h, TRUE, &clipbrd->marshal_data))) |
| { |
| GlobalFree(h); |
| HeapFree(GetProcessHeap(), 0, clipbrd); |
| return; |
| } |
| |
| theOleClipboard = clipbrd; |
| } |
| } |
| |
| /********************************************************************* |
| * set_clipboard_formats |
| * |
| * Enumerate all formats supported by the source and make |
| * those formats available using delayed rendering using SetClipboardData. |
| * Cache the enumeration list and make that list visible as the |
| * 'Ole Private Data' format on the clipboard. |
| * |
| */ |
| static HRESULT set_clipboard_formats(ole_clipbrd *clipbrd, IDataObject *data) |
| { |
| HRESULT hr; |
| FORMATETC fmt; |
| IEnumFORMATETC *enum_fmt; |
| HGLOBAL priv_data_handle; |
| DWORD_PTR target_offset; |
| ole_priv_data *priv_data; |
| DWORD count = 0, needed = sizeof(*priv_data), idx; |
| |
| hr = IDataObject_EnumFormatEtc(data, DATADIR_GET, &enum_fmt); |
| if(FAILED(hr)) return hr; |
| |
| while(IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL) == S_OK) |
| { |
| count++; |
| needed += sizeof(priv_data->entries[0]); |
| if(fmt.ptd) |
| { |
| needed += fmt.ptd->tdSize; |
| CoTaskMemFree(fmt.ptd); |
| } |
| } |
| |
| /* Windows pads the list with two empty ole_priv_data_entries, one |
| * after the entries array and one after the target device data. |
| * Allocating with zero init to zero these pads. */ |
| |
| needed += sizeof(priv_data->entries[0]); /* initialisation of needed includes one of these. */ |
| priv_data_handle = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT, needed); |
| priv_data = GlobalLock(priv_data_handle); |
| |
| priv_data->unk1 = 0; |
| priv_data->size = needed; |
| priv_data->unk2 = 1; |
| priv_data->count = count; |
| priv_data->unk3[0] = 0; |
| priv_data->unk3[1] = 0; |
| |
| IEnumFORMATETC_Reset(enum_fmt); |
| |
| idx = 0; |
| target_offset = FIELD_OFFSET(ole_priv_data, entries[count + 1]); /* count entries + one pad. */ |
| |
| while(IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL) == S_OK) |
| { |
| TRACE("%s\n", dump_fmtetc(&fmt)); |
| |
| priv_data->entries[idx].fmtetc = fmt; |
| if(fmt.ptd) |
| { |
| memcpy((char*)priv_data + target_offset, fmt.ptd, fmt.ptd->tdSize); |
| priv_data->entries[idx].fmtetc.ptd = (DVTARGETDEVICE*)target_offset; |
| target_offset += fmt.ptd->tdSize; |
| CoTaskMemFree(fmt.ptd); |
| } |
| |
| priv_data->entries[idx].first_use = !find_format_in_list(priv_data->entries, idx, fmt.cfFormat); |
| priv_data->entries[idx].unk[0] = 0; |
| priv_data->entries[idx].unk[1] = 0; |
| |
| if (priv_data->entries[idx].first_use) |
| SetClipboardData(fmt.cfFormat, NULL); |
| |
| idx++; |
| } |
| |
| IEnumFORMATETC_Release(enum_fmt); |
| |
| /* Cache the list and fixup any target device offsets to ptrs */ |
| clipbrd->cached_enum = HeapAlloc(GetProcessHeap(), 0, needed); |
| memcpy(clipbrd->cached_enum, priv_data, needed); |
| for(idx = 0; idx < clipbrd->cached_enum->count; idx++) |
| clipbrd->cached_enum->entries[idx].fmtetc.ptd = |
| td_offs_to_ptr(clipbrd->cached_enum, (DWORD_PTR)clipbrd->cached_enum->entries[idx].fmtetc.ptd); |
| |
| GlobalUnlock(priv_data_handle); |
| if(!SetClipboardData(ole_private_data_clipboard_format, priv_data_handle)) |
| { |
| GlobalFree(priv_data_handle); |
| return CLIPBRD_E_CANT_SET; |
| } |
| |
| return S_OK; |
| } |
| |
| static HWND create_clipbrd_window(void); |
| |
| /*********************************************************************** |
| * get_clipbrd_window |
| */ |
| static inline HRESULT get_clipbrd_window(ole_clipbrd *clipbrd, HWND *wnd) |
| { |
| if ( !clipbrd->window ) |
| clipbrd->window = create_clipbrd_window(); |
| |
| *wnd = clipbrd->window; |
| return *wnd ? S_OK : E_FAIL; |
| } |
| |
| |
| /********************************************************************** |
| * release_marshal_data |
| * |
| * Releases the data and sets the stream back to zero size. |
| */ |
| static inline void release_marshal_data(IStream *stm) |
| { |
| LARGE_INTEGER pos; |
| ULARGE_INTEGER size; |
| pos.QuadPart = size.QuadPart = 0; |
| |
| IStream_Seek(stm, pos, STREAM_SEEK_SET, NULL); |
| CoReleaseMarshalData(stm); |
| IStream_Seek(stm, pos, STREAM_SEEK_SET, NULL); |
| IStream_SetSize(stm, size); |
| } |
| |
| /*********************************************************************** |
| * expose_marshalled_dataobject |
| * |
| * Sets the marshalled dataobject to the clipboard. In the flushed case |
| * we set a zero sized HGLOBAL to clear the old marshalled data. |
| */ |
| static HRESULT expose_marshalled_dataobject(ole_clipbrd *clipbrd, IDataObject *data) |
| { |
| HGLOBAL h; |
| |
| if(data) |
| { |
| HGLOBAL h_stm; |
| GetHGlobalFromStream(clipbrd->marshal_data, &h_stm); |
| dup_global_mem(h_stm, GMEM_DDESHARE|GMEM_MOVEABLE, &h); |
| } |
| else /* flushed */ |
| h = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE, 1); |
| |
| if(!h) return E_OUTOFMEMORY; |
| |
| if(!SetClipboardData(wine_marshal_clipboard_format, h)) |
| { |
| GlobalFree(h); |
| return CLIPBRD_E_CANT_SET; |
| } |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * set_src_dataobject |
| * |
| * Clears and sets the clipboard's src IDataObject. |
| * |
| * To marshal the source dataobject we do something rather different from Windows. |
| * We set a clipboard format which contains the marshalled data. |
| * Windows sets two window props one of which is an IID, the other is an endpoint number. |
| */ |
| static HRESULT set_src_dataobject(ole_clipbrd *clipbrd, IDataObject *data) |
| { |
| HRESULT hr; |
| HWND wnd; |
| |
| if(FAILED(hr = get_clipbrd_window(clipbrd, &wnd))) return hr; |
| |
| if(clipbrd->src_data) |
| { |
| release_marshal_data(clipbrd->marshal_data); |
| |
| IDataObject_Release(clipbrd->src_data); |
| clipbrd->src_data = NULL; |
| HeapFree(GetProcessHeap(), 0, clipbrd->cached_enum); |
| clipbrd->cached_enum = NULL; |
| } |
| |
| if(data) |
| { |
| IUnknown *unk; |
| |
| IDataObject_AddRef(data); |
| clipbrd->src_data = data; |
| |
| IDataObject_QueryInterface(data, &IID_IUnknown, (void**)&unk); |
| hr = CoMarshalInterface(clipbrd->marshal_data, &IID_IDataObject, unk, |
| MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG); |
| IUnknown_Release(unk); /* Don't hold a ref on IUnknown, we have one on IDataObject. */ |
| if(FAILED(hr)) return hr; |
| hr = set_clipboard_formats(clipbrd, data); |
| } |
| return hr; |
| } |
| |
| /*********************************************************************** |
| * OLEClipbrd_UnInitialize() |
| * Un-Initializes the OLE clipboard |
| */ |
| void OLEClipbrd_UnInitialize(void) |
| { |
| ole_clipbrd *clipbrd = theOleClipboard; |
| |
| TRACE("()\n"); |
| |
| if ( clipbrd ) |
| { |
| static const WCHAR ole32W[] = {'o','l','e','3','2',0}; |
| HINSTANCE hinst = GetModuleHandleW(ole32W); |
| |
| /* OleUninitialize() does not release the reference to the dataobject, so |
| take an additional reference here. This reference is then leaked. */ |
| if (clipbrd->src_data) |
| { |
| IDataObject_AddRef(clipbrd->src_data); |
| set_src_dataobject(clipbrd, NULL); |
| } |
| |
| if ( clipbrd->window ) |
| { |
| DestroyWindow(clipbrd->window); |
| UnregisterClassW( clipbrd_wndclass, hinst ); |
| } |
| |
| IStream_Release(clipbrd->marshal_data); |
| HeapFree(GetProcessHeap(), 0, clipbrd); |
| theOleClipboard = NULL; |
| } |
| } |
| |
| /*********************************************************************** |
| * clipbrd_wndproc |
| */ |
| static LRESULT CALLBACK clipbrd_wndproc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) |
| { |
| ole_clipbrd *clipbrd; |
| |
| get_ole_clipbrd(&clipbrd); |
| |
| switch (message) |
| { |
| case WM_RENDERFORMAT: |
| { |
| UINT cf = wparam; |
| ole_priv_data_entry *entry; |
| |
| TRACE("(): WM_RENDERFORMAT(cfFormat=%x)\n", cf); |
| entry = find_format_in_list(clipbrd->cached_enum->entries, clipbrd->cached_enum->count, cf); |
| |
| if(entry) |
| render_format(clipbrd->src_data, &entry->fmtetc); |
| |
| break; |
| } |
| |
| case WM_RENDERALLFORMATS: |
| { |
| DWORD i; |
| ole_priv_data_entry *entries; |
| |
| TRACE("(): WM_RENDERALLFORMATS\n"); |
| |
| if (!clipbrd || !clipbrd->cached_enum) break; |
| entries = clipbrd->cached_enum->entries; |
| for(i = 0; i < clipbrd->cached_enum->count; i++) |
| { |
| if(entries[i].first_use) |
| render_format(clipbrd->src_data, &entries[i].fmtetc); |
| } |
| break; |
| } |
| |
| case WM_DESTROYCLIPBOARD: |
| { |
| TRACE("(): WM_DESTROYCLIPBOARD\n"); |
| |
| set_src_dataobject(clipbrd, NULL); |
| break; |
| } |
| |
| default: |
| return DefWindowProcW(hwnd, message, wparam, lparam); |
| } |
| |
| return 0; |
| } |
| |
| |
| /*********************************************************************** |
| * create_clipbrd_window |
| */ |
| static HWND create_clipbrd_window(void) |
| { |
| WNDCLASSEXW class; |
| static const WCHAR ole32W[] = {'o','l','e','3','2',0}; |
| static const WCHAR title[] = {'C','l','i','p','b','o','a','r','d','W','i','n','d','o','w',0}; |
| HINSTANCE hinst = GetModuleHandleW(ole32W); |
| |
| class.cbSize = sizeof(class); |
| class.style = 0; |
| class.lpfnWndProc = clipbrd_wndproc; |
| class.cbClsExtra = 0; |
| class.cbWndExtra = 0; |
| class.hInstance = hinst; |
| class.hIcon = 0; |
| class.hCursor = 0; |
| class.hbrBackground = 0; |
| class.lpszMenuName = NULL; |
| class.lpszClassName = clipbrd_wndclass; |
| class.hIconSm = NULL; |
| |
| RegisterClassExW(&class); |
| |
| return CreateWindowW(clipbrd_wndclass, title, WS_POPUP | WS_CLIPSIBLINGS | WS_OVERLAPPED, |
| 0, 0, 0, 0, HWND_MESSAGE, NULL, hinst, 0); |
| } |
| |
| /********************************************************************* |
| * set_dataobject_format |
| * |
| * Windows creates a 'DataObject' clipboard format that contains the |
| * clipboard window's HWND or NULL if the Ole clipboard has been flushed. |
| */ |
| static HRESULT set_dataobject_format(HWND hwnd) |
| { |
| HGLOBAL h = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, sizeof(hwnd)); |
| HWND *data; |
| |
| if(!h) return E_OUTOFMEMORY; |
| |
| data = GlobalLock(h); |
| *data = hwnd; |
| GlobalUnlock(h); |
| |
| if(!SetClipboardData(dataobject_clipboard_format, h)) |
| { |
| GlobalFree(h); |
| return CLIPBRD_E_CANT_SET; |
| } |
| |
| return S_OK; |
| } |
| |
| /*---------------------------------------------------------------------* |
| * 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* data) |
| { |
| HRESULT hr; |
| ole_clipbrd *clipbrd; |
| HWND wnd; |
| |
| TRACE("(%p)\n", data); |
| |
| if(FAILED(hr = get_ole_clipbrd(&clipbrd))) return hr; |
| |
| if(FAILED(hr = get_clipbrd_window(clipbrd, &wnd))) return hr; |
| |
| if ( !OpenClipboard(wnd) ) return CLIPBRD_E_CANT_OPEN; |
| |
| if ( !EmptyClipboard() ) |
| { |
| hr = CLIPBRD_E_CANT_EMPTY; |
| goto end; |
| } |
| |
| hr = set_src_dataobject(clipbrd, data); |
| if(FAILED(hr)) goto end; |
| |
| if(data) |
| { |
| hr = expose_marshalled_dataobject(clipbrd, data); |
| if(FAILED(hr)) goto end; |
| hr = set_dataobject_format(wnd); |
| } |
| |
| end: |
| |
| if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; |
| |
| if ( FAILED(hr) ) |
| { |
| expose_marshalled_dataobject(clipbrd, NULL); |
| set_src_dataobject(clipbrd, 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 **obj) |
| { |
| HRESULT hr; |
| ole_clipbrd *clipbrd; |
| DWORD seq_no; |
| |
| TRACE("(%p)\n", obj); |
| |
| if(!obj) return E_INVALIDARG; |
| *obj = NULL; |
| |
| if(FAILED(hr = get_ole_clipbrd(&clipbrd))) return hr; |
| |
| seq_no = GetClipboardSequenceNumber(); |
| EnterCriticalSection(&latest_snapshot_cs); |
| if(clipbrd->latest_snapshot && clipbrd->latest_snapshot->seq_no != seq_no) |
| clipbrd->latest_snapshot = NULL; |
| |
| if(!clipbrd->latest_snapshot) |
| { |
| clipbrd->latest_snapshot = snapshot_construct(seq_no); |
| if(!clipbrd->latest_snapshot) |
| { |
| LeaveCriticalSection(&latest_snapshot_cs); |
| return E_OUTOFMEMORY; |
| } |
| } |
| |
| *obj = &clipbrd->latest_snapshot->IDataObject_iface; |
| IDataObject_AddRef(*obj); |
| LeaveCriticalSection(&latest_snapshot_cs); |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * 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(void) |
| { |
| HRESULT hr; |
| ole_clipbrd *clipbrd; |
| HWND wnd; |
| |
| TRACE("()\n"); |
| |
| if(FAILED(hr = get_ole_clipbrd(&clipbrd))) return hr; |
| |
| if(FAILED(hr = get_clipbrd_window(clipbrd, &wnd))) return hr; |
| |
| /* |
| * Already flushed or no source DataObject? Nothing to do. |
| */ |
| if (!clipbrd->src_data) return S_OK; |
| |
| if (!OpenClipboard(wnd)) return CLIPBRD_E_CANT_OPEN; |
| |
| SendMessageW(wnd, WM_RENDERALLFORMATS, 0, 0); |
| |
| hr = set_dataobject_format(NULL); |
| |
| expose_marshalled_dataobject(clipbrd, NULL); |
| set_src_dataobject(clipbrd, NULL); |
| |
| if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; |
| |
| return hr; |
| } |
| |
| |
| /*********************************************************************** |
| * OleIsCurrentClipboard [OLE32.@] |
| */ |
| HRESULT WINAPI OleIsCurrentClipboard(IDataObject *data) |
| { |
| HRESULT hr; |
| ole_clipbrd *clipbrd; |
| TRACE("()\n"); |
| |
| if(FAILED(hr = get_ole_clipbrd(&clipbrd))) return hr; |
| |
| if (data == NULL) return S_FALSE; |
| |
| return (data == clipbrd->src_data) ? S_OK : S_FALSE; |
| } |