| /* | |
| * OLE 2 Data cache | |
| * | |
| * Copyright 1999 Francis Beaudet | |
| * Copyright 2000 Abey George | |
| * | |
| * 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: | |
| * The OLE2 data cache supports a whole whack of | |
| * interfaces including: | |
| * IDataObject, IPersistStorage, IViewObject2, | |
| * IOleCache2 and IOleCacheControl. | |
| * | |
| * Most of the implementation details are taken from: Inside OLE | |
| * second edition by Kraig Brockschmidt, | |
| * | |
| * NOTES | |
| * - This implementation of the datacache will let your application | |
| * load documents that have embedded OLE objects in them and it will | |
| * also retrieve the metafile representation of those objects. | |
| * - This implementation of the datacache will also allow your | |
| * application to save new documents with OLE objects in them. | |
| * - The main thing that it doesn't do is allow you to activate | |
| * or modify the OLE objects in any way. | |
| * - I haven't found any good documentation on the real usage of | |
| * the streams created by the data cache. In particular, How to | |
| * determine what the XXX stands for in the stream name | |
| * "\002OlePresXXX". It appears to just be a counter. | |
| * - Also, I don't know the real content of the presentation stream | |
| * header. I was able to figure-out where the extent of the object | |
| * was stored and the aspect, but that's about it. | |
| */ | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #define COBJMACROS | |
| #define NONAMELESSUNION | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winerror.h" | |
| #include "ole2.h" | |
| #include "compobj_private.h" | |
| #include "wine/unicode.h" | |
| #include "wine/list.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); | |
| /**************************************************************************** | |
| * PresentationDataHeader | |
| * | |
| * This structure represents the header of the \002OlePresXXX stream in | |
| * the OLE object storage. | |
| */ | |
| typedef struct PresentationDataHeader | |
| { | |
| /* clipformat: | |
| * - standard clipformat: | |
| * DWORD length = 0xffffffff; | |
| * DWORD cfFormat; | |
| * - or custom clipformat: | |
| * DWORD length; | |
| * CHAR format_name[length]; (null-terminated) | |
| */ | |
| DWORD unknown3; /* 4, possibly TYMED_ISTREAM */ | |
| DVASPECT dvAspect; | |
| DWORD lindex; | |
| DWORD advf; | |
| DWORD unknown7; /* 0 */ | |
| DWORD dwObjectExtentX; | |
| DWORD dwObjectExtentY; | |
| DWORD dwSize; | |
| } PresentationDataHeader; | |
| enum stream_type | |
| { | |
| no_stream, | |
| pres_stream, | |
| contents_stream | |
| }; | |
| typedef struct DataCacheEntry | |
| { | |
| struct list entry; | |
| /* format of this entry */ | |
| FORMATETC fmtetc; | |
| /* the clipboard format of the data */ | |
| CLIPFORMAT data_cf; | |
| /* cached data */ | |
| STGMEDIUM stgmedium; | |
| /* | |
| * This stream pointer is set through a call to | |
| * IPersistStorage_Load. This is where the visual | |
| * representation of the object is stored. | |
| */ | |
| IStream *stream; | |
| enum stream_type stream_type; | |
| /* connection ID */ | |
| DWORD id; | |
| /* dirty flag */ | |
| BOOL dirty; | |
| /* stream number (-1 if not set ) */ | |
| unsigned short stream_number; | |
| /* sink id set when object is running */ | |
| DWORD sink_id; | |
| /* Advise sink flags */ | |
| DWORD advise_flags; | |
| } DataCacheEntry; | |
| /**************************************************************************** | |
| * DataCache | |
| */ | |
| struct DataCache | |
| { | |
| /* | |
| * List all interface here | |
| */ | |
| IUnknown IUnknown_inner; | |
| IDataObject IDataObject_iface; | |
| IPersistStorage IPersistStorage_iface; | |
| IViewObject2 IViewObject2_iface; | |
| IOleCache2 IOleCache2_iface; | |
| IOleCacheControl IOleCacheControl_iface; | |
| /* The sink that is connected to a remote object. | |
| The other interfaces are not available by QI'ing the sink and vice-versa */ | |
| IAdviseSink IAdviseSink_iface; | |
| /* | |
| * Reference count of this object | |
| */ | |
| LONG ref; | |
| /* | |
| * IUnknown implementation of the outer object. | |
| */ | |
| IUnknown *outer_unk; | |
| /* | |
| * The user of this object can setup ONE advise sink | |
| * connection with the object. These parameters describe | |
| * that connection. | |
| */ | |
| DWORD sinkAspects; | |
| DWORD sinkAdviseFlag; | |
| IAdviseSink *sinkInterface; | |
| CLSID clsid; | |
| IStorage *presentationStorage; | |
| /* list of cache entries */ | |
| struct list cache_list; | |
| /* last id assigned to an entry */ | |
| DWORD last_cache_id; | |
| /* dirty flag */ | |
| BOOL dirty; | |
| /* running object set by OnRun */ | |
| IDataObject *running_object; | |
| }; | |
| typedef struct DataCache DataCache; | |
| /* | |
| * Here, I define utility macros to help with the casting of the | |
| * "this" parameter. | |
| * There is a version to accommodate all of the VTables implemented | |
| * by this object. | |
| */ | |
| static inline DataCache *impl_from_IDataObject( IDataObject *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IDataObject_iface); | |
| } | |
| static inline DataCache *impl_from_IUnknown( IUnknown *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IUnknown_inner); | |
| } | |
| static inline DataCache *impl_from_IPersistStorage( IPersistStorage *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IPersistStorage_iface); | |
| } | |
| static inline DataCache *impl_from_IViewObject2( IViewObject2 *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IViewObject2_iface); | |
| } | |
| static inline DataCache *impl_from_IOleCache2( IOleCache2 *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IOleCache2_iface); | |
| } | |
| static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IOleCacheControl_iface); | |
| } | |
| static inline DataCache *impl_from_IAdviseSink( IAdviseSink *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, DataCache, IAdviseSink_iface); | |
| } | |
| const char *debugstr_formatetc(const FORMATETC *formatetc) | |
| { | |
| return wine_dbg_sprintf("{ cfFormat = 0x%x, ptd = %p, dwAspect = %d, lindex = %d, tymed = %d }", | |
| formatetc->cfFormat, formatetc->ptd, formatetc->dwAspect, | |
| formatetc->lindex, formatetc->tymed); | |
| } | |
| /*********************************************************************** | |
| * bitmap_info_size | |
| * | |
| * Return the size of the bitmap info structure including color table. | |
| */ | |
| static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) | |
| { | |
| unsigned int colors, size, masks = 0; | |
| if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) | |
| { | |
| const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info; | |
| colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0; | |
| return sizeof(BITMAPCOREHEADER) + colors * | |
| ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD)); | |
| } | |
| else /* assume BITMAPINFOHEADER */ | |
| { | |
| colors = info->bmiHeader.biClrUsed; | |
| if (colors > 256) /* buffer overflow otherwise */ | |
| colors = 256; | |
| if (!colors && (info->bmiHeader.biBitCount <= 8)) | |
| colors = 1 << info->bmiHeader.biBitCount; | |
| if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3; | |
| size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) ); | |
| return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD)); | |
| } | |
| } | |
| static void DataCacheEntry_Destroy(DataCache *cache, DataCacheEntry *cache_entry) | |
| { | |
| list_remove(&cache_entry->entry); | |
| if (cache_entry->stream) | |
| IStream_Release(cache_entry->stream); | |
| CoTaskMemFree(cache_entry->fmtetc.ptd); | |
| ReleaseStgMedium(&cache_entry->stgmedium); | |
| if(cache_entry->sink_id) | |
| IDataObject_DUnadvise(cache->running_object, cache_entry->sink_id); | |
| HeapFree(GetProcessHeap(), 0, cache_entry); | |
| } | |
| static void DataCache_Destroy( | |
| DataCache* ptrToDestroy) | |
| { | |
| DataCacheEntry *cache_entry, *next_cache_entry; | |
| TRACE("()\n"); | |
| if (ptrToDestroy->sinkInterface != NULL) | |
| { | |
| IAdviseSink_Release(ptrToDestroy->sinkInterface); | |
| ptrToDestroy->sinkInterface = NULL; | |
| } | |
| LIST_FOR_EACH_ENTRY_SAFE(cache_entry, next_cache_entry, &ptrToDestroy->cache_list, DataCacheEntry, entry) | |
| DataCacheEntry_Destroy(ptrToDestroy, cache_entry); | |
| if (ptrToDestroy->presentationStorage != NULL) | |
| { | |
| IStorage_Release(ptrToDestroy->presentationStorage); | |
| ptrToDestroy->presentationStorage = NULL; | |
| } | |
| /* | |
| * Free the datacache pointer. | |
| */ | |
| HeapFree(GetProcessHeap(), 0, ptrToDestroy); | |
| } | |
| static DataCacheEntry *DataCache_GetEntryForFormatEtc(DataCache *This, const FORMATETC *formatetc) | |
| { | |
| DataCacheEntry *cache_entry; | |
| FORMATETC fmt = *formatetc; | |
| if (fmt.cfFormat == CF_BITMAP) | |
| { | |
| fmt.cfFormat = CF_DIB; | |
| fmt.tymed = TYMED_HGLOBAL; | |
| } | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| /* FIXME: also compare DVTARGETDEVICEs */ | |
| if ((!cache_entry->fmtetc.cfFormat || !fmt.cfFormat || (fmt.cfFormat == cache_entry->fmtetc.cfFormat)) && | |
| (fmt.dwAspect == cache_entry->fmtetc.dwAspect) && | |
| (fmt.lindex == cache_entry->fmtetc.lindex) && | |
| (!cache_entry->fmtetc.tymed || !fmt.tymed || (fmt.tymed == cache_entry->fmtetc.tymed))) | |
| return cache_entry; | |
| } | |
| return NULL; | |
| } | |
| /* checks that the clipformat and tymed are valid and returns an error if they | |
| * aren't and CACHE_S_NOTSUPPORTED if they are valid, but can't be rendered by | |
| * DataCache_Draw */ | |
| static HRESULT check_valid_clipformat_and_tymed(CLIPFORMAT cfFormat, DWORD tymed) | |
| { | |
| if (!cfFormat || !tymed || | |
| (cfFormat == CF_METAFILEPICT && tymed == TYMED_MFPICT) || | |
| (cfFormat == CF_BITMAP && tymed == TYMED_GDI) || | |
| (cfFormat == CF_DIB && tymed == TYMED_HGLOBAL) || | |
| (cfFormat == CF_ENHMETAFILE && tymed == TYMED_ENHMF)) | |
| return S_OK; | |
| else if (tymed == TYMED_HGLOBAL) | |
| return CACHE_S_FORMATETC_NOTSUPPORTED; | |
| else | |
| { | |
| WARN("invalid clipformat/tymed combination: %d/%d\n", cfFormat, tymed); | |
| return DV_E_TYMED; | |
| } | |
| } | |
| static BOOL init_cache_entry(DataCacheEntry *entry, const FORMATETC *fmt, DWORD advf, | |
| DWORD id) | |
| { | |
| HRESULT hr; | |
| hr = copy_formatetc(&entry->fmtetc, fmt); | |
| if (FAILED(hr)) return FALSE; | |
| entry->data_cf = 0; | |
| entry->stgmedium.tymed = TYMED_NULL; | |
| entry->stgmedium.pUnkForRelease = NULL; | |
| entry->stream = NULL; | |
| entry->stream_type = no_stream; | |
| entry->id = id; | |
| entry->dirty = TRUE; | |
| entry->stream_number = -1; | |
| entry->sink_id = 0; | |
| entry->advise_flags = advf; | |
| return TRUE; | |
| } | |
| static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc, DWORD advf, | |
| BOOL automatic, DataCacheEntry **cache_entry) | |
| { | |
| HRESULT hr; | |
| DWORD id = automatic ? 1 : This->last_cache_id; | |
| DataCacheEntry *entry; | |
| hr = check_valid_clipformat_and_tymed(formatetc->cfFormat, formatetc->tymed); | |
| if (FAILED(hr)) | |
| return hr; | |
| if (hr == CACHE_S_FORMATETC_NOTSUPPORTED) | |
| TRACE("creating unsupported format %d\n", formatetc->cfFormat); | |
| entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); | |
| if (!entry) | |
| return E_OUTOFMEMORY; | |
| if (!init_cache_entry(entry, formatetc, advf, id)) | |
| goto fail; | |
| if (automatic) | |
| list_add_head(&This->cache_list, &entry->entry); | |
| else | |
| { | |
| list_add_tail(&This->cache_list, &entry->entry); | |
| This->last_cache_id++; | |
| } | |
| if (cache_entry) *cache_entry = entry; | |
| return hr; | |
| fail: | |
| HeapFree(GetProcessHeap(), 0, entry); | |
| return E_OUTOFMEMORY; | |
| } | |
| /************************************************************************ | |
| * DataCache_FireOnViewChange | |
| * | |
| * This method will fire an OnViewChange notification to the advise | |
| * sink registered with the datacache. | |
| * | |
| * See IAdviseSink::OnViewChange for more details. | |
| */ | |
| static void DataCache_FireOnViewChange( | |
| DataCache* this, | |
| DWORD aspect, | |
| LONG lindex) | |
| { | |
| TRACE("(%p, %x, %d)\n", this, aspect, lindex); | |
| /* | |
| * The sink supplies a filter when it registers | |
| * we make sure we only send the notifications when that | |
| * filter matches. | |
| */ | |
| if ((this->sinkAspects & aspect) != 0) | |
| { | |
| if (this->sinkInterface != NULL) | |
| { | |
| IAdviseSink_OnViewChange(this->sinkInterface, | |
| aspect, | |
| lindex); | |
| /* | |
| * Some sinks want to be unregistered automatically when | |
| * the first notification goes out. | |
| */ | |
| if ( (this->sinkAdviseFlag & ADVF_ONLYONCE) != 0) | |
| { | |
| IAdviseSink_Release(this->sinkInterface); | |
| this->sinkInterface = NULL; | |
| this->sinkAspects = 0; | |
| this->sinkAdviseFlag = 0; | |
| } | |
| } | |
| } | |
| } | |
| /* Helper for DataCacheEntry_OpenPresStream */ | |
| static BOOL DataCache_IsPresentationStream(const STATSTG *elem) | |
| { | |
| /* The presentation streams have names of the form "\002OlePresXXX", | |
| * where XXX goes from 000 to 999. */ | |
| static const WCHAR OlePres[] = { 2,'O','l','e','P','r','e','s' }; | |
| LPCWSTR name = elem->pwcsName; | |
| return (elem->type == STGTY_STREAM) | |
| && (strlenW(name) == 11) | |
| && (strncmpW(name, OlePres, 8) == 0) | |
| && (name[8] >= '0') && (name[8] <= '9') | |
| && (name[9] >= '0') && (name[9] <= '9') | |
| && (name[10] >= '0') && (name[10] <= '9'); | |
| } | |
| static HRESULT read_clipformat(IStream *stream, CLIPFORMAT *clipformat) | |
| { | |
| DWORD length; | |
| HRESULT hr; | |
| ULONG read; | |
| *clipformat = 0; | |
| hr = IStream_Read(stream, &length, sizeof(length), &read); | |
| if (hr != S_OK || read != sizeof(length)) | |
| return DV_E_CLIPFORMAT; | |
| if (length == -1) | |
| { | |
| DWORD cf; | |
| hr = IStream_Read(stream, &cf, sizeof(cf), &read); | |
| if (hr != S_OK || read != sizeof(cf)) | |
| return DV_E_CLIPFORMAT; | |
| *clipformat = cf; | |
| } | |
| else | |
| { | |
| char *format_name = HeapAlloc(GetProcessHeap(), 0, length); | |
| if (!format_name) | |
| return E_OUTOFMEMORY; | |
| hr = IStream_Read(stream, format_name, length, &read); | |
| if (hr != S_OK || read != length || format_name[length - 1] != '\0') | |
| { | |
| HeapFree(GetProcessHeap(), 0, format_name); | |
| return DV_E_CLIPFORMAT; | |
| } | |
| *clipformat = RegisterClipboardFormatA(format_name); | |
| HeapFree(GetProcessHeap(), 0, format_name); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat) | |
| { | |
| DWORD length; | |
| HRESULT hr; | |
| if (clipformat < 0xc000) | |
| length = -1; | |
| else | |
| length = GetClipboardFormatNameA(clipformat, NULL, 0); | |
| hr = IStream_Write(stream, &length, sizeof(length), NULL); | |
| if (FAILED(hr)) | |
| return hr; | |
| if (clipformat < 0xc000) | |
| { | |
| DWORD cf = clipformat; | |
| hr = IStream_Write(stream, &cf, sizeof(cf), NULL); | |
| } | |
| else | |
| { | |
| char *format_name = HeapAlloc(GetProcessHeap(), 0, length); | |
| if (!format_name) | |
| return E_OUTOFMEMORY; | |
| GetClipboardFormatNameA(clipformat, format_name, length); | |
| hr = IStream_Write(stream, format_name, length, NULL); | |
| HeapFree(GetProcessHeap(), 0, format_name); | |
| } | |
| return hr; | |
| } | |
| /************************************************************************ | |
| * DataCacheEntry_OpenPresStream | |
| * | |
| * This method will find the stream for the given presentation. It makes | |
| * no attempt at fallback. | |
| * | |
| * Param: | |
| * this - Pointer to the DataCache object | |
| * drawAspect - The aspect of the object that we wish to draw. | |
| * pStm - A returned stream. It points to the beginning of the | |
| * - presentation data, including the header. | |
| * | |
| * Errors: | |
| * S_OK The requested stream has been opened. | |
| * OLE_E_BLANK The requested stream could not be found. | |
| * Quite a few others I'm too lazy to map correctly. | |
| * | |
| * Notes: | |
| * Algorithm: Scan the elements of the presentation storage, looking | |
| * for presentation streams. For each presentation stream, | |
| * load the header and check to see if the aspect matches. | |
| * | |
| * If a fallback is desired, just opening the first presentation stream | |
| * is a possibility. | |
| */ | |
| static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStream **ppStm) | |
| { | |
| HRESULT hr; | |
| LARGE_INTEGER offset; | |
| if (cache_entry->stream) | |
| { | |
| /* Rewind the stream before returning it. */ | |
| offset.QuadPart = 0; | |
| hr = IStream_Seek( cache_entry->stream, offset, STREAM_SEEK_SET, NULL ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| *ppStm = cache_entry->stream; | |
| IStream_AddRef( cache_entry->stream ); | |
| } | |
| } | |
| else | |
| hr = OLE_E_BLANK; | |
| return hr; | |
| } | |
| static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm ) | |
| { | |
| HRESULT hr; | |
| STATSTG stat; | |
| ULARGE_INTEGER current_pos; | |
| void *bits; | |
| METAFILEPICT *mfpict; | |
| HGLOBAL hmfpict; | |
| PresentationDataHeader header; | |
| CLIPFORMAT clipformat; | |
| static const LARGE_INTEGER offset_zero; | |
| ULONG read; | |
| if (cache_entry->stream_type != pres_stream) | |
| { | |
| FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type ); | |
| return E_FAIL; | |
| } | |
| hr = IStream_Stat( stm, &stat, STATFLAG_NONAME ); | |
| if (FAILED( hr )) return hr; | |
| hr = read_clipformat( stm, &clipformat ); | |
| if (FAILED( hr )) return hr; | |
| hr = IStream_Read( stm, &header, sizeof(header), &read ); | |
| if (hr != S_OK || read != sizeof(header)) return E_FAIL; | |
| hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, ¤t_pos ); | |
| if (FAILED( hr )) return hr; | |
| stat.cbSize.QuadPart -= current_pos.QuadPart; | |
| hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) ); | |
| if (!hmfpict) return E_OUTOFMEMORY; | |
| mfpict = GlobalLock( hmfpict ); | |
| bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart); | |
| if (!bits) | |
| { | |
| GlobalFree( hmfpict ); | |
| return E_OUTOFMEMORY; | |
| } | |
| hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read ); | |
| if (hr != S_OK || read != stat.cbSize.u.LowPart) hr = E_FAIL; | |
| if (SUCCEEDED( hr )) | |
| { | |
| /* FIXME: get this from the stream */ | |
| mfpict->mm = MM_ANISOTROPIC; | |
| mfpict->xExt = header.dwObjectExtentX; | |
| mfpict->yExt = header.dwObjectExtentY; | |
| mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits ); | |
| if (!mfpict->hMF) | |
| hr = E_FAIL; | |
| } | |
| GlobalUnlock( hmfpict ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| cache_entry->data_cf = cache_entry->fmtetc.cfFormat; | |
| cache_entry->stgmedium.tymed = TYMED_MFPICT; | |
| cache_entry->stgmedium.u.hMetaFilePict = hmfpict; | |
| } | |
| else | |
| GlobalFree( hmfpict ); | |
| HeapFree( GetProcessHeap(), 0, bits ); | |
| return hr; | |
| } | |
| static HRESULT load_dib( DataCacheEntry *cache_entry, IStream *stm ) | |
| { | |
| HRESULT hr; | |
| STATSTG stat; | |
| void *dib; | |
| HGLOBAL hglobal; | |
| ULONG read, info_size, bi_size; | |
| BITMAPFILEHEADER file; | |
| BITMAPINFOHEADER *info; | |
| if (cache_entry->stream_type != contents_stream) | |
| { | |
| FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type ); | |
| return E_FAIL; | |
| } | |
| hr = IStream_Stat( stm, &stat, STATFLAG_NONAME ); | |
| if (FAILED( hr )) return hr; | |
| if (stat.cbSize.QuadPart < sizeof(file) + sizeof(DWORD)) return E_FAIL; | |
| hr = IStream_Read( stm, &file, sizeof(file), &read ); | |
| if (hr != S_OK || read != sizeof(file)) return E_FAIL; | |
| stat.cbSize.QuadPart -= sizeof(file); | |
| hglobal = GlobalAlloc( GMEM_MOVEABLE, stat.cbSize.u.LowPart ); | |
| if (!hglobal) return E_OUTOFMEMORY; | |
| dib = GlobalLock( hglobal ); | |
| hr = IStream_Read( stm, dib, sizeof(DWORD), &read ); | |
| if (hr != S_OK || read != sizeof(DWORD)) goto fail; | |
| bi_size = *(DWORD *)dib; | |
| if (stat.cbSize.QuadPart < bi_size) goto fail; | |
| hr = IStream_Read( stm, (char *)dib + sizeof(DWORD), bi_size - sizeof(DWORD), &read ); | |
| if (hr != S_OK || read != bi_size - sizeof(DWORD)) goto fail; | |
| info_size = bitmap_info_size( dib, DIB_RGB_COLORS ); | |
| if (stat.cbSize.QuadPart < info_size) goto fail; | |
| if (info_size > bi_size) | |
| { | |
| hr = IStream_Read( stm, (char *)dib + bi_size, info_size - bi_size, &read ); | |
| if (hr != S_OK || read != info_size - bi_size) goto fail; | |
| } | |
| stat.cbSize.QuadPart -= info_size; | |
| if (file.bfOffBits) | |
| { | |
| LARGE_INTEGER skip; | |
| skip.QuadPart = file.bfOffBits - sizeof(file) - info_size; | |
| if (stat.cbSize.QuadPart < skip.QuadPart) goto fail; | |
| hr = IStream_Seek( stm, skip, STREAM_SEEK_CUR, NULL ); | |
| if (hr != S_OK) goto fail; | |
| stat.cbSize.QuadPart -= skip.QuadPart; | |
| } | |
| hr = IStream_Read( stm, (char *)dib + info_size, stat.cbSize.u.LowPart, &read ); | |
| if (hr != S_OK || read != stat.cbSize.QuadPart) goto fail; | |
| if (bi_size >= sizeof(*info)) | |
| { | |
| info = (BITMAPINFOHEADER *)dib; | |
| if (info->biXPelsPerMeter == 0 || info->biYPelsPerMeter == 0) | |
| { | |
| HDC hdc = GetDC( 0 ); | |
| info->biXPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSX ), 10000, 254 ); | |
| info->biYPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSY ), 10000, 254 ); | |
| ReleaseDC( 0, hdc ); | |
| } | |
| } | |
| GlobalUnlock( hglobal ); | |
| cache_entry->data_cf = cache_entry->fmtetc.cfFormat; | |
| cache_entry->stgmedium.tymed = TYMED_HGLOBAL; | |
| cache_entry->stgmedium.u.hGlobal = hglobal; | |
| return S_OK; | |
| fail: | |
| GlobalUnlock( hglobal ); | |
| GlobalFree( hglobal ); | |
| return E_FAIL; | |
| } | |
| /************************************************************************ | |
| * DataCacheEntry_LoadData | |
| * | |
| * This method will read information for the requested presentation | |
| * into the given structure. | |
| * | |
| * Param: | |
| * This - The entry to load the data from. | |
| * | |
| * Returns: | |
| * This method returns a metafile handle if it is successful. | |
| * it will return 0 if not. | |
| */ | |
| static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry) | |
| { | |
| HRESULT hr; | |
| IStream *stm; | |
| hr = DataCacheEntry_OpenPresStream( cache_entry, &stm ); | |
| if (FAILED(hr)) return hr; | |
| switch (cache_entry->fmtetc.cfFormat) | |
| { | |
| case CF_METAFILEPICT: | |
| hr = load_mf_pict( cache_entry, stm ); | |
| break; | |
| case CF_DIB: | |
| hr = load_dib( cache_entry, stm ); | |
| break; | |
| default: | |
| FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat ); | |
| hr = E_NOTIMPL; | |
| } | |
| IStream_Release( stm ); | |
| return hr; | |
| } | |
| static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *cache_entry, | |
| IStorage *storage, IStream **stream) | |
| { | |
| WCHAR wszName[] = {2,'O','l','e','P','r','e','s', | |
| '0' + (cache_entry->stream_number / 100) % 10, | |
| '0' + (cache_entry->stream_number / 10) % 10, | |
| '0' + cache_entry->stream_number % 10, 0}; | |
| /* FIXME: cache the created stream in This? */ | |
| return IStorage_CreateStream(storage, wszName, | |
| STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, | |
| 0, 0, stream); | |
| } | |
| static HRESULT DataCacheEntry_Save(DataCacheEntry *cache_entry, IStorage *storage, | |
| BOOL same_as_load) | |
| { | |
| PresentationDataHeader header; | |
| HRESULT hr; | |
| IStream *pres_stream; | |
| void *data = NULL; | |
| TRACE("stream_number = %d, fmtetc = %s\n", cache_entry->stream_number, debugstr_formatetc(&cache_entry->fmtetc)); | |
| hr = DataCacheEntry_CreateStream(cache_entry, storage, &pres_stream); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = write_clipformat(pres_stream, cache_entry->data_cf); | |
| if (FAILED(hr)) | |
| return hr; | |
| if (cache_entry->fmtetc.ptd) | |
| FIXME("ptd not serialized\n"); | |
| header.unknown3 = 4; | |
| header.dvAspect = cache_entry->fmtetc.dwAspect; | |
| header.lindex = cache_entry->fmtetc.lindex; | |
| header.advf = cache_entry->advise_flags; | |
| header.unknown7 = 0; | |
| header.dwObjectExtentX = 0; | |
| header.dwObjectExtentY = 0; | |
| header.dwSize = 0; | |
| /* size the data */ | |
| switch (cache_entry->data_cf) | |
| { | |
| case CF_METAFILEPICT: | |
| { | |
| if (cache_entry->stgmedium.tymed != TYMED_NULL) | |
| { | |
| const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict); | |
| if (!mfpict) | |
| { | |
| IStream_Release(pres_stream); | |
| return DV_E_STGMEDIUM; | |
| } | |
| header.dwObjectExtentX = mfpict->xExt; | |
| header.dwObjectExtentY = mfpict->yExt; | |
| header.dwSize = GetMetaFileBitsEx(mfpict->hMF, 0, NULL); | |
| GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict); | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| /* | |
| * Write the header. | |
| */ | |
| hr = IStream_Write(pres_stream, &header, sizeof(PresentationDataHeader), | |
| NULL); | |
| if (FAILED(hr)) | |
| { | |
| IStream_Release(pres_stream); | |
| return hr; | |
| } | |
| /* get the data */ | |
| switch (cache_entry->data_cf) | |
| { | |
| case CF_METAFILEPICT: | |
| { | |
| if (cache_entry->stgmedium.tymed != TYMED_NULL) | |
| { | |
| const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict); | |
| if (!mfpict) | |
| { | |
| IStream_Release(pres_stream); | |
| return DV_E_STGMEDIUM; | |
| } | |
| data = HeapAlloc(GetProcessHeap(), 0, header.dwSize); | |
| GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data); | |
| GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict); | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| if (data) | |
| hr = IStream_Write(pres_stream, data, header.dwSize, NULL); | |
| HeapFree(GetProcessHeap(), 0, data); | |
| IStream_Release(pres_stream); | |
| return hr; | |
| } | |
| /* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL. | |
| * does no checking of whether src_stgm has a supported tymed, so this should be | |
| * done in the caller */ | |
| static HRESULT copy_stg_medium(CLIPFORMAT cf, STGMEDIUM *dest_stgm, | |
| const STGMEDIUM *src_stgm) | |
| { | |
| if (src_stgm->tymed == TYMED_MFPICT) | |
| { | |
| const METAFILEPICT *src_mfpict = GlobalLock(src_stgm->u.hMetaFilePict); | |
| METAFILEPICT *dest_mfpict; | |
| if (!src_mfpict) | |
| return DV_E_STGMEDIUM; | |
| dest_stgm->u.hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT)); | |
| dest_mfpict = GlobalLock(dest_stgm->u.hMetaFilePict); | |
| if (!dest_mfpict) | |
| { | |
| GlobalUnlock(src_stgm->u.hMetaFilePict); | |
| return E_OUTOFMEMORY; | |
| } | |
| *dest_mfpict = *src_mfpict; | |
| dest_mfpict->hMF = CopyMetaFileW(src_mfpict->hMF, NULL); | |
| GlobalUnlock(src_stgm->u.hMetaFilePict); | |
| GlobalUnlock(dest_stgm->u.hMetaFilePict); | |
| } | |
| else if (src_stgm->tymed != TYMED_NULL) | |
| { | |
| dest_stgm->u.hGlobal = OleDuplicateData(src_stgm->u.hGlobal, cf, | |
| GMEM_MOVEABLE); | |
| if (!dest_stgm->u.hGlobal) | |
| return E_OUTOFMEMORY; | |
| } | |
| dest_stgm->tymed = src_stgm->tymed; | |
| dest_stgm->pUnkForRelease = src_stgm->pUnkForRelease; | |
| if (dest_stgm->pUnkForRelease) | |
| IUnknown_AddRef(dest_stgm->pUnkForRelease); | |
| return S_OK; | |
| } | |
| static HGLOBAL synthesize_dib( HBITMAP bm ) | |
| { | |
| HDC hdc = GetDC( 0 ); | |
| BITMAPINFOHEADER header; | |
| BITMAPINFO *bmi; | |
| HGLOBAL ret = 0; | |
| DWORD header_size; | |
| memset( &header, 0, sizeof(header) ); | |
| header.biSize = sizeof(header); | |
| if (!GetDIBits( hdc, bm, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done; | |
| header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS ); | |
| if (!(ret = GlobalAlloc( GMEM_MOVEABLE, header_size + header.biSizeImage ))) goto done; | |
| bmi = GlobalLock( ret ); | |
| memset( bmi, 0, header_size ); | |
| memcpy( bmi, &header, header.biSize ); | |
| GetDIBits( hdc, bm, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS ); | |
| GlobalUnlock( ret ); | |
| done: | |
| ReleaseDC( 0, hdc ); | |
| return ret; | |
| } | |
| static HBITMAP synthesize_bitmap( HGLOBAL dib ) | |
| { | |
| HBITMAP ret = 0; | |
| BITMAPINFO *bmi; | |
| HDC hdc = GetDC( 0 ); | |
| if ((bmi = GlobalLock( dib ))) | |
| { | |
| /* FIXME: validate data size */ | |
| ret = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT, | |
| (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ), | |
| bmi, DIB_RGB_COLORS ); | |
| GlobalUnlock( dib ); | |
| } | |
| ReleaseDC( 0, hdc ); | |
| return ret; | |
| } | |
| static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry, | |
| const FORMATETC *formatetc, | |
| STGMEDIUM *stgmedium, | |
| BOOL fRelease) | |
| { | |
| STGMEDIUM dib_copy; | |
| if ((!cache_entry->fmtetc.cfFormat && !formatetc->cfFormat) || | |
| (cache_entry->fmtetc.tymed == TYMED_NULL && formatetc->tymed == TYMED_NULL) || | |
| stgmedium->tymed == TYMED_NULL) | |
| { | |
| WARN("invalid formatetc\n"); | |
| return DV_E_FORMATETC; | |
| } | |
| cache_entry->dirty = TRUE; | |
| ReleaseStgMedium(&cache_entry->stgmedium); | |
| cache_entry->data_cf = cache_entry->fmtetc.cfFormat ? cache_entry->fmtetc.cfFormat : formatetc->cfFormat; | |
| if (formatetc->cfFormat == CF_BITMAP) | |
| { | |
| dib_copy.tymed = TYMED_HGLOBAL; | |
| dib_copy.u.hGlobal = synthesize_dib( stgmedium->u.hBitmap ); | |
| dib_copy.pUnkForRelease = NULL; | |
| if (fRelease) ReleaseStgMedium(stgmedium); | |
| stgmedium = &dib_copy; | |
| fRelease = TRUE; | |
| } | |
| if (fRelease) | |
| { | |
| cache_entry->stgmedium = *stgmedium; | |
| return S_OK; | |
| } | |
| else | |
| return copy_stg_medium(cache_entry->data_cf, | |
| &cache_entry->stgmedium, stgmedium); | |
| } | |
| static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, FORMATETC *fmt, STGMEDIUM *stgmedium) | |
| { | |
| if (cache_entry->stgmedium.tymed == TYMED_NULL && cache_entry->stream) | |
| { | |
| HRESULT hr = DataCacheEntry_LoadData(cache_entry); | |
| if (FAILED(hr)) | |
| return hr; | |
| } | |
| if (cache_entry->stgmedium.tymed == TYMED_NULL) | |
| return OLE_E_BLANK; | |
| if (fmt->cfFormat == CF_BITMAP) | |
| { | |
| stgmedium->tymed = TYMED_GDI; | |
| stgmedium->u.hBitmap = synthesize_bitmap( cache_entry->stgmedium.u.hGlobal ); | |
| stgmedium->pUnkForRelease = NULL; | |
| return S_OK; | |
| } | |
| return copy_stg_medium(cache_entry->data_cf, stgmedium, &cache_entry->stgmedium); | |
| } | |
| static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry) | |
| { | |
| ReleaseStgMedium(&cache_entry->stgmedium); | |
| cache_entry->data_cf = cache_entry->fmtetc.cfFormat; | |
| return S_OK; | |
| } | |
| static inline void DataCacheEntry_HandsOffStorage(DataCacheEntry *cache_entry) | |
| { | |
| if (cache_entry->stream) | |
| { | |
| IStream_Release(cache_entry->stream); | |
| cache_entry->stream = NULL; | |
| } | |
| } | |
| static inline DWORD tymed_from_cf( DWORD cf ) | |
| { | |
| switch( cf ) | |
| { | |
| case CF_BITMAP: return TYMED_GDI; | |
| case CF_METAFILEPICT: return TYMED_MFPICT; | |
| case CF_ENHMETAFILE: return TYMED_ENHMF; | |
| case CF_DIB: | |
| default: return TYMED_HGLOBAL; | |
| } | |
| } | |
| /**************************************************************** | |
| * create_automatic_entry | |
| * | |
| * Creates an appropriate cache entry for one of the CLSID_Picture_ | |
| * classes. The connection id of the entry is one. Any pre-existing | |
| * automatic entry is re-assigned a new connection id, and moved to | |
| * the end of the list. | |
| */ | |
| static HRESULT create_automatic_entry(DataCache *cache, const CLSID *clsid) | |
| { | |
| static const struct data | |
| { | |
| const CLSID *clsid; | |
| FORMATETC fmt; | |
| } data[] = | |
| { | |
| { &CLSID_Picture_Dib, { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } }, | |
| { &CLSID_Picture_Metafile, { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT } }, | |
| { &CLSID_Picture_EnhMetafile, { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF } }, | |
| { NULL } | |
| }; | |
| const struct data *ptr = data; | |
| struct list *head; | |
| DataCacheEntry *entry; | |
| if (IsEqualCLSID( &cache->clsid, clsid )) return S_OK; | |
| /* move and reassign any pre-existing automatic entry */ | |
| if ((head = list_head( &cache->cache_list ))) | |
| { | |
| entry = LIST_ENTRY( head, DataCacheEntry, entry ); | |
| if (entry->id == 1) | |
| { | |
| list_remove( &entry->entry ); | |
| entry->id = cache->last_cache_id++; | |
| list_add_tail( &cache->cache_list, &entry->entry ); | |
| } | |
| } | |
| while (ptr->clsid) | |
| { | |
| if (IsEqualCLSID( clsid, ptr->clsid )) | |
| return DataCache_CreateEntry( cache, &ptr->fmt, 0, TRUE, NULL ); | |
| ptr++; | |
| } | |
| return S_OK; | |
| } | |
| /********************************************************* | |
| * Method implementation for the non delegating IUnknown | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_NDIUnknown_QueryInterface (IUnknown) | |
| * | |
| * This version of QueryInterface will not delegate its implementation | |
| * to the outer unknown. | |
| */ | |
| static HRESULT WINAPI DataCache_NDIUnknown_QueryInterface( | |
| IUnknown* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IUnknown(iface); | |
| if ( ppvObject==0 ) | |
| return E_INVALIDARG; | |
| *ppvObject = 0; | |
| if (IsEqualIID(&IID_IUnknown, riid)) | |
| { | |
| if (this->outer_unk == iface) /* non-aggregated, return IUnknown from IOleCache2 */ | |
| *ppvObject = &this->IOleCache2_iface; | |
| else | |
| *ppvObject = iface; | |
| } | |
| else if (IsEqualIID(&IID_IDataObject, riid)) | |
| { | |
| *ppvObject = &this->IDataObject_iface; | |
| } | |
| else if ( IsEqualIID(&IID_IPersistStorage, riid) || | |
| IsEqualIID(&IID_IPersist, riid) ) | |
| { | |
| *ppvObject = &this->IPersistStorage_iface; | |
| } | |
| else if ( IsEqualIID(&IID_IViewObject, riid) || | |
| IsEqualIID(&IID_IViewObject2, riid) ) | |
| { | |
| *ppvObject = &this->IViewObject2_iface; | |
| } | |
| else if ( IsEqualIID(&IID_IOleCache, riid) || | |
| IsEqualIID(&IID_IOleCache2, riid) ) | |
| { | |
| *ppvObject = &this->IOleCache2_iface; | |
| } | |
| else if ( IsEqualIID(&IID_IOleCacheControl, riid) ) | |
| { | |
| *ppvObject = &this->IOleCacheControl_iface; | |
| } | |
| if ((*ppvObject)==0) | |
| { | |
| WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| IUnknown_AddRef((IUnknown*)*ppvObject); | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_NDIUnknown_AddRef (IUnknown) | |
| * | |
| * This version of QueryInterface will not delegate its implementation | |
| * to the outer unknown. | |
| */ | |
| static ULONG WINAPI DataCache_NDIUnknown_AddRef( | |
| IUnknown* iface) | |
| { | |
| DataCache *this = impl_from_IUnknown(iface); | |
| return InterlockedIncrement(&this->ref); | |
| } | |
| /************************************************************************ | |
| * DataCache_NDIUnknown_Release (IUnknown) | |
| * | |
| * This version of QueryInterface will not delegate its implementation | |
| * to the outer unknown. | |
| */ | |
| static ULONG WINAPI DataCache_NDIUnknown_Release( | |
| IUnknown* iface) | |
| { | |
| DataCache *this = impl_from_IUnknown(iface); | |
| ULONG ref; | |
| ref = InterlockedDecrement(&this->ref); | |
| if (ref == 0) DataCache_Destroy(this); | |
| return ref; | |
| } | |
| /********************************************************* | |
| * Method implementation for the IDataObject | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_IDataObject_QueryInterface (IUnknown) | |
| */ | |
| static HRESULT WINAPI DataCache_IDataObject_QueryInterface( | |
| IDataObject* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IDataObject(iface); | |
| return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject); | |
| } | |
| /************************************************************************ | |
| * DataCache_IDataObject_AddRef (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IDataObject_AddRef( | |
| IDataObject* iface) | |
| { | |
| DataCache *this = impl_from_IDataObject(iface); | |
| return IUnknown_AddRef(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_IDataObject_Release (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IDataObject_Release( | |
| IDataObject* iface) | |
| { | |
| DataCache *this = impl_from_IDataObject(iface); | |
| return IUnknown_Release(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_GetData | |
| * | |
| * Get Data from a source dataobject using format pformatetcIn->cfFormat | |
| */ | |
| static HRESULT WINAPI DataCache_GetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetcIn, | |
| STGMEDIUM* pmedium) | |
| { | |
| DataCache *This = impl_from_IDataObject(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p, %s, %p)\n", iface, debugstr_formatetc(pformatetcIn), pmedium); | |
| memset(pmedium, 0, sizeof(*pmedium)); | |
| cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetcIn); | |
| if (!cache_entry) | |
| return OLE_E_BLANK; | |
| return DataCacheEntry_GetData(cache_entry, pformatetcIn, pmedium); | |
| } | |
| static HRESULT WINAPI DataCache_GetDataHere( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium) | |
| { | |
| FIXME("stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt ) | |
| { | |
| DataCache *This = impl_from_IDataObject( iface ); | |
| DataCacheEntry *cache_entry; | |
| TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) ); | |
| cache_entry = DataCache_GetEntryForFormatEtc( This, fmt ); | |
| return cache_entry ? S_OK : S_FALSE; | |
| } | |
| /************************************************************************ | |
| * DataCache_EnumFormatEtc (IDataObject) | |
| * | |
| * The data cache doesn't implement this method. | |
| */ | |
| static HRESULT WINAPI DataCache_GetCanonicalFormatEtc( | |
| IDataObject* iface, | |
| LPFORMATETC pformatectIn, | |
| LPFORMATETC pformatetcOut) | |
| { | |
| TRACE("()\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * DataCache_IDataObject_SetData (IDataObject) | |
| * | |
| * This method is delegated to the IOleCache2 implementation. | |
| */ | |
| static HRESULT WINAPI DataCache_IDataObject_SetData( | |
| IDataObject* iface, | |
| LPFORMATETC pformatetc, | |
| STGMEDIUM* pmedium, | |
| BOOL fRelease) | |
| { | |
| IOleCache2* oleCache = NULL; | |
| HRESULT hres; | |
| TRACE("(%p, %p, %p, %d)\n", iface, pformatetc, pmedium, fRelease); | |
| hres = IDataObject_QueryInterface(iface, &IID_IOleCache2, (void**)&oleCache); | |
| if (FAILED(hres)) | |
| return E_UNEXPECTED; | |
| hres = IOleCache2_SetData(oleCache, pformatetc, pmedium, fRelease); | |
| IOleCache2_Release(oleCache); | |
| return hres; | |
| } | |
| /************************************************************************ | |
| * DataCache_EnumFormatEtc (IDataObject) | |
| * | |
| * The data cache doesn't implement this method. | |
| */ | |
| static HRESULT WINAPI DataCache_EnumFormatEtc( | |
| IDataObject* iface, | |
| DWORD dwDirection, | |
| IEnumFORMATETC** ppenumFormatEtc) | |
| { | |
| TRACE("()\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * DataCache_DAdvise (IDataObject) | |
| * | |
| * The data cache doesn't support connections. | |
| */ | |
| static HRESULT WINAPI DataCache_DAdvise( | |
| IDataObject* iface, | |
| FORMATETC* pformatetc, | |
| DWORD advf, | |
| IAdviseSink* pAdvSink, | |
| DWORD* pdwConnection) | |
| { | |
| TRACE("()\n"); | |
| return OLE_E_ADVISENOTSUPPORTED; | |
| } | |
| /************************************************************************ | |
| * DataCache_DUnadvise (IDataObject) | |
| * | |
| * The data cache doesn't support connections. | |
| */ | |
| static HRESULT WINAPI DataCache_DUnadvise( | |
| IDataObject* iface, | |
| DWORD dwConnection) | |
| { | |
| TRACE("()\n"); | |
| return OLE_E_NOCONNECTION; | |
| } | |
| /************************************************************************ | |
| * DataCache_EnumDAdvise (IDataObject) | |
| * | |
| * The data cache doesn't support connections. | |
| */ | |
| static HRESULT WINAPI DataCache_EnumDAdvise( | |
| IDataObject* iface, | |
| IEnumSTATDATA** ppenumAdvise) | |
| { | |
| TRACE("()\n"); | |
| return OLE_E_ADVISENOTSUPPORTED; | |
| } | |
| /********************************************************* | |
| * Method implementation for the IDataObject | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_IPersistStorage_QueryInterface (IUnknown) | |
| */ | |
| static HRESULT WINAPI DataCache_IPersistStorage_QueryInterface( | |
| IPersistStorage* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IPersistStorage(iface); | |
| return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject); | |
| } | |
| /************************************************************************ | |
| * DataCache_IPersistStorage_AddRef (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IPersistStorage_AddRef( | |
| IPersistStorage* iface) | |
| { | |
| DataCache *this = impl_from_IPersistStorage(iface); | |
| return IUnknown_AddRef(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_IPersistStorage_Release (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IPersistStorage_Release( | |
| IPersistStorage* iface) | |
| { | |
| DataCache *this = impl_from_IPersistStorage(iface); | |
| return IUnknown_Release(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_GetClassID (IPersistStorage) | |
| * | |
| */ | |
| static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid) | |
| { | |
| DataCache *This = impl_from_IPersistStorage( iface ); | |
| TRACE( "(%p, %p) returning %s\n", iface, clsid, debugstr_guid(&This->clsid) ); | |
| *clsid = This->clsid; | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_IsDirty (IPersistStorage) | |
| */ | |
| static HRESULT WINAPI DataCache_IsDirty( | |
| IPersistStorage* iface) | |
| { | |
| DataCache *This = impl_from_IPersistStorage(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p)\n", iface); | |
| if (This->dirty) | |
| return S_OK; | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| if (cache_entry->dirty) | |
| return S_OK; | |
| return S_FALSE; | |
| } | |
| /************************************************************************ | |
| * DataCache_InitNew (IPersistStorage) | |
| * | |
| * The data cache implementation of IPersistStorage_InitNew simply stores | |
| * the storage pointer. | |
| */ | |
| static HRESULT WINAPI DataCache_InitNew( | |
| IPersistStorage* iface, | |
| IStorage* pStg) | |
| { | |
| DataCache *This = impl_from_IPersistStorage(iface); | |
| CLSID clsid; | |
| HRESULT hr; | |
| TRACE("(%p, %p)\n", iface, pStg); | |
| if (This->presentationStorage != NULL) | |
| return CO_E_ALREADYINITIALIZED; | |
| This->presentationStorage = pStg; | |
| IStorage_AddRef(This->presentationStorage); | |
| This->dirty = TRUE; | |
| ReadClassStg( pStg, &clsid ); | |
| hr = create_automatic_entry( This, &clsid ); | |
| if (FAILED(hr)) | |
| { | |
| IStorage_Release( pStg ); | |
| This->presentationStorage = NULL; | |
| return hr; | |
| } | |
| This->clsid = clsid; | |
| return S_OK; | |
| } | |
| static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, DWORD advf, IStream *stm, | |
| enum stream_type type ) | |
| { | |
| DataCacheEntry *cache_entry; | |
| HRESULT hr = S_OK; | |
| TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) ); | |
| cache_entry = DataCache_GetEntryForFormatEtc( This, fmt ); | |
| if (!cache_entry) | |
| hr = DataCache_CreateEntry( This, fmt, advf, FALSE, &cache_entry ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| DataCacheEntry_DiscardData( cache_entry ); | |
| if (cache_entry->stream) IStream_Release( cache_entry->stream ); | |
| cache_entry->stream = stm; | |
| IStream_AddRef( stm ); | |
| cache_entry->stream_type = type; | |
| cache_entry->dirty = FALSE; | |
| } | |
| return hr; | |
| } | |
| static HRESULT parse_pres_streams( DataCache *This, IStorage *stg ) | |
| { | |
| HRESULT hr; | |
| IEnumSTATSTG *stat_enum; | |
| STATSTG stat; | |
| IStream *stm; | |
| PresentationDataHeader header; | |
| ULONG actual_read; | |
| CLIPFORMAT clipformat; | |
| FORMATETC fmtetc; | |
| hr = IStorage_EnumElements( stg, 0, NULL, 0, &stat_enum ); | |
| if (FAILED( hr )) return hr; | |
| while ((hr = IEnumSTATSTG_Next( stat_enum, 1, &stat, NULL )) == S_OK) | |
| { | |
| if (DataCache_IsPresentationStream( &stat )) | |
| { | |
| hr = IStorage_OpenStream( stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, | |
| 0, &stm ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| hr = read_clipformat( stm, &clipformat ); | |
| if (hr == S_OK) | |
| hr = IStream_Read( stm, &header, sizeof(header), &actual_read ); | |
| if (hr == S_OK && actual_read == sizeof(header)) | |
| { | |
| fmtetc.cfFormat = clipformat; | |
| fmtetc.ptd = NULL; /* FIXME */ | |
| fmtetc.dwAspect = header.dvAspect; | |
| fmtetc.lindex = header.lindex; | |
| fmtetc.tymed = tymed_from_cf( clipformat ); | |
| add_cache_entry( This, &fmtetc, header.advf, stm, pres_stream ); | |
| } | |
| IStream_Release( stm ); | |
| } | |
| } | |
| CoTaskMemFree( stat.pwcsName ); | |
| } | |
| IEnumSTATSTG_Release( stat_enum ); | |
| return S_OK; | |
| } | |
| static const FORMATETC static_dib_fmt = { CF_DIB, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | |
| static HRESULT parse_contents_stream( DataCache *This, IStorage *stg, IStream *stm ) | |
| { | |
| HRESULT hr; | |
| STATSTG stat; | |
| const FORMATETC *fmt; | |
| hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); | |
| if (FAILED( hr )) return hr; | |
| if (IsEqualCLSID( &stat.clsid, &CLSID_Picture_Dib )) | |
| fmt = &static_dib_fmt; | |
| else | |
| { | |
| FIXME("unsupported format %s\n", debugstr_guid( &stat.clsid )); | |
| return E_FAIL; | |
| } | |
| return add_cache_entry( This, fmt, 0, stm, contents_stream ); | |
| } | |
| static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0}; | |
| /************************************************************************ | |
| * DataCache_Load (IPersistStorage) | |
| * | |
| * The data cache implementation of IPersistStorage_Load doesn't | |
| * actually load anything. Instead, it holds on to the storage pointer | |
| * and it will load the presentation information when the | |
| * IDataObject_GetData or IViewObject2_Draw methods are called. | |
| */ | |
| static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *pStg ) | |
| { | |
| DataCache *This = impl_from_IPersistStorage(iface); | |
| HRESULT hr; | |
| IStream *stm; | |
| CLSID clsid; | |
| DataCacheEntry *entry, *cursor2; | |
| TRACE("(%p, %p)\n", iface, pStg); | |
| IPersistStorage_HandsOffStorage( iface ); | |
| LIST_FOR_EACH_ENTRY_SAFE( entry, cursor2, &This->cache_list, DataCacheEntry, entry ) | |
| DataCacheEntry_Destroy( This, entry ); | |
| ReadClassStg( pStg, &clsid ); | |
| hr = create_automatic_entry( This, &clsid ); | |
| if (FAILED( hr )) return hr; | |
| This->clsid = clsid; | |
| hr = IStorage_OpenStream( pStg, CONTENTS, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, | |
| 0, &stm ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| hr = parse_contents_stream( This, pStg, stm ); | |
| IStream_Release( stm ); | |
| } | |
| if (FAILED(hr)) | |
| hr = parse_pres_streams( This, pStg ); | |
| if (SUCCEEDED( hr )) | |
| { | |
| This->dirty = FALSE; | |
| This->presentationStorage = pStg; | |
| IStorage_AddRef( This->presentationStorage ); | |
| } | |
| return hr; | |
| } | |
| /************************************************************************ | |
| * DataCache_Save (IPersistStorage) | |
| * | |
| * Until we actually connect to a running object and retrieve new | |
| * information to it, we never have to save anything. However, it is | |
| * our responsibility to copy the information when saving to a new | |
| * storage. | |
| */ | |
| static HRESULT WINAPI DataCache_Save( | |
| IPersistStorage* iface, | |
| IStorage* pStg, | |
| BOOL fSameAsLoad) | |
| { | |
| DataCache *This = impl_from_IPersistStorage(iface); | |
| DataCacheEntry *cache_entry; | |
| BOOL dirty = FALSE; | |
| HRESULT hr = S_OK; | |
| unsigned short stream_number = 0; | |
| TRACE("(%p, %p, %d)\n", iface, pStg, fSameAsLoad); | |
| dirty = This->dirty; | |
| if (!dirty) | |
| { | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| dirty = cache_entry->dirty; | |
| if (dirty) | |
| break; | |
| } | |
| } | |
| /* this is a shortcut if nothing changed */ | |
| if (!dirty && !fSameAsLoad && This->presentationStorage) | |
| { | |
| return IStorage_CopyTo(This->presentationStorage, 0, NULL, NULL, pStg); | |
| } | |
| /* assign stream numbers to the cache entries */ | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| if (cache_entry->stream_number != stream_number) | |
| { | |
| cache_entry->dirty = TRUE; /* needs to be written out again */ | |
| cache_entry->stream_number = stream_number; | |
| } | |
| stream_number++; | |
| } | |
| /* write out the cache entries */ | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| if (!fSameAsLoad || cache_entry->dirty) | |
| { | |
| hr = DataCacheEntry_Save(cache_entry, pStg, fSameAsLoad); | |
| if (FAILED(hr)) | |
| break; | |
| cache_entry->dirty = FALSE; | |
| } | |
| } | |
| This->dirty = FALSE; | |
| return hr; | |
| } | |
| /************************************************************************ | |
| * DataCache_SaveCompleted (IPersistStorage) | |
| * | |
| * This method is called to tell the cache to release the storage | |
| * pointer it's currently holding. | |
| */ | |
| static HRESULT WINAPI DataCache_SaveCompleted( | |
| IPersistStorage* iface, | |
| IStorage* pStgNew) | |
| { | |
| TRACE("(%p, %p)\n", iface, pStgNew); | |
| if (pStgNew) | |
| { | |
| IPersistStorage_HandsOffStorage(iface); | |
| DataCache_Load(iface, pStgNew); | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_HandsOffStorage (IPersistStorage) | |
| * | |
| * This method is called to tell the cache to release the storage | |
| * pointer it's currently holding. | |
| */ | |
| static HRESULT WINAPI DataCache_HandsOffStorage( | |
| IPersistStorage* iface) | |
| { | |
| DataCache *this = impl_from_IPersistStorage(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p)\n", iface); | |
| if (this->presentationStorage != NULL) | |
| { | |
| IStorage_Release(this->presentationStorage); | |
| this->presentationStorage = NULL; | |
| } | |
| LIST_FOR_EACH_ENTRY(cache_entry, &this->cache_list, DataCacheEntry, entry) | |
| DataCacheEntry_HandsOffStorage(cache_entry); | |
| return S_OK; | |
| } | |
| /********************************************************* | |
| * Method implementation for the IViewObject2 | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_IViewObject2_QueryInterface (IUnknown) | |
| */ | |
| static HRESULT WINAPI DataCache_IViewObject2_QueryInterface( | |
| IViewObject2* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IViewObject2(iface); | |
| return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject); | |
| } | |
| /************************************************************************ | |
| * DataCache_IViewObject2_AddRef (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IViewObject2_AddRef( | |
| IViewObject2* iface) | |
| { | |
| DataCache *this = impl_from_IViewObject2(iface); | |
| return IUnknown_AddRef(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_IViewObject2_Release (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IViewObject2_Release( | |
| IViewObject2* iface) | |
| { | |
| DataCache *this = impl_from_IViewObject2(iface); | |
| return IUnknown_Release(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_Draw (IViewObject2) | |
| * | |
| * This method will draw the cached representation of the object | |
| * to the given device context. | |
| */ | |
| static HRESULT WINAPI DataCache_Draw( | |
| IViewObject2* iface, | |
| DWORD dwDrawAspect, | |
| LONG lindex, | |
| void* pvAspect, | |
| DVTARGETDEVICE* ptd, | |
| HDC hdcTargetDev, | |
| HDC hdcDraw, | |
| LPCRECTL lprcBounds, | |
| LPCRECTL lprcWBounds, | |
| BOOL (CALLBACK *pfnContinue)(ULONG_PTR dwContinue), | |
| ULONG_PTR dwContinue) | |
| { | |
| DataCache *This = impl_from_IViewObject2(iface); | |
| HRESULT hres; | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p, %x, %d, %p, %p, %p, %p, %p, %p, %lx)\n", | |
| iface, | |
| dwDrawAspect, | |
| lindex, | |
| pvAspect, | |
| hdcTargetDev, | |
| hdcDraw, | |
| lprcBounds, | |
| lprcWBounds, | |
| pfnContinue, | |
| dwContinue); | |
| if (lprcBounds==NULL) | |
| return E_INVALIDARG; | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| /* FIXME: compare ptd too */ | |
| if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) || | |
| (cache_entry->fmtetc.lindex != lindex)) | |
| continue; | |
| /* if the data hasn't been loaded yet, do it now */ | |
| if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream) | |
| { | |
| hres = DataCacheEntry_LoadData(cache_entry); | |
| if (FAILED(hres)) | |
| continue; | |
| } | |
| /* no data */ | |
| if (cache_entry->stgmedium.tymed == TYMED_NULL) | |
| continue; | |
| if (pfnContinue && !pfnContinue(dwContinue)) return E_ABORT; | |
| switch (cache_entry->data_cf) | |
| { | |
| case CF_METAFILEPICT: | |
| { | |
| /* | |
| * We have to be careful not to modify the state of the | |
| * DC. | |
| */ | |
| INT prevMapMode; | |
| SIZE oldWindowExt; | |
| SIZE oldViewportExt; | |
| POINT oldViewportOrg; | |
| METAFILEPICT *mfpict; | |
| if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) || | |
| !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict)))) | |
| continue; | |
| prevMapMode = SetMapMode(hdcDraw, mfpict->mm); | |
| SetWindowExtEx(hdcDraw, | |
| mfpict->xExt, | |
| mfpict->yExt, | |
| &oldWindowExt); | |
| SetViewportExtEx(hdcDraw, | |
| lprcBounds->right - lprcBounds->left, | |
| lprcBounds->bottom - lprcBounds->top, | |
| &oldViewportExt); | |
| SetViewportOrgEx(hdcDraw, | |
| lprcBounds->left, | |
| lprcBounds->top, | |
| &oldViewportOrg); | |
| PlayMetaFile(hdcDraw, mfpict->hMF); | |
| SetWindowExtEx(hdcDraw, | |
| oldWindowExt.cx, | |
| oldWindowExt.cy, | |
| NULL); | |
| SetViewportExtEx(hdcDraw, | |
| oldViewportExt.cx, | |
| oldViewportExt.cy, | |
| NULL); | |
| SetViewportOrgEx(hdcDraw, | |
| oldViewportOrg.x, | |
| oldViewportOrg.y, | |
| NULL); | |
| SetMapMode(hdcDraw, prevMapMode); | |
| GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict); | |
| return S_OK; | |
| } | |
| case CF_DIB: | |
| { | |
| BITMAPINFO *info; | |
| BYTE *bits; | |
| if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) || | |
| !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal )))) | |
| continue; | |
| bits = (BYTE *) info + bitmap_info_size( info, DIB_RGB_COLORS ); | |
| StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top, | |
| lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top, | |
| 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight, | |
| bits, info, DIB_RGB_COLORS, SRCCOPY ); | |
| GlobalUnlock( cache_entry->stgmedium.u.hGlobal ); | |
| return S_OK; | |
| } | |
| } | |
| } | |
| WARN("no data could be found to be drawn\n"); | |
| return OLE_E_BLANK; | |
| } | |
| static HRESULT WINAPI DataCache_GetColorSet( | |
| IViewObject2* iface, | |
| DWORD dwDrawAspect, | |
| LONG lindex, | |
| void* pvAspect, | |
| DVTARGETDEVICE* ptd, | |
| HDC hicTargetDevice, | |
| LOGPALETTE** ppColorSet) | |
| { | |
| FIXME("stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DataCache_Freeze( | |
| IViewObject2* iface, | |
| DWORD dwDrawAspect, | |
| LONG lindex, | |
| void* pvAspect, | |
| DWORD* pdwFreeze) | |
| { | |
| FIXME("stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DataCache_Unfreeze( | |
| IViewObject2* iface, | |
| DWORD dwFreeze) | |
| { | |
| FIXME("stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| /************************************************************************ | |
| * DataCache_SetAdvise (IViewObject2) | |
| * | |
| * This sets-up an advisory sink with the data cache. When the object's | |
| * view changes, this sink is called. | |
| */ | |
| static HRESULT WINAPI DataCache_SetAdvise( | |
| IViewObject2* iface, | |
| DWORD aspects, | |
| DWORD advf, | |
| IAdviseSink* pAdvSink) | |
| { | |
| DataCache *this = impl_from_IViewObject2(iface); | |
| TRACE("(%p, %x, %x, %p)\n", iface, aspects, advf, pAdvSink); | |
| /* | |
| * A call to this function removes the previous sink | |
| */ | |
| if (this->sinkInterface != NULL) | |
| { | |
| IAdviseSink_Release(this->sinkInterface); | |
| this->sinkInterface = NULL; | |
| this->sinkAspects = 0; | |
| this->sinkAdviseFlag = 0; | |
| } | |
| /* | |
| * Now, setup the new one. | |
| */ | |
| if (pAdvSink!=NULL) | |
| { | |
| this->sinkInterface = pAdvSink; | |
| this->sinkAspects = aspects; | |
| this->sinkAdviseFlag = advf; | |
| IAdviseSink_AddRef(this->sinkInterface); | |
| } | |
| /* | |
| * When the ADVF_PRIMEFIRST flag is set, we have to advise the | |
| * sink immediately. | |
| */ | |
| if (advf & ADVF_PRIMEFIRST) | |
| { | |
| DataCache_FireOnViewChange(this, aspects, -1); | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_GetAdvise (IViewObject2) | |
| * | |
| * This method queries the current state of the advise sink | |
| * installed on the data cache. | |
| */ | |
| static HRESULT WINAPI DataCache_GetAdvise( | |
| IViewObject2* iface, | |
| DWORD* pAspects, | |
| DWORD* pAdvf, | |
| IAdviseSink** ppAdvSink) | |
| { | |
| DataCache *this = impl_from_IViewObject2(iface); | |
| TRACE("(%p, %p, %p, %p)\n", iface, pAspects, pAdvf, ppAdvSink); | |
| /* | |
| * Just copy all the requested values. | |
| */ | |
| if (pAspects!=NULL) | |
| *pAspects = this->sinkAspects; | |
| if (pAdvf!=NULL) | |
| *pAdvf = this->sinkAdviseFlag; | |
| if (ppAdvSink!=NULL) | |
| { | |
| if (this->sinkInterface != NULL) | |
| IAdviseSink_QueryInterface(this->sinkInterface, | |
| &IID_IAdviseSink, | |
| (void**)ppAdvSink); | |
| else *ppAdvSink = NULL; | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_GetExtent (IViewObject2) | |
| * | |
| * This method retrieves the "natural" size of this cached object. | |
| */ | |
| static HRESULT WINAPI DataCache_GetExtent( | |
| IViewObject2* iface, | |
| DWORD dwDrawAspect, | |
| LONG lindex, | |
| DVTARGETDEVICE* ptd, | |
| LPSIZEL lpsizel) | |
| { | |
| DataCache *This = impl_from_IViewObject2(iface); | |
| HRESULT hres = E_FAIL; | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p, %x, %d, %p, %p)\n", | |
| iface, dwDrawAspect, lindex, ptd, lpsizel); | |
| if (lpsizel==NULL) | |
| return E_POINTER; | |
| lpsizel->cx = 0; | |
| lpsizel->cy = 0; | |
| if (lindex!=-1) | |
| FIXME("Unimplemented flag lindex = %d\n", lindex); | |
| /* | |
| * Right now, we support only the callback from | |
| * the default handler. | |
| */ | |
| if (ptd!=NULL) | |
| FIXME("Unimplemented ptd = %p\n", ptd); | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| /* FIXME: compare ptd too */ | |
| if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) || | |
| (cache_entry->fmtetc.lindex != lindex)) | |
| continue; | |
| /* if the data hasn't been loaded yet, do it now */ | |
| if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream) | |
| { | |
| hres = DataCacheEntry_LoadData(cache_entry); | |
| if (FAILED(hres)) | |
| continue; | |
| } | |
| /* no data */ | |
| if (cache_entry->stgmedium.tymed == TYMED_NULL) | |
| continue; | |
| switch (cache_entry->data_cf) | |
| { | |
| case CF_METAFILEPICT: | |
| { | |
| METAFILEPICT *mfpict; | |
| if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) || | |
| !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict)))) | |
| continue; | |
| lpsizel->cx = mfpict->xExt; | |
| lpsizel->cy = mfpict->yExt; | |
| GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict); | |
| return S_OK; | |
| } | |
| case CF_DIB: | |
| { | |
| BITMAPINFOHEADER *info; | |
| LONG x_pels_m, y_pels_m; | |
| if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) || | |
| !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal )))) | |
| continue; | |
| x_pels_m = info->biXPelsPerMeter; | |
| y_pels_m = info->biYPelsPerMeter; | |
| /* Size in units of 0.01mm (ie. MM_HIMETRIC) */ | |
| if (x_pels_m != 0 && y_pels_m != 0) | |
| { | |
| lpsizel->cx = info->biWidth * 100000 / x_pels_m; | |
| lpsizel->cy = info->biHeight * 100000 / y_pels_m; | |
| } | |
| else | |
| { | |
| HDC hdc = GetDC( 0 ); | |
| lpsizel->cx = info->biWidth * 2540 / GetDeviceCaps( hdc, LOGPIXELSX ); | |
| lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY ); | |
| ReleaseDC( 0, hdc ); | |
| } | |
| GlobalUnlock( cache_entry->stgmedium.u.hGlobal ); | |
| return S_OK; | |
| } | |
| } | |
| } | |
| WARN("no data could be found to get the extents from\n"); | |
| /* | |
| * This method returns OLE_E_BLANK when it fails. | |
| */ | |
| return OLE_E_BLANK; | |
| } | |
| /********************************************************* | |
| * Method implementation for the IOleCache2 | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_IOleCache2_QueryInterface (IUnknown) | |
| */ | |
| static HRESULT WINAPI DataCache_IOleCache2_QueryInterface( | |
| IOleCache2* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IOleCache2(iface); | |
| return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject); | |
| } | |
| /************************************************************************ | |
| * DataCache_IOleCache2_AddRef (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IOleCache2_AddRef( | |
| IOleCache2* iface) | |
| { | |
| DataCache *this = impl_from_IOleCache2(iface); | |
| return IUnknown_AddRef(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_IOleCache2_Release (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IOleCache2_Release( | |
| IOleCache2* iface) | |
| { | |
| DataCache *this = impl_from_IOleCache2(iface); | |
| return IUnknown_Release(this->outer_unk); | |
| } | |
| /***************************************************************************** | |
| * setup_sink | |
| * | |
| * Set up the sink connection to the running object. | |
| */ | |
| static HRESULT setup_sink(DataCache *This, DataCacheEntry *cache_entry) | |
| { | |
| HRESULT hr = S_FALSE; | |
| DWORD flags; | |
| /* Clear the ADVFCACHE_* bits. Native also sets the two highest bits for some reason. */ | |
| flags = cache_entry->advise_flags & ~(ADVFCACHE_NOHANDLER | ADVFCACHE_FORCEBUILTIN | ADVFCACHE_ONSAVE); | |
| if(This->running_object) | |
| if(!(flags & ADVF_NODATA)) | |
| hr = IDataObject_DAdvise(This->running_object, &cache_entry->fmtetc, flags, | |
| &This->IAdviseSink_iface, &cache_entry->sink_id); | |
| return hr; | |
| } | |
| static HRESULT WINAPI DataCache_Cache( | |
| IOleCache2* iface, | |
| FORMATETC* pformatetc, | |
| DWORD advf, | |
| DWORD* pdwConnection) | |
| { | |
| DataCache *This = impl_from_IOleCache2(iface); | |
| DataCacheEntry *cache_entry; | |
| HRESULT hr; | |
| FORMATETC fmt_cpy; | |
| TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection); | |
| if (!pformatetc || !pdwConnection) | |
| return E_INVALIDARG; | |
| TRACE("pformatetc = %s\n", debugstr_formatetc(pformatetc)); | |
| fmt_cpy = *pformatetc; /* No need for a deep copy */ | |
| if (fmt_cpy.cfFormat == CF_BITMAP && fmt_cpy.tymed == TYMED_GDI) | |
| { | |
| fmt_cpy.cfFormat = CF_DIB; | |
| fmt_cpy.tymed = TYMED_HGLOBAL; | |
| } | |
| *pdwConnection = 0; | |
| cache_entry = DataCache_GetEntryForFormatEtc(This, &fmt_cpy); | |
| if (cache_entry) | |
| { | |
| TRACE("found an existing cache entry\n"); | |
| *pdwConnection = cache_entry->id; | |
| return CACHE_S_SAMECACHE; | |
| } | |
| hr = DataCache_CreateEntry(This, &fmt_cpy, advf, FALSE, &cache_entry); | |
| if (SUCCEEDED(hr)) | |
| { | |
| *pdwConnection = cache_entry->id; | |
| setup_sink(This, cache_entry); | |
| } | |
| return hr; | |
| } | |
| static HRESULT WINAPI DataCache_Uncache( | |
| IOleCache2* iface, | |
| DWORD dwConnection) | |
| { | |
| DataCache *This = impl_from_IOleCache2(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%d)\n", dwConnection); | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| if (cache_entry->id == dwConnection) | |
| { | |
| DataCacheEntry_Destroy(This, cache_entry); | |
| return S_OK; | |
| } | |
| WARN("no connection found for %d\n", dwConnection); | |
| return OLE_E_NOCONNECTION; | |
| } | |
| static HRESULT WINAPI DataCache_EnumCache(IOleCache2 *iface, | |
| IEnumSTATDATA **enum_stat) | |
| { | |
| DataCache *This = impl_from_IOleCache2( iface ); | |
| DataCacheEntry *cache_entry; | |
| int i = 0, count = 0; | |
| STATDATA *data; | |
| HRESULT hr; | |
| TRACE( "(%p, %p)\n", This, enum_stat ); | |
| LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry ) | |
| { | |
| count++; | |
| if (cache_entry->fmtetc.cfFormat == CF_DIB) | |
| count++; | |
| } | |
| data = CoTaskMemAlloc( count * sizeof(*data) ); | |
| if (!data) return E_OUTOFMEMORY; | |
| LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry ) | |
| { | |
| if (i == count) goto fail; | |
| hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc ); | |
| if (FAILED(hr)) goto fail; | |
| data[i].advf = cache_entry->advise_flags; | |
| data[i].pAdvSink = NULL; | |
| data[i].dwConnection = cache_entry->id; | |
| i++; | |
| if (cache_entry->fmtetc.cfFormat == CF_DIB) | |
| { | |
| if (i == count) goto fail; | |
| hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc ); | |
| if (FAILED(hr)) goto fail; | |
| data[i].formatetc.cfFormat = CF_BITMAP; | |
| data[i].formatetc.tymed = TYMED_GDI; | |
| data[i].advf = cache_entry->advise_flags; | |
| data[i].pAdvSink = NULL; | |
| data[i].dwConnection = cache_entry->id; | |
| i++; | |
| } | |
| } | |
| hr = EnumSTATDATA_Construct( NULL, 0, i, data, FALSE, enum_stat ); | |
| if (SUCCEEDED(hr)) return hr; | |
| fail: | |
| while (i--) CoTaskMemFree( data[i].formatetc.ptd ); | |
| CoTaskMemFree( data ); | |
| return hr; | |
| } | |
| static HRESULT WINAPI DataCache_InitCache( | |
| IOleCache2* iface, | |
| IDataObject* pDataObject) | |
| { | |
| FIXME("stub\n"); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DataCache_IOleCache2_SetData( | |
| IOleCache2* iface, | |
| FORMATETC* pformatetc, | |
| STGMEDIUM* pmedium, | |
| BOOL fRelease) | |
| { | |
| DataCache *This = impl_from_IOleCache2(iface); | |
| DataCacheEntry *cache_entry; | |
| HRESULT hr; | |
| TRACE("(%p, %p, %s)\n", pformatetc, pmedium, fRelease ? "TRUE" : "FALSE"); | |
| TRACE("formatetc = %s\n", debugstr_formatetc(pformatetc)); | |
| cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetc); | |
| if (cache_entry) | |
| { | |
| hr = DataCacheEntry_SetData(cache_entry, pformatetc, pmedium, fRelease); | |
| if (SUCCEEDED(hr)) | |
| DataCache_FireOnViewChange(This, cache_entry->fmtetc.dwAspect, | |
| cache_entry->fmtetc.lindex); | |
| return hr; | |
| } | |
| WARN("cache entry not found\n"); | |
| return OLE_E_BLANK; | |
| } | |
| static HRESULT WINAPI DataCache_UpdateCache( | |
| IOleCache2* iface, | |
| LPDATAOBJECT pDataObject, | |
| DWORD grfUpdf, | |
| LPVOID pReserved) | |
| { | |
| FIXME("(%p, 0x%x, %p): stub\n", pDataObject, grfUpdf, pReserved); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DataCache_DiscardCache( | |
| IOleCache2* iface, | |
| DWORD dwDiscardOptions) | |
| { | |
| DataCache *This = impl_from_IOleCache2(iface); | |
| DataCacheEntry *cache_entry; | |
| HRESULT hr = S_OK; | |
| TRACE("(%d)\n", dwDiscardOptions); | |
| if (dwDiscardOptions == DISCARDCACHE_SAVEIFDIRTY) | |
| hr = DataCache_Save(&This->IPersistStorage_iface, This->presentationStorage, TRUE); | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| hr = DataCacheEntry_DiscardData(cache_entry); | |
| if (FAILED(hr)) | |
| break; | |
| } | |
| return hr; | |
| } | |
| /********************************************************* | |
| * Method implementation for the IOleCacheControl | |
| * part of the DataCache class. | |
| */ | |
| /************************************************************************ | |
| * DataCache_IOleCacheControl_QueryInterface (IUnknown) | |
| */ | |
| static HRESULT WINAPI DataCache_IOleCacheControl_QueryInterface( | |
| IOleCacheControl* iface, | |
| REFIID riid, | |
| void** ppvObject) | |
| { | |
| DataCache *this = impl_from_IOleCacheControl(iface); | |
| return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject); | |
| } | |
| /************************************************************************ | |
| * DataCache_IOleCacheControl_AddRef (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IOleCacheControl_AddRef( | |
| IOleCacheControl* iface) | |
| { | |
| DataCache *this = impl_from_IOleCacheControl(iface); | |
| return IUnknown_AddRef(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_IOleCacheControl_Release (IUnknown) | |
| */ | |
| static ULONG WINAPI DataCache_IOleCacheControl_Release( | |
| IOleCacheControl* iface) | |
| { | |
| DataCache *this = impl_from_IOleCacheControl(iface); | |
| return IUnknown_Release(this->outer_unk); | |
| } | |
| /************************************************************************ | |
| * DataCache_OnRun (IOleCacheControl) | |
| */ | |
| static HRESULT WINAPI DataCache_OnRun(IOleCacheControl* iface, IDataObject *data_obj) | |
| { | |
| DataCache *This = impl_from_IOleCacheControl(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p)->(%p)\n", iface, data_obj); | |
| if(This->running_object) return S_OK; | |
| /* No reference is taken on the data object */ | |
| This->running_object = data_obj; | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| setup_sink(This, cache_entry); | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * DataCache_OnStop (IOleCacheControl) | |
| */ | |
| static HRESULT WINAPI DataCache_OnStop(IOleCacheControl* iface) | |
| { | |
| DataCache *This = impl_from_IOleCacheControl(iface); | |
| DataCacheEntry *cache_entry; | |
| TRACE("(%p)\n", iface); | |
| if(!This->running_object) return S_OK; | |
| LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry) | |
| { | |
| if(cache_entry->sink_id) | |
| { | |
| IDataObject_DUnadvise(This->running_object, cache_entry->sink_id); | |
| cache_entry->sink_id = 0; | |
| } | |
| } | |
| /* No ref taken in OnRun, so no Release call here */ | |
| This->running_object = NULL; | |
| return S_OK; | |
| } | |
| /************************************************************************ | |
| * IAdviseSink methods. | |
| * This behaves as an internal object to the data cache. QI'ing its ptr doesn't | |
| * give access to the cache's other interfaces. We don't maintain a ref count, | |
| * the object exists as long as the cache is around. | |
| */ | |
| static HRESULT WINAPI DataCache_IAdviseSink_QueryInterface(IAdviseSink *iface, REFIID iid, void **obj) | |
| { | |
| *obj = NULL; | |
| if (IsEqualIID(&IID_IUnknown, iid) || | |
| IsEqualIID(&IID_IAdviseSink, iid)) | |
| { | |
| *obj = iface; | |
| } | |
| if(*obj) | |
| { | |
| IAdviseSink_AddRef(iface); | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI DataCache_IAdviseSink_AddRef(IAdviseSink *iface) | |
| { | |
| return 2; | |
| } | |
| static ULONG WINAPI DataCache_IAdviseSink_Release(IAdviseSink *iface) | |
| { | |
| return 1; | |
| } | |
| static void WINAPI DataCache_OnDataChange(IAdviseSink *iface, FORMATETC *fmt, STGMEDIUM *med) | |
| { | |
| DataCache *This = impl_from_IAdviseSink(iface); | |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_formatetc(fmt), med); | |
| IOleCache2_SetData(&This->IOleCache2_iface, fmt, med, FALSE); | |
| } | |
| static void WINAPI DataCache_OnViewChange(IAdviseSink *iface, DWORD aspect, LONG index) | |
| { | |
| FIXME("stub\n"); | |
| } | |
| static void WINAPI DataCache_OnRename(IAdviseSink *iface, IMoniker *mk) | |
| { | |
| FIXME("stub\n"); | |
| } | |
| static void WINAPI DataCache_OnSave(IAdviseSink *iface) | |
| { | |
| FIXME("stub\n"); | |
| } | |
| static void WINAPI DataCache_OnClose(IAdviseSink *iface) | |
| { | |
| FIXME("stub\n"); | |
| } | |
| /* | |
| * Virtual function tables for the DataCache class. | |
| */ | |
| static const IUnknownVtbl DataCache_NDIUnknown_VTable = | |
| { | |
| DataCache_NDIUnknown_QueryInterface, | |
| DataCache_NDIUnknown_AddRef, | |
| DataCache_NDIUnknown_Release | |
| }; | |
| static const IDataObjectVtbl DataCache_IDataObject_VTable = | |
| { | |
| DataCache_IDataObject_QueryInterface, | |
| DataCache_IDataObject_AddRef, | |
| DataCache_IDataObject_Release, | |
| DataCache_GetData, | |
| DataCache_GetDataHere, | |
| DataCache_QueryGetData, | |
| DataCache_GetCanonicalFormatEtc, | |
| DataCache_IDataObject_SetData, | |
| DataCache_EnumFormatEtc, | |
| DataCache_DAdvise, | |
| DataCache_DUnadvise, | |
| DataCache_EnumDAdvise | |
| }; | |
| static const IPersistStorageVtbl DataCache_IPersistStorage_VTable = | |
| { | |
| DataCache_IPersistStorage_QueryInterface, | |
| DataCache_IPersistStorage_AddRef, | |
| DataCache_IPersistStorage_Release, | |
| DataCache_GetClassID, | |
| DataCache_IsDirty, | |
| DataCache_InitNew, | |
| DataCache_Load, | |
| DataCache_Save, | |
| DataCache_SaveCompleted, | |
| DataCache_HandsOffStorage | |
| }; | |
| static const IViewObject2Vtbl DataCache_IViewObject2_VTable = | |
| { | |
| DataCache_IViewObject2_QueryInterface, | |
| DataCache_IViewObject2_AddRef, | |
| DataCache_IViewObject2_Release, | |
| DataCache_Draw, | |
| DataCache_GetColorSet, | |
| DataCache_Freeze, | |
| DataCache_Unfreeze, | |
| DataCache_SetAdvise, | |
| DataCache_GetAdvise, | |
| DataCache_GetExtent | |
| }; | |
| static const IOleCache2Vtbl DataCache_IOleCache2_VTable = | |
| { | |
| DataCache_IOleCache2_QueryInterface, | |
| DataCache_IOleCache2_AddRef, | |
| DataCache_IOleCache2_Release, | |
| DataCache_Cache, | |
| DataCache_Uncache, | |
| DataCache_EnumCache, | |
| DataCache_InitCache, | |
| DataCache_IOleCache2_SetData, | |
| DataCache_UpdateCache, | |
| DataCache_DiscardCache | |
| }; | |
| static const IOleCacheControlVtbl DataCache_IOleCacheControl_VTable = | |
| { | |
| DataCache_IOleCacheControl_QueryInterface, | |
| DataCache_IOleCacheControl_AddRef, | |
| DataCache_IOleCacheControl_Release, | |
| DataCache_OnRun, | |
| DataCache_OnStop | |
| }; | |
| static const IAdviseSinkVtbl DataCache_IAdviseSink_VTable = | |
| { | |
| DataCache_IAdviseSink_QueryInterface, | |
| DataCache_IAdviseSink_AddRef, | |
| DataCache_IAdviseSink_Release, | |
| DataCache_OnDataChange, | |
| DataCache_OnViewChange, | |
| DataCache_OnRename, | |
| DataCache_OnSave, | |
| DataCache_OnClose | |
| }; | |
| /********************************************************* | |
| * Method implementation for DataCache class. | |
| */ | |
| static DataCache* DataCache_Construct( | |
| REFCLSID clsid, | |
| LPUNKNOWN pUnkOuter) | |
| { | |
| DataCache* newObject = 0; | |
| /* | |
| * Allocate space for the object. | |
| */ | |
| newObject = HeapAlloc(GetProcessHeap(), 0, sizeof(DataCache)); | |
| if (newObject==0) | |
| return newObject; | |
| /* | |
| * Initialize the virtual function table. | |
| */ | |
| newObject->IDataObject_iface.lpVtbl = &DataCache_IDataObject_VTable; | |
| newObject->IUnknown_inner.lpVtbl = &DataCache_NDIUnknown_VTable; | |
| newObject->IPersistStorage_iface.lpVtbl = &DataCache_IPersistStorage_VTable; | |
| newObject->IViewObject2_iface.lpVtbl = &DataCache_IViewObject2_VTable; | |
| newObject->IOleCache2_iface.lpVtbl = &DataCache_IOleCache2_VTable; | |
| newObject->IOleCacheControl_iface.lpVtbl = &DataCache_IOleCacheControl_VTable; | |
| newObject->IAdviseSink_iface.lpVtbl = &DataCache_IAdviseSink_VTable; | |
| newObject->outer_unk = pUnkOuter ? pUnkOuter : &newObject->IUnknown_inner; | |
| newObject->ref = 1; | |
| /* | |
| * Initialize the other members of the structure. | |
| */ | |
| newObject->sinkAspects = 0; | |
| newObject->sinkAdviseFlag = 0; | |
| newObject->sinkInterface = 0; | |
| newObject->clsid = CLSID_NULL; | |
| newObject->presentationStorage = NULL; | |
| list_init(&newObject->cache_list); | |
| newObject->last_cache_id = 2; | |
| newObject->dirty = FALSE; | |
| newObject->running_object = NULL; | |
| create_automatic_entry( newObject, clsid ); | |
| newObject->clsid = *clsid; | |
| return newObject; | |
| } | |
| /****************************************************************************** | |
| * CreateDataCache [OLE32.@] | |
| * | |
| * Creates a data cache to allow an object to render one or more of its views, | |
| * whether running or not. | |
| * | |
| * PARAMS | |
| * pUnkOuter [I] Outer unknown for the object. | |
| * rclsid [I] | |
| * riid [I] IID of interface to return. | |
| * ppvObj [O] Address where the data cache object will be stored on return. | |
| * | |
| * RETURNS | |
| * Success: S_OK. | |
| * Failure: HRESULT code. | |
| * | |
| * NOTES | |
| * The following interfaces are supported by the returned data cache object: | |
| * IOleCache, IOleCache2, IOleCacheControl, IPersistStorage, IDataObject, | |
| * IViewObject and IViewObject2. | |
| */ | |
| HRESULT WINAPI CreateDataCache( | |
| LPUNKNOWN pUnkOuter, | |
| REFCLSID rclsid, | |
| REFIID riid, | |
| LPVOID* ppvObj) | |
| { | |
| DataCache* newCache = NULL; | |
| HRESULT hr = S_OK; | |
| TRACE("(%s, %p, %s, %p)\n", debugstr_guid(rclsid), pUnkOuter, debugstr_guid(riid), ppvObj); | |
| /* | |
| * Sanity check | |
| */ | |
| if (ppvObj==0) | |
| return E_POINTER; | |
| *ppvObj = 0; | |
| /* | |
| * If this cache is constructed for aggregation, make sure | |
| * the caller is requesting the IUnknown interface. | |
| * This is necessary because it's the only time the non-delegating | |
| * IUnknown pointer can be returned to the outside. | |
| */ | |
| if ( pUnkOuter && !IsEqualIID(&IID_IUnknown, riid) ) | |
| return E_INVALIDARG; | |
| /* | |
| * Try to construct a new instance of the class. | |
| */ | |
| newCache = DataCache_Construct(rclsid, | |
| pUnkOuter); | |
| if (newCache == 0) | |
| return E_OUTOFMEMORY; | |
| hr = IUnknown_QueryInterface(&newCache->IUnknown_inner, riid, ppvObj); | |
| IUnknown_Release(&newCache->IUnknown_inner); | |
| return hr; | |
| } |