gphoto2.ds: Set supported groups.
[wine.git] / dlls / ole32 / datacache.c
blob48453b4d52a8d217a6ea08f05ec568bc522b0dad
1 /*
2 * OLE 2 Data cache
4 * Copyright 1999 Francis Beaudet
5 * Copyright 2000 Abey George
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * NOTES:
22 * The OLE2 data cache supports a whole whack of
23 * interfaces including:
24 * IDataObject, IPersistStorage, IViewObject2,
25 * IOleCache2 and IOleCacheControl.
27 * Most of the implementation details are taken from: Inside OLE
28 * second edition by Kraig Brockschmidt,
30 * NOTES
31 * - This implementation of the datacache will let your application
32 * load documents that have embedded OLE objects in them and it will
33 * also retrieve the metafile representation of those objects.
34 * - This implementation of the datacache will also allow your
35 * application to save new documents with OLE objects in them.
36 * - The main thing that it doesn't do is allow you to activate
37 * or modify the OLE objects in any way.
38 * - I haven't found any good documentation on the real usage of
39 * the streams created by the data cache. In particular, How to
40 * determine what the XXX stands for in the stream name
41 * "\002OlePresXXX". It appears to just be a counter.
42 * - Also, I don't know the real content of the presentation stream
43 * header. I was able to figure-out where the extent of the object
44 * was stored and the aspect, but that's about it.
47 #include <stdarg.h>
48 #include <string.h>
50 #define COBJMACROS
51 #define NONAMELESSUNION
53 #include "windef.h"
54 #include "winbase.h"
55 #include "wingdi.h"
56 #include "winuser.h"
57 #include "winerror.h"
58 #include "ole2.h"
59 #include "compobj_private.h"
60 #include "wine/unicode.h"
61 #include "wine/list.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(ole);
66 /****************************************************************************
67 * PresentationDataHeader
69 * This structure represents the header of the \002OlePresXXX stream in
70 * the OLE object storage.
72 typedef struct PresentationDataHeader
74 /* clipformat:
75 * - standard clipformat:
76 * DWORD length = 0xffffffff;
77 * DWORD cfFormat;
78 * - or custom clipformat:
79 * DWORD length;
80 * CHAR format_name[length]; (null-terminated)
82 DWORD unknown3; /* 4, possibly TYMED_ISTREAM */
83 DVASPECT dvAspect;
84 DWORD lindex;
85 DWORD advf;
86 DWORD unknown7; /* 0 */
87 DWORD dwObjectExtentX;
88 DWORD dwObjectExtentY;
89 DWORD dwSize;
90 } PresentationDataHeader;
92 enum stream_type
94 no_stream,
95 pres_stream,
96 contents_stream
99 typedef struct DataCacheEntry
101 struct list entry;
102 /* format of this entry */
103 FORMATETC fmtetc;
104 /* cached data */
105 STGMEDIUM stgmedium;
107 * This stream pointer is set through a call to
108 * IPersistStorage_Load. This is where the visual
109 * representation of the object is stored.
111 IStream *stream;
112 enum stream_type stream_type;
113 /* connection ID */
114 DWORD id;
115 /* dirty flag */
116 BOOL dirty;
117 /* stream number (-1 if not set ) */
118 unsigned short stream_number;
119 /* sink id set when object is running */
120 DWORD sink_id;
121 /* Advise sink flags */
122 DWORD advise_flags;
123 } DataCacheEntry;
125 /****************************************************************************
126 * DataCache
128 struct DataCache
131 * List all interface here
133 IUnknown IUnknown_inner;
134 IDataObject IDataObject_iface;
135 IPersistStorage IPersistStorage_iface;
136 IViewObject2 IViewObject2_iface;
137 IOleCache2 IOleCache2_iface;
138 IOleCacheControl IOleCacheControl_iface;
140 /* The sink that is connected to a remote object.
141 The other interfaces are not available by QI'ing the sink and vice-versa */
142 IAdviseSink IAdviseSink_iface;
145 * Reference count of this object
147 LONG ref;
150 * IUnknown implementation of the outer object.
152 IUnknown *outer_unk;
155 * The user of this object can setup ONE advise sink
156 * connection with the object. These parameters describe
157 * that connection.
159 DWORD sinkAspects;
160 DWORD sinkAdviseFlag;
161 IAdviseSink *sinkInterface;
163 CLSID clsid;
164 IStorage *presentationStorage;
166 /* list of cache entries */
167 struct list cache_list;
168 /* last id assigned to an entry */
169 DWORD last_cache_id;
170 /* dirty flag */
171 BOOL dirty;
172 /* running object set by OnRun */
173 IDataObject *running_object;
176 typedef struct DataCache DataCache;
179 * Here, I define utility macros to help with the casting of the
180 * "this" parameter.
181 * There is a version to accommodate all of the VTables implemented
182 * by this object.
185 static inline DataCache *impl_from_IDataObject( IDataObject *iface )
187 return CONTAINING_RECORD(iface, DataCache, IDataObject_iface);
190 static inline DataCache *impl_from_IUnknown( IUnknown *iface )
192 return CONTAINING_RECORD(iface, DataCache, IUnknown_inner);
195 static inline DataCache *impl_from_IPersistStorage( IPersistStorage *iface )
197 return CONTAINING_RECORD(iface, DataCache, IPersistStorage_iface);
200 static inline DataCache *impl_from_IViewObject2( IViewObject2 *iface )
202 return CONTAINING_RECORD(iface, DataCache, IViewObject2_iface);
205 static inline DataCache *impl_from_IOleCache2( IOleCache2 *iface )
207 return CONTAINING_RECORD(iface, DataCache, IOleCache2_iface);
210 static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface )
212 return CONTAINING_RECORD(iface, DataCache, IOleCacheControl_iface);
215 static inline DataCache *impl_from_IAdviseSink( IAdviseSink *iface )
217 return CONTAINING_RECORD(iface, DataCache, IAdviseSink_iface);
220 const char *debugstr_formatetc(const FORMATETC *formatetc)
222 return wine_dbg_sprintf("{ cfFormat = 0x%x, ptd = %p, dwAspect = %d, lindex = %d, tymed = %d }",
223 formatetc->cfFormat, formatetc->ptd, formatetc->dwAspect,
224 formatetc->lindex, formatetc->tymed);
227 /***********************************************************************
228 * bitmap_info_size
230 * Return the size of the bitmap info structure including color table.
232 static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
234 unsigned int colors, size, masks = 0;
236 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
238 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
239 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
240 return sizeof(BITMAPCOREHEADER) + colors *
241 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
243 else /* assume BITMAPINFOHEADER */
245 colors = info->bmiHeader.biClrUsed;
246 if (colors > 256) /* buffer overflow otherwise */
247 colors = 256;
248 if (!colors && (info->bmiHeader.biBitCount <= 8))
249 colors = 1 << info->bmiHeader.biBitCount;
250 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
251 size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
252 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
256 static void DataCacheEntry_Destroy(DataCache *cache, DataCacheEntry *cache_entry)
258 list_remove(&cache_entry->entry);
259 if (cache_entry->stream)
260 IStream_Release(cache_entry->stream);
261 CoTaskMemFree(cache_entry->fmtetc.ptd);
262 ReleaseStgMedium(&cache_entry->stgmedium);
263 if(cache_entry->sink_id)
264 IDataObject_DUnadvise(cache->running_object, cache_entry->sink_id);
266 HeapFree(GetProcessHeap(), 0, cache_entry);
269 static void DataCache_Destroy(
270 DataCache* ptrToDestroy)
272 DataCacheEntry *cache_entry, *next_cache_entry;
274 TRACE("()\n");
276 if (ptrToDestroy->sinkInterface != NULL)
278 IAdviseSink_Release(ptrToDestroy->sinkInterface);
279 ptrToDestroy->sinkInterface = NULL;
282 LIST_FOR_EACH_ENTRY_SAFE(cache_entry, next_cache_entry, &ptrToDestroy->cache_list, DataCacheEntry, entry)
283 DataCacheEntry_Destroy(ptrToDestroy, cache_entry);
285 if (ptrToDestroy->presentationStorage != NULL)
287 IStorage_Release(ptrToDestroy->presentationStorage);
288 ptrToDestroy->presentationStorage = NULL;
292 * Free the datacache pointer.
294 HeapFree(GetProcessHeap(), 0, ptrToDestroy);
297 static DataCacheEntry *DataCache_GetEntryForFormatEtc(DataCache *This, const FORMATETC *formatetc)
299 DataCacheEntry *cache_entry;
300 FORMATETC fmt = *formatetc;
302 if (fmt.cfFormat == CF_BITMAP)
304 fmt.cfFormat = CF_DIB;
305 fmt.tymed = TYMED_HGLOBAL;
308 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
310 /* FIXME: also compare DVTARGETDEVICEs */
311 if ((fmt.cfFormat == cache_entry->fmtetc.cfFormat) &&
312 (fmt.dwAspect == cache_entry->fmtetc.dwAspect) &&
313 (fmt.lindex == cache_entry->fmtetc.lindex) &&
314 ((fmt.tymed == cache_entry->fmtetc.tymed) || !cache_entry->fmtetc.cfFormat)) /* tymed is ignored for view caching */
315 return cache_entry;
317 return NULL;
320 /* checks that the clipformat and tymed are valid and returns an error if they
321 * aren't and CACHE_S_NOTSUPPORTED if they are valid, but can't be rendered by
322 * DataCache_Draw */
323 static HRESULT check_valid_formatetc( const FORMATETC *fmt )
325 /* DVASPECT_ICON must be CF_METAFILEPICT */
326 if (fmt->dwAspect == DVASPECT_ICON && fmt->cfFormat != CF_METAFILEPICT)
327 return DV_E_FORMATETC;
329 if (!fmt->cfFormat ||
330 (fmt->cfFormat == CF_METAFILEPICT && fmt->tymed == TYMED_MFPICT) ||
331 (fmt->cfFormat == CF_BITMAP && fmt->tymed == TYMED_GDI) ||
332 (fmt->cfFormat == CF_DIB && fmt->tymed == TYMED_HGLOBAL) ||
333 (fmt->cfFormat == CF_ENHMETAFILE && fmt->tymed == TYMED_ENHMF))
334 return S_OK;
335 else if (fmt->tymed == TYMED_HGLOBAL)
336 return CACHE_S_FORMATETC_NOTSUPPORTED;
337 else
339 WARN("invalid clipformat/tymed combination: %d/%d\n", fmt->cfFormat, fmt->tymed);
340 return DV_E_TYMED;
344 static BOOL init_cache_entry(DataCacheEntry *entry, const FORMATETC *fmt, DWORD advf,
345 DWORD id)
347 HRESULT hr;
349 hr = copy_formatetc(&entry->fmtetc, fmt);
350 if (FAILED(hr)) return FALSE;
352 entry->stgmedium.tymed = TYMED_NULL;
353 entry->stgmedium.pUnkForRelease = NULL;
354 entry->stream = NULL;
355 entry->stream_type = no_stream;
356 entry->id = id;
357 entry->dirty = TRUE;
358 entry->stream_number = -1;
359 entry->sink_id = 0;
360 entry->advise_flags = advf;
362 return TRUE;
365 static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc, DWORD advf,
366 BOOL automatic, DataCacheEntry **cache_entry)
368 HRESULT hr;
369 DWORD id = automatic ? 1 : This->last_cache_id;
370 DataCacheEntry *entry;
372 hr = check_valid_formatetc( formatetc );
373 if (FAILED(hr))
374 return hr;
375 if (hr == CACHE_S_FORMATETC_NOTSUPPORTED)
376 TRACE("creating unsupported format %d\n", formatetc->cfFormat);
378 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
379 if (!entry)
380 return E_OUTOFMEMORY;
382 if (!init_cache_entry(entry, formatetc, advf, id))
383 goto fail;
385 if (automatic)
386 list_add_head(&This->cache_list, &entry->entry);
387 else
389 list_add_tail(&This->cache_list, &entry->entry);
390 This->last_cache_id++;
393 if (cache_entry) *cache_entry = entry;
394 return hr;
396 fail:
397 HeapFree(GetProcessHeap(), 0, entry);
398 return E_OUTOFMEMORY;
401 /************************************************************************
402 * DataCache_FireOnViewChange
404 * This method will fire an OnViewChange notification to the advise
405 * sink registered with the datacache.
407 * See IAdviseSink::OnViewChange for more details.
409 static void DataCache_FireOnViewChange(
410 DataCache* this,
411 DWORD aspect,
412 LONG lindex)
414 TRACE("(%p, %x, %d)\n", this, aspect, lindex);
417 * The sink supplies a filter when it registers
418 * we make sure we only send the notifications when that
419 * filter matches.
421 if ((this->sinkAspects & aspect) != 0)
423 if (this->sinkInterface != NULL)
425 IAdviseSink_OnViewChange(this->sinkInterface,
426 aspect,
427 lindex);
430 * Some sinks want to be unregistered automatically when
431 * the first notification goes out.
433 if ( (this->sinkAdviseFlag & ADVF_ONLYONCE) != 0)
435 IAdviseSink_Release(this->sinkInterface);
437 this->sinkInterface = NULL;
438 this->sinkAspects = 0;
439 this->sinkAdviseFlag = 0;
445 /* Helper for DataCacheEntry_OpenPresStream */
446 static BOOL DataCache_IsPresentationStream(const STATSTG *elem)
448 /* The presentation streams have names of the form "\002OlePresXXX",
449 * where XXX goes from 000 to 999. */
450 static const WCHAR OlePres[] = { 2,'O','l','e','P','r','e','s' };
452 LPCWSTR name = elem->pwcsName;
454 return (elem->type == STGTY_STREAM)
455 && (strlenW(name) == 11)
456 && (strncmpW(name, OlePres, 8) == 0)
457 && (name[8] >= '0') && (name[8] <= '9')
458 && (name[9] >= '0') && (name[9] <= '9')
459 && (name[10] >= '0') && (name[10] <= '9');
462 static HRESULT read_clipformat(IStream *stream, CLIPFORMAT *clipformat)
464 DWORD length;
465 HRESULT hr;
466 ULONG read;
468 *clipformat = 0;
470 hr = IStream_Read(stream, &length, sizeof(length), &read);
471 if (hr != S_OK || read != sizeof(length))
472 return DV_E_CLIPFORMAT;
473 if (!length) {
474 /* No clipboard format present */
475 return S_OK;
477 if (length == -1)
479 DWORD cf;
480 hr = IStream_Read(stream, &cf, sizeof(cf), &read);
481 if (hr != S_OK || read != sizeof(cf))
482 return DV_E_CLIPFORMAT;
483 *clipformat = cf;
485 else
487 char *format_name = HeapAlloc(GetProcessHeap(), 0, length);
488 if (!format_name)
489 return E_OUTOFMEMORY;
490 hr = IStream_Read(stream, format_name, length, &read);
491 if (hr != S_OK || read != length || format_name[length - 1] != '\0')
493 HeapFree(GetProcessHeap(), 0, format_name);
494 return DV_E_CLIPFORMAT;
496 *clipformat = RegisterClipboardFormatA(format_name);
497 HeapFree(GetProcessHeap(), 0, format_name);
499 return S_OK;
502 static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat)
504 DWORD length;
505 HRESULT hr;
506 char format_name[256];
508 if (clipformat < 0xc000)
509 length = -1;
510 else
512 length = GetClipboardFormatNameA(clipformat, format_name, sizeof(format_name));
513 /* If there is a clipboard format name, we need to include its terminating \0 */
514 if (length) length++;
516 hr = IStream_Write(stream, &length, sizeof(length), NULL);
517 if (FAILED(hr))
518 return hr;
519 if (clipformat < 0xc000)
521 DWORD cf = clipformat;
522 hr = IStream_Write(stream, &cf, sizeof(cf), NULL);
524 else
526 hr = IStream_Write(stream, format_name, length, NULL);
528 return hr;
531 /************************************************************************
532 * DataCacheEntry_OpenPresStream
534 * This method will find the stream for the given presentation. It makes
535 * no attempt at fallback.
537 * Param:
538 * this - Pointer to the DataCache object
539 * drawAspect - The aspect of the object that we wish to draw.
540 * pStm - A returned stream. It points to the beginning of the
541 * - presentation data, including the header.
543 * Errors:
544 * S_OK The requested stream has been opened.
545 * OLE_E_BLANK The requested stream could not be found.
546 * Quite a few others I'm too lazy to map correctly.
548 * Notes:
549 * Algorithm: Scan the elements of the presentation storage, looking
550 * for presentation streams. For each presentation stream,
551 * load the header and check to see if the aspect matches.
553 * If a fallback is desired, just opening the first presentation stream
554 * is a possibility.
556 static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStream **ppStm)
558 HRESULT hr;
559 LARGE_INTEGER offset;
561 if (cache_entry->stream)
563 /* Rewind the stream before returning it. */
564 offset.QuadPart = 0;
566 hr = IStream_Seek( cache_entry->stream, offset, STREAM_SEEK_SET, NULL );
567 if (SUCCEEDED( hr ))
569 *ppStm = cache_entry->stream;
570 IStream_AddRef( cache_entry->stream );
573 else
574 hr = OLE_E_BLANK;
576 return hr;
580 static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm )
582 HRESULT hr;
583 STATSTG stat;
584 ULARGE_INTEGER current_pos;
585 void *bits;
586 METAFILEPICT *mfpict;
587 HGLOBAL hmfpict;
588 PresentationDataHeader header;
589 CLIPFORMAT clipformat;
590 static const LARGE_INTEGER offset_zero;
591 ULONG read;
593 if (cache_entry->stream_type != pres_stream)
595 FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type );
596 return E_FAIL;
599 hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
600 if (FAILED( hr )) return hr;
602 hr = read_clipformat( stm, &clipformat );
603 if (FAILED( hr )) return hr;
605 hr = IStream_Read( stm, &header, sizeof(header), &read );
606 if (hr != S_OK || read != sizeof(header)) return E_FAIL;
608 hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, &current_pos );
609 if (FAILED( hr )) return hr;
611 stat.cbSize.QuadPart -= current_pos.QuadPart;
613 hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) );
614 if (!hmfpict) return E_OUTOFMEMORY;
615 mfpict = GlobalLock( hmfpict );
617 bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart);
618 if (!bits)
620 GlobalFree( hmfpict );
621 return E_OUTOFMEMORY;
624 hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read );
625 if (hr != S_OK || read != stat.cbSize.u.LowPart) hr = E_FAIL;
627 if (SUCCEEDED( hr ))
629 /* FIXME: get this from the stream */
630 mfpict->mm = MM_ANISOTROPIC;
631 mfpict->xExt = header.dwObjectExtentX;
632 mfpict->yExt = header.dwObjectExtentY;
633 mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits );
634 if (!mfpict->hMF)
635 hr = E_FAIL;
638 GlobalUnlock( hmfpict );
639 if (SUCCEEDED( hr ))
641 cache_entry->stgmedium.tymed = TYMED_MFPICT;
642 cache_entry->stgmedium.u.hMetaFilePict = hmfpict;
644 else
645 GlobalFree( hmfpict );
647 HeapFree( GetProcessHeap(), 0, bits );
649 return hr;
652 static HRESULT load_dib( DataCacheEntry *cache_entry, IStream *stm )
654 HRESULT hr;
655 STATSTG stat;
656 void *dib;
657 HGLOBAL hglobal;
658 ULONG read, info_size, bi_size;
659 BITMAPFILEHEADER file;
660 BITMAPINFOHEADER *info;
662 if (cache_entry->stream_type != contents_stream)
664 FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type );
665 return E_FAIL;
668 hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
669 if (FAILED( hr )) return hr;
671 if (stat.cbSize.QuadPart < sizeof(file) + sizeof(DWORD)) return E_FAIL;
672 hr = IStream_Read( stm, &file, sizeof(file), &read );
673 if (hr != S_OK || read != sizeof(file)) return E_FAIL;
674 stat.cbSize.QuadPart -= sizeof(file);
676 hglobal = GlobalAlloc( GMEM_MOVEABLE, stat.cbSize.u.LowPart );
677 if (!hglobal) return E_OUTOFMEMORY;
678 dib = GlobalLock( hglobal );
680 hr = IStream_Read( stm, dib, sizeof(DWORD), &read );
681 if (hr != S_OK || read != sizeof(DWORD)) goto fail;
682 bi_size = *(DWORD *)dib;
683 if (stat.cbSize.QuadPart < bi_size) goto fail;
685 hr = IStream_Read( stm, (char *)dib + sizeof(DWORD), bi_size - sizeof(DWORD), &read );
686 if (hr != S_OK || read != bi_size - sizeof(DWORD)) goto fail;
688 info_size = bitmap_info_size( dib, DIB_RGB_COLORS );
689 if (stat.cbSize.QuadPart < info_size) goto fail;
690 if (info_size > bi_size)
692 hr = IStream_Read( stm, (char *)dib + bi_size, info_size - bi_size, &read );
693 if (hr != S_OK || read != info_size - bi_size) goto fail;
695 stat.cbSize.QuadPart -= info_size;
697 if (file.bfOffBits)
699 LARGE_INTEGER skip;
701 skip.QuadPart = file.bfOffBits - sizeof(file) - info_size;
702 if (stat.cbSize.QuadPart < skip.QuadPart) goto fail;
703 hr = IStream_Seek( stm, skip, STREAM_SEEK_CUR, NULL );
704 if (hr != S_OK) goto fail;
705 stat.cbSize.QuadPart -= skip.QuadPart;
708 hr = IStream_Read( stm, (char *)dib + info_size, stat.cbSize.u.LowPart, &read );
709 if (hr != S_OK || read != stat.cbSize.QuadPart) goto fail;
711 if (bi_size >= sizeof(*info))
713 info = (BITMAPINFOHEADER *)dib;
714 if (info->biXPelsPerMeter == 0 || info->biYPelsPerMeter == 0)
716 HDC hdc = GetDC( 0 );
717 info->biXPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSX ), 10000, 254 );
718 info->biYPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSY ), 10000, 254 );
719 ReleaseDC( 0, hdc );
723 GlobalUnlock( hglobal );
725 cache_entry->stgmedium.tymed = TYMED_HGLOBAL;
726 cache_entry->stgmedium.u.hGlobal = hglobal;
728 return S_OK;
730 fail:
731 GlobalUnlock( hglobal );
732 GlobalFree( hglobal );
733 return E_FAIL;
737 /************************************************************************
738 * DataCacheEntry_LoadData
740 * This method will read information for the requested presentation
741 * into the given structure.
743 * Param:
744 * This - The entry to load the data from.
746 * Returns:
747 * This method returns a metafile handle if it is successful.
748 * it will return 0 if not.
750 static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry)
752 HRESULT hr;
753 IStream *stm;
755 hr = DataCacheEntry_OpenPresStream( cache_entry, &stm );
756 if (FAILED(hr)) return hr;
758 switch (cache_entry->fmtetc.cfFormat)
760 case CF_METAFILEPICT:
761 hr = load_mf_pict( cache_entry, stm );
762 break;
764 case CF_DIB:
765 hr = load_dib( cache_entry, stm );
766 break;
768 default:
769 FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat );
770 hr = E_NOTIMPL;
773 IStream_Release( stm );
774 return hr;
777 static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *cache_entry,
778 IStorage *storage, IStream **stream)
780 WCHAR wszName[] = {2,'O','l','e','P','r','e','s',
781 '0' + (cache_entry->stream_number / 100) % 10,
782 '0' + (cache_entry->stream_number / 10) % 10,
783 '0' + cache_entry->stream_number % 10, 0};
785 /* FIXME: cache the created stream in This? */
786 return IStorage_CreateStream(storage, wszName,
787 STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
788 0, 0, stream);
791 static HRESULT DataCacheEntry_Save(DataCacheEntry *cache_entry, IStorage *storage,
792 BOOL same_as_load)
794 PresentationDataHeader header;
795 HRESULT hr;
796 IStream *pres_stream;
797 void *data = NULL;
799 TRACE("stream_number = %d, fmtetc = %s\n", cache_entry->stream_number, debugstr_formatetc(&cache_entry->fmtetc));
801 hr = DataCacheEntry_CreateStream(cache_entry, storage, &pres_stream);
802 if (FAILED(hr))
803 return hr;
805 hr = write_clipformat(pres_stream, cache_entry->fmtetc.cfFormat);
806 if (FAILED(hr))
807 return hr;
809 if (cache_entry->fmtetc.ptd)
810 FIXME("ptd not serialized\n");
811 header.unknown3 = 4;
812 header.dvAspect = cache_entry->fmtetc.dwAspect;
813 header.lindex = cache_entry->fmtetc.lindex;
814 header.advf = cache_entry->advise_flags;
815 header.unknown7 = 0;
816 header.dwObjectExtentX = 0;
817 header.dwObjectExtentY = 0;
818 header.dwSize = 0;
820 /* size the data */
821 switch (cache_entry->fmtetc.cfFormat)
823 case CF_METAFILEPICT:
825 if (cache_entry->stgmedium.tymed != TYMED_NULL)
827 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
828 if (!mfpict)
830 IStream_Release(pres_stream);
831 return DV_E_STGMEDIUM;
833 header.dwObjectExtentX = mfpict->xExt;
834 header.dwObjectExtentY = mfpict->yExt;
835 header.dwSize = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
836 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
838 break;
840 default:
841 break;
845 * Write the header.
847 hr = IStream_Write(pres_stream, &header, sizeof(PresentationDataHeader),
848 NULL);
849 if (FAILED(hr))
851 IStream_Release(pres_stream);
852 return hr;
855 /* get the data */
856 switch (cache_entry->fmtetc.cfFormat)
858 case CF_METAFILEPICT:
860 if (cache_entry->stgmedium.tymed != TYMED_NULL)
862 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
863 if (!mfpict)
865 IStream_Release(pres_stream);
866 return DV_E_STGMEDIUM;
868 data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
869 GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data);
870 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
872 break;
874 default:
875 break;
878 if (data)
879 hr = IStream_Write(pres_stream, data, header.dwSize, NULL);
880 HeapFree(GetProcessHeap(), 0, data);
882 IStream_Release(pres_stream);
883 return hr;
886 /* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL.
887 * does no checking of whether src_stgm has a supported tymed, so this should be
888 * done in the caller */
889 static HRESULT copy_stg_medium(CLIPFORMAT cf, STGMEDIUM *dest_stgm,
890 const STGMEDIUM *src_stgm)
892 if (src_stgm->tymed == TYMED_MFPICT)
894 const METAFILEPICT *src_mfpict = GlobalLock(src_stgm->u.hMetaFilePict);
895 METAFILEPICT *dest_mfpict;
897 if (!src_mfpict)
898 return DV_E_STGMEDIUM;
899 dest_stgm->u.hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT));
900 dest_mfpict = GlobalLock(dest_stgm->u.hMetaFilePict);
901 if (!dest_mfpict)
903 GlobalUnlock(src_stgm->u.hMetaFilePict);
904 return E_OUTOFMEMORY;
906 *dest_mfpict = *src_mfpict;
907 dest_mfpict->hMF = CopyMetaFileW(src_mfpict->hMF, NULL);
908 GlobalUnlock(src_stgm->u.hMetaFilePict);
909 GlobalUnlock(dest_stgm->u.hMetaFilePict);
911 else if (src_stgm->tymed != TYMED_NULL)
913 dest_stgm->u.hGlobal = OleDuplicateData(src_stgm->u.hGlobal, cf,
914 GMEM_MOVEABLE);
915 if (!dest_stgm->u.hGlobal)
916 return E_OUTOFMEMORY;
918 dest_stgm->tymed = src_stgm->tymed;
919 dest_stgm->pUnkForRelease = src_stgm->pUnkForRelease;
920 if (dest_stgm->pUnkForRelease)
921 IUnknown_AddRef(dest_stgm->pUnkForRelease);
922 return S_OK;
925 static HGLOBAL synthesize_dib( HBITMAP bm )
927 HDC hdc = GetDC( 0 );
928 BITMAPINFOHEADER header;
929 BITMAPINFO *bmi;
930 HGLOBAL ret = 0;
931 DWORD header_size;
933 memset( &header, 0, sizeof(header) );
934 header.biSize = sizeof(header);
935 if (!GetDIBits( hdc, bm, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done;
937 header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS );
938 if (!(ret = GlobalAlloc( GMEM_MOVEABLE, header_size + header.biSizeImage ))) goto done;
939 bmi = GlobalLock( ret );
940 memset( bmi, 0, header_size );
941 memcpy( bmi, &header, header.biSize );
942 GetDIBits( hdc, bm, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS );
943 GlobalUnlock( ret );
945 done:
946 ReleaseDC( 0, hdc );
947 return ret;
950 static HBITMAP synthesize_bitmap( HGLOBAL dib )
952 HBITMAP ret = 0;
953 BITMAPINFO *bmi;
954 HDC hdc = GetDC( 0 );
956 if ((bmi = GlobalLock( dib )))
958 /* FIXME: validate data size */
959 ret = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT,
960 (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ),
961 bmi, DIB_RGB_COLORS );
962 GlobalUnlock( dib );
964 ReleaseDC( 0, hdc );
965 return ret;
968 static HENHMETAFILE synthesize_emf( HMETAFILEPICT data )
970 METAFILEPICT *pict;
971 HENHMETAFILE emf = 0;
972 UINT size;
973 void *bits;
975 if (!(pict = GlobalLock( data ))) return 0;
977 size = GetMetaFileBitsEx( pict->hMF, 0, NULL );
978 if ((bits = HeapAlloc( GetProcessHeap(), 0, size )))
980 GetMetaFileBitsEx( pict->hMF, size, bits );
981 emf = SetWinMetaFileBits( size, bits, NULL, pict );
982 HeapFree( GetProcessHeap(), 0, bits );
985 GlobalUnlock( data );
986 return emf;
989 static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry,
990 const FORMATETC *formatetc,
991 STGMEDIUM *stgmedium,
992 BOOL fRelease)
994 STGMEDIUM copy;
996 if ((!cache_entry->fmtetc.cfFormat && !formatetc->cfFormat) ||
997 (cache_entry->fmtetc.tymed == TYMED_NULL && formatetc->tymed == TYMED_NULL) ||
998 stgmedium->tymed == TYMED_NULL)
1000 WARN("invalid formatetc\n");
1001 return DV_E_FORMATETC;
1004 cache_entry->dirty = TRUE;
1005 ReleaseStgMedium(&cache_entry->stgmedium);
1007 if (formatetc->cfFormat == CF_BITMAP)
1009 copy.tymed = TYMED_HGLOBAL;
1010 copy.u.hGlobal = synthesize_dib( stgmedium->u.hBitmap );
1011 copy.pUnkForRelease = NULL;
1012 if (fRelease) ReleaseStgMedium(stgmedium);
1013 stgmedium = &copy;
1014 fRelease = TRUE;
1016 else if (formatetc->cfFormat == CF_METAFILEPICT && cache_entry->fmtetc.cfFormat == CF_ENHMETAFILE)
1018 copy.tymed = TYMED_ENHMF;
1019 copy.u.hEnhMetaFile = synthesize_emf( stgmedium->u.hMetaFilePict );
1020 if (fRelease) ReleaseStgMedium(stgmedium);
1021 stgmedium = &copy;
1022 fRelease = TRUE;
1025 if (fRelease)
1027 cache_entry->stgmedium = *stgmedium;
1028 return S_OK;
1030 else
1031 return copy_stg_medium(cache_entry->fmtetc.cfFormat, &cache_entry->stgmedium, stgmedium);
1034 static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, FORMATETC *fmt, STGMEDIUM *stgmedium)
1036 if (cache_entry->stgmedium.tymed == TYMED_NULL && cache_entry->stream)
1038 HRESULT hr = DataCacheEntry_LoadData(cache_entry);
1039 if (FAILED(hr))
1040 return hr;
1042 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1043 return OLE_E_BLANK;
1045 if (fmt->cfFormat == CF_BITMAP)
1047 stgmedium->tymed = TYMED_GDI;
1048 stgmedium->u.hBitmap = synthesize_bitmap( cache_entry->stgmedium.u.hGlobal );
1049 stgmedium->pUnkForRelease = NULL;
1050 return S_OK;
1052 return copy_stg_medium(cache_entry->fmtetc.cfFormat, stgmedium, &cache_entry->stgmedium);
1055 static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry)
1057 ReleaseStgMedium(&cache_entry->stgmedium);
1058 return S_OK;
1061 static inline void DataCacheEntry_HandsOffStorage(DataCacheEntry *cache_entry)
1063 if (cache_entry->stream)
1065 IStream_Release(cache_entry->stream);
1066 cache_entry->stream = NULL;
1070 static inline DWORD tymed_from_cf( DWORD cf )
1072 switch( cf )
1074 case CF_BITMAP: return TYMED_GDI;
1075 case CF_METAFILEPICT: return TYMED_MFPICT;
1076 case CF_ENHMETAFILE: return TYMED_ENHMF;
1077 case CF_DIB:
1078 default: return TYMED_HGLOBAL;
1082 /****************************************************************
1083 * create_automatic_entry
1085 * Creates an appropriate cache entry for one of the CLSID_Picture_
1086 * classes. The connection id of the entry is one. Any pre-existing
1087 * automatic entry is re-assigned a new connection id, and moved to
1088 * the end of the list.
1090 static HRESULT create_automatic_entry(DataCache *cache, const CLSID *clsid)
1092 static const struct data
1094 const CLSID *clsid;
1095 FORMATETC fmt;
1096 } data[] =
1098 { &CLSID_Picture_Dib, { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
1099 { &CLSID_Picture_Metafile, { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
1100 { &CLSID_Picture_EnhMetafile, { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
1101 { NULL }
1103 const struct data *ptr = data;
1104 struct list *head;
1105 DataCacheEntry *entry;
1107 if (IsEqualCLSID( &cache->clsid, clsid )) return S_OK;
1109 /* move and reassign any pre-existing automatic entry */
1110 if ((head = list_head( &cache->cache_list )))
1112 entry = LIST_ENTRY( head, DataCacheEntry, entry );
1113 if (entry->id == 1)
1115 list_remove( &entry->entry );
1116 entry->id = cache->last_cache_id++;
1117 list_add_tail( &cache->cache_list, &entry->entry );
1121 while (ptr->clsid)
1123 if (IsEqualCLSID( clsid, ptr->clsid ))
1124 return DataCache_CreateEntry( cache, &ptr->fmt, 0, TRUE, NULL );
1125 ptr++;
1127 return S_OK;
1130 /*********************************************************
1131 * Method implementation for the non delegating IUnknown
1132 * part of the DataCache class.
1135 /************************************************************************
1136 * DataCache_NDIUnknown_QueryInterface (IUnknown)
1138 * This version of QueryInterface will not delegate its implementation
1139 * to the outer unknown.
1141 static HRESULT WINAPI DataCache_NDIUnknown_QueryInterface(
1142 IUnknown* iface,
1143 REFIID riid,
1144 void** ppvObject)
1146 DataCache *this = impl_from_IUnknown(iface);
1148 if ( ppvObject==0 )
1149 return E_INVALIDARG;
1151 *ppvObject = 0;
1153 if (IsEqualIID(&IID_IUnknown, riid))
1155 if (this->outer_unk == iface) /* non-aggregated, return IUnknown from IOleCache2 */
1156 *ppvObject = &this->IOleCache2_iface;
1157 else
1158 *ppvObject = iface;
1160 else if (IsEqualIID(&IID_IDataObject, riid))
1162 *ppvObject = &this->IDataObject_iface;
1164 else if ( IsEqualIID(&IID_IPersistStorage, riid) ||
1165 IsEqualIID(&IID_IPersist, riid) )
1167 *ppvObject = &this->IPersistStorage_iface;
1169 else if ( IsEqualIID(&IID_IViewObject, riid) ||
1170 IsEqualIID(&IID_IViewObject2, riid) )
1172 *ppvObject = &this->IViewObject2_iface;
1174 else if ( IsEqualIID(&IID_IOleCache, riid) ||
1175 IsEqualIID(&IID_IOleCache2, riid) )
1177 *ppvObject = &this->IOleCache2_iface;
1179 else if ( IsEqualIID(&IID_IOleCacheControl, riid) )
1181 *ppvObject = &this->IOleCacheControl_iface;
1184 if ((*ppvObject)==0)
1186 WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid));
1187 return E_NOINTERFACE;
1190 IUnknown_AddRef((IUnknown*)*ppvObject);
1192 return S_OK;
1195 /************************************************************************
1196 * DataCache_NDIUnknown_AddRef (IUnknown)
1198 * This version of QueryInterface will not delegate its implementation
1199 * to the outer unknown.
1201 static ULONG WINAPI DataCache_NDIUnknown_AddRef(
1202 IUnknown* iface)
1204 DataCache *this = impl_from_IUnknown(iface);
1205 return InterlockedIncrement(&this->ref);
1208 /************************************************************************
1209 * DataCache_NDIUnknown_Release (IUnknown)
1211 * This version of QueryInterface will not delegate its implementation
1212 * to the outer unknown.
1214 static ULONG WINAPI DataCache_NDIUnknown_Release(
1215 IUnknown* iface)
1217 DataCache *this = impl_from_IUnknown(iface);
1218 ULONG ref;
1220 ref = InterlockedDecrement(&this->ref);
1222 if (ref == 0) DataCache_Destroy(this);
1224 return ref;
1227 /*********************************************************
1228 * Method implementation for the IDataObject
1229 * part of the DataCache class.
1232 /************************************************************************
1233 * DataCache_IDataObject_QueryInterface (IUnknown)
1235 static HRESULT WINAPI DataCache_IDataObject_QueryInterface(
1236 IDataObject* iface,
1237 REFIID riid,
1238 void** ppvObject)
1240 DataCache *this = impl_from_IDataObject(iface);
1242 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1245 /************************************************************************
1246 * DataCache_IDataObject_AddRef (IUnknown)
1248 static ULONG WINAPI DataCache_IDataObject_AddRef(
1249 IDataObject* iface)
1251 DataCache *this = impl_from_IDataObject(iface);
1253 return IUnknown_AddRef(this->outer_unk);
1256 /************************************************************************
1257 * DataCache_IDataObject_Release (IUnknown)
1259 static ULONG WINAPI DataCache_IDataObject_Release(
1260 IDataObject* iface)
1262 DataCache *this = impl_from_IDataObject(iface);
1264 return IUnknown_Release(this->outer_unk);
1267 /************************************************************************
1268 * DataCache_GetData
1270 * Get Data from a source dataobject using format pformatetcIn->cfFormat
1272 static HRESULT WINAPI DataCache_GetData(
1273 IDataObject* iface,
1274 LPFORMATETC pformatetcIn,
1275 STGMEDIUM* pmedium)
1277 DataCache *This = impl_from_IDataObject(iface);
1278 DataCacheEntry *cache_entry;
1280 TRACE("(%p, %s, %p)\n", iface, debugstr_formatetc(pformatetcIn), pmedium);
1282 memset(pmedium, 0, sizeof(*pmedium));
1284 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetcIn);
1285 if (!cache_entry)
1286 return OLE_E_BLANK;
1288 return DataCacheEntry_GetData(cache_entry, pformatetcIn, pmedium);
1291 static HRESULT WINAPI DataCache_GetDataHere(
1292 IDataObject* iface,
1293 LPFORMATETC pformatetc,
1294 STGMEDIUM* pmedium)
1296 FIXME("stub\n");
1297 return E_NOTIMPL;
1300 static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt )
1302 DataCache *This = impl_from_IDataObject( iface );
1303 DataCacheEntry *cache_entry;
1305 TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) );
1306 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1308 return cache_entry ? S_OK : S_FALSE;
1311 /************************************************************************
1312 * DataCache_EnumFormatEtc (IDataObject)
1314 * The data cache doesn't implement this method.
1316 static HRESULT WINAPI DataCache_GetCanonicalFormatEtc(
1317 IDataObject* iface,
1318 LPFORMATETC pformatectIn,
1319 LPFORMATETC pformatetcOut)
1321 TRACE("()\n");
1322 return E_NOTIMPL;
1325 /************************************************************************
1326 * DataCache_IDataObject_SetData (IDataObject)
1328 * This method is delegated to the IOleCache2 implementation.
1330 static HRESULT WINAPI DataCache_IDataObject_SetData(
1331 IDataObject* iface,
1332 LPFORMATETC pformatetc,
1333 STGMEDIUM* pmedium,
1334 BOOL fRelease)
1336 IOleCache2* oleCache = NULL;
1337 HRESULT hres;
1339 TRACE("(%p, %p, %p, %d)\n", iface, pformatetc, pmedium, fRelease);
1341 hres = IDataObject_QueryInterface(iface, &IID_IOleCache2, (void**)&oleCache);
1343 if (FAILED(hres))
1344 return E_UNEXPECTED;
1346 hres = IOleCache2_SetData(oleCache, pformatetc, pmedium, fRelease);
1348 IOleCache2_Release(oleCache);
1350 return hres;
1353 /************************************************************************
1354 * DataCache_EnumFormatEtc (IDataObject)
1356 * The data cache doesn't implement this method.
1358 static HRESULT WINAPI DataCache_EnumFormatEtc(
1359 IDataObject* iface,
1360 DWORD dwDirection,
1361 IEnumFORMATETC** ppenumFormatEtc)
1363 TRACE("()\n");
1364 return E_NOTIMPL;
1367 /************************************************************************
1368 * DataCache_DAdvise (IDataObject)
1370 * The data cache doesn't support connections.
1372 static HRESULT WINAPI DataCache_DAdvise(
1373 IDataObject* iface,
1374 FORMATETC* pformatetc,
1375 DWORD advf,
1376 IAdviseSink* pAdvSink,
1377 DWORD* pdwConnection)
1379 TRACE("()\n");
1380 return OLE_E_ADVISENOTSUPPORTED;
1383 /************************************************************************
1384 * DataCache_DUnadvise (IDataObject)
1386 * The data cache doesn't support connections.
1388 static HRESULT WINAPI DataCache_DUnadvise(
1389 IDataObject* iface,
1390 DWORD dwConnection)
1392 TRACE("()\n");
1393 return OLE_E_NOCONNECTION;
1396 /************************************************************************
1397 * DataCache_EnumDAdvise (IDataObject)
1399 * The data cache doesn't support connections.
1401 static HRESULT WINAPI DataCache_EnumDAdvise(
1402 IDataObject* iface,
1403 IEnumSTATDATA** ppenumAdvise)
1405 TRACE("()\n");
1406 return OLE_E_ADVISENOTSUPPORTED;
1409 /*********************************************************
1410 * Method implementation for the IDataObject
1411 * part of the DataCache class.
1414 /************************************************************************
1415 * DataCache_IPersistStorage_QueryInterface (IUnknown)
1417 static HRESULT WINAPI DataCache_IPersistStorage_QueryInterface(
1418 IPersistStorage* iface,
1419 REFIID riid,
1420 void** ppvObject)
1422 DataCache *this = impl_from_IPersistStorage(iface);
1424 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1427 /************************************************************************
1428 * DataCache_IPersistStorage_AddRef (IUnknown)
1430 static ULONG WINAPI DataCache_IPersistStorage_AddRef(
1431 IPersistStorage* iface)
1433 DataCache *this = impl_from_IPersistStorage(iface);
1435 return IUnknown_AddRef(this->outer_unk);
1438 /************************************************************************
1439 * DataCache_IPersistStorage_Release (IUnknown)
1441 static ULONG WINAPI DataCache_IPersistStorage_Release(
1442 IPersistStorage* iface)
1444 DataCache *this = impl_from_IPersistStorage(iface);
1446 return IUnknown_Release(this->outer_unk);
1449 /************************************************************************
1450 * DataCache_GetClassID (IPersistStorage)
1453 static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid)
1455 DataCache *This = impl_from_IPersistStorage( iface );
1457 TRACE( "(%p, %p) returning %s\n", iface, clsid, debugstr_guid(&This->clsid) );
1458 *clsid = This->clsid;
1460 return S_OK;
1463 /************************************************************************
1464 * DataCache_IsDirty (IPersistStorage)
1466 static HRESULT WINAPI DataCache_IsDirty(
1467 IPersistStorage* iface)
1469 DataCache *This = impl_from_IPersistStorage(iface);
1470 DataCacheEntry *cache_entry;
1472 TRACE("(%p)\n", iface);
1474 if (This->dirty)
1475 return S_OK;
1477 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1478 if (cache_entry->dirty)
1479 return S_OK;
1481 return S_FALSE;
1484 /************************************************************************
1485 * DataCache_InitNew (IPersistStorage)
1487 * The data cache implementation of IPersistStorage_InitNew simply stores
1488 * the storage pointer.
1490 static HRESULT WINAPI DataCache_InitNew(
1491 IPersistStorage* iface,
1492 IStorage* pStg)
1494 DataCache *This = impl_from_IPersistStorage(iface);
1495 CLSID clsid;
1496 HRESULT hr;
1498 TRACE("(%p, %p)\n", iface, pStg);
1500 if (This->presentationStorage != NULL)
1501 return CO_E_ALREADYINITIALIZED;
1503 This->presentationStorage = pStg;
1505 IStorage_AddRef(This->presentationStorage);
1506 This->dirty = TRUE;
1507 ReadClassStg( pStg, &clsid );
1508 hr = create_automatic_entry( This, &clsid );
1509 if (FAILED(hr))
1511 IStorage_Release( pStg );
1512 This->presentationStorage = NULL;
1513 return hr;
1515 This->clsid = clsid;
1517 return S_OK;
1521 static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, DWORD advf, IStream *stm,
1522 enum stream_type type )
1524 DataCacheEntry *cache_entry;
1525 HRESULT hr = S_OK;
1527 TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) );
1529 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1530 if (!cache_entry)
1531 hr = DataCache_CreateEntry( This, fmt, advf, FALSE, &cache_entry );
1532 if (SUCCEEDED( hr ))
1534 DataCacheEntry_DiscardData( cache_entry );
1535 if (cache_entry->stream) IStream_Release( cache_entry->stream );
1536 cache_entry->stream = stm;
1537 IStream_AddRef( stm );
1538 cache_entry->stream_type = type;
1539 cache_entry->dirty = FALSE;
1541 return hr;
1544 static HRESULT parse_pres_streams( DataCache *This, IStorage *stg )
1546 HRESULT hr;
1547 IEnumSTATSTG *stat_enum;
1548 STATSTG stat;
1549 IStream *stm;
1550 PresentationDataHeader header;
1551 ULONG actual_read;
1552 CLIPFORMAT clipformat;
1553 FORMATETC fmtetc;
1555 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stat_enum );
1556 if (FAILED( hr )) return hr;
1558 while ((hr = IEnumSTATSTG_Next( stat_enum, 1, &stat, NULL )) == S_OK)
1560 if (DataCache_IsPresentationStream( &stat ))
1562 hr = IStorage_OpenStream( stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1563 0, &stm );
1564 if (SUCCEEDED( hr ))
1566 hr = read_clipformat( stm, &clipformat );
1568 if (hr == S_OK)
1569 hr = IStream_Read( stm, &header, sizeof(header), &actual_read );
1571 if (hr == S_OK && actual_read == sizeof(header))
1573 fmtetc.cfFormat = clipformat;
1574 fmtetc.ptd = NULL; /* FIXME */
1575 fmtetc.dwAspect = header.dvAspect;
1576 fmtetc.lindex = header.lindex;
1577 fmtetc.tymed = tymed_from_cf( clipformat );
1579 add_cache_entry( This, &fmtetc, header.advf, stm, pres_stream );
1581 IStream_Release( stm );
1584 CoTaskMemFree( stat.pwcsName );
1586 IEnumSTATSTG_Release( stat_enum );
1588 return S_OK;
1591 static const FORMATETC static_dib_fmt = { CF_DIB, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1593 static HRESULT parse_contents_stream( DataCache *This, IStorage *stg, IStream *stm )
1595 HRESULT hr;
1596 STATSTG stat;
1597 const FORMATETC *fmt;
1599 hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
1600 if (FAILED( hr )) return hr;
1602 if (IsEqualCLSID( &stat.clsid, &CLSID_Picture_Dib ))
1603 fmt = &static_dib_fmt;
1604 else
1606 FIXME("unsupported format %s\n", debugstr_guid( &stat.clsid ));
1607 return E_FAIL;
1610 return add_cache_entry( This, fmt, 0, stm, contents_stream );
1613 static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0};
1615 /************************************************************************
1616 * DataCache_Load (IPersistStorage)
1618 * The data cache implementation of IPersistStorage_Load doesn't
1619 * actually load anything. Instead, it holds on to the storage pointer
1620 * and it will load the presentation information when the
1621 * IDataObject_GetData or IViewObject2_Draw methods are called.
1623 static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *pStg )
1625 DataCache *This = impl_from_IPersistStorage(iface);
1626 HRESULT hr;
1627 IStream *stm;
1628 CLSID clsid;
1629 DataCacheEntry *entry, *cursor2;
1631 TRACE("(%p, %p)\n", iface, pStg);
1633 IPersistStorage_HandsOffStorage( iface );
1635 LIST_FOR_EACH_ENTRY_SAFE( entry, cursor2, &This->cache_list, DataCacheEntry, entry )
1636 DataCacheEntry_Destroy( This, entry );
1638 ReadClassStg( pStg, &clsid );
1639 hr = create_automatic_entry( This, &clsid );
1640 if (FAILED( hr )) return hr;
1642 This->clsid = clsid;
1644 hr = IStorage_OpenStream( pStg, CONTENTS, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1645 0, &stm );
1646 if (SUCCEEDED( hr ))
1648 hr = parse_contents_stream( This, pStg, stm );
1649 IStream_Release( stm );
1652 if (FAILED(hr))
1653 hr = parse_pres_streams( This, pStg );
1655 if (SUCCEEDED( hr ))
1657 This->dirty = FALSE;
1658 This->presentationStorage = pStg;
1659 IStorage_AddRef( This->presentationStorage );
1662 return hr;
1665 /************************************************************************
1666 * DataCache_Save (IPersistStorage)
1668 * Until we actually connect to a running object and retrieve new
1669 * information to it, we never have to save anything. However, it is
1670 * our responsibility to copy the information when saving to a new
1671 * storage.
1673 static HRESULT WINAPI DataCache_Save(
1674 IPersistStorage* iface,
1675 IStorage* pStg,
1676 BOOL fSameAsLoad)
1678 DataCache *This = impl_from_IPersistStorage(iface);
1679 DataCacheEntry *cache_entry;
1680 BOOL dirty = FALSE;
1681 HRESULT hr = S_OK;
1682 unsigned short stream_number = 0;
1684 TRACE("(%p, %p, %d)\n", iface, pStg, fSameAsLoad);
1686 dirty = This->dirty;
1687 if (!dirty)
1689 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1691 dirty = cache_entry->dirty;
1692 if (dirty)
1693 break;
1697 /* assign stream numbers to the cache entries */
1698 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1700 if (cache_entry->stream_number != stream_number)
1702 cache_entry->dirty = TRUE; /* needs to be written out again */
1703 cache_entry->stream_number = stream_number;
1705 stream_number++;
1708 /* write out the cache entries */
1709 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1711 if (!fSameAsLoad || cache_entry->dirty)
1713 hr = DataCacheEntry_Save(cache_entry, pStg, fSameAsLoad);
1714 if (FAILED(hr))
1715 break;
1717 cache_entry->dirty = FALSE;
1721 This->dirty = FALSE;
1722 return hr;
1725 /************************************************************************
1726 * DataCache_SaveCompleted (IPersistStorage)
1728 * This method is called to tell the cache to release the storage
1729 * pointer it's currently holding.
1731 static HRESULT WINAPI DataCache_SaveCompleted(
1732 IPersistStorage* iface,
1733 IStorage* pStgNew)
1735 TRACE("(%p, %p)\n", iface, pStgNew);
1737 if (pStgNew)
1739 IPersistStorage_HandsOffStorage(iface);
1741 DataCache_Load(iface, pStgNew);
1744 return S_OK;
1747 /************************************************************************
1748 * DataCache_HandsOffStorage (IPersistStorage)
1750 * This method is called to tell the cache to release the storage
1751 * pointer it's currently holding.
1753 static HRESULT WINAPI DataCache_HandsOffStorage(
1754 IPersistStorage* iface)
1756 DataCache *this = impl_from_IPersistStorage(iface);
1757 DataCacheEntry *cache_entry;
1759 TRACE("(%p)\n", iface);
1761 if (this->presentationStorage != NULL)
1763 IStorage_Release(this->presentationStorage);
1764 this->presentationStorage = NULL;
1767 LIST_FOR_EACH_ENTRY(cache_entry, &this->cache_list, DataCacheEntry, entry)
1768 DataCacheEntry_HandsOffStorage(cache_entry);
1770 return S_OK;
1773 /*********************************************************
1774 * Method implementation for the IViewObject2
1775 * part of the DataCache class.
1778 /************************************************************************
1779 * DataCache_IViewObject2_QueryInterface (IUnknown)
1781 static HRESULT WINAPI DataCache_IViewObject2_QueryInterface(
1782 IViewObject2* iface,
1783 REFIID riid,
1784 void** ppvObject)
1786 DataCache *this = impl_from_IViewObject2(iface);
1788 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1791 /************************************************************************
1792 * DataCache_IViewObject2_AddRef (IUnknown)
1794 static ULONG WINAPI DataCache_IViewObject2_AddRef(
1795 IViewObject2* iface)
1797 DataCache *this = impl_from_IViewObject2(iface);
1799 return IUnknown_AddRef(this->outer_unk);
1802 /************************************************************************
1803 * DataCache_IViewObject2_Release (IUnknown)
1805 static ULONG WINAPI DataCache_IViewObject2_Release(
1806 IViewObject2* iface)
1808 DataCache *this = impl_from_IViewObject2(iface);
1810 return IUnknown_Release(this->outer_unk);
1813 /************************************************************************
1814 * DataCache_Draw (IViewObject2)
1816 * This method will draw the cached representation of the object
1817 * to the given device context.
1819 static HRESULT WINAPI DataCache_Draw(
1820 IViewObject2* iface,
1821 DWORD dwDrawAspect,
1822 LONG lindex,
1823 void* pvAspect,
1824 DVTARGETDEVICE* ptd,
1825 HDC hdcTargetDev,
1826 HDC hdcDraw,
1827 LPCRECTL lprcBounds,
1828 LPCRECTL lprcWBounds,
1829 BOOL (CALLBACK *pfnContinue)(ULONG_PTR dwContinue),
1830 ULONG_PTR dwContinue)
1832 DataCache *This = impl_from_IViewObject2(iface);
1833 HRESULT hres;
1834 DataCacheEntry *cache_entry;
1836 TRACE("(%p, %x, %d, %p, %p, %p, %p, %p, %p, %lx)\n",
1837 iface,
1838 dwDrawAspect,
1839 lindex,
1840 pvAspect,
1841 hdcTargetDev,
1842 hdcDraw,
1843 lprcBounds,
1844 lprcWBounds,
1845 pfnContinue,
1846 dwContinue);
1848 if (lprcBounds==NULL)
1849 return E_INVALIDARG;
1851 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1853 /* FIXME: compare ptd too */
1854 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
1855 (cache_entry->fmtetc.lindex != lindex))
1856 continue;
1858 /* if the data hasn't been loaded yet, do it now */
1859 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
1861 hres = DataCacheEntry_LoadData(cache_entry);
1862 if (FAILED(hres))
1863 continue;
1866 /* no data */
1867 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1868 continue;
1870 if (pfnContinue && !pfnContinue(dwContinue)) return E_ABORT;
1872 switch (cache_entry->fmtetc.cfFormat)
1874 case CF_METAFILEPICT:
1877 * We have to be careful not to modify the state of the
1878 * DC.
1880 INT prevMapMode;
1881 SIZE oldWindowExt;
1882 SIZE oldViewportExt;
1883 POINT oldViewportOrg;
1884 METAFILEPICT *mfpict;
1886 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
1887 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
1888 continue;
1890 prevMapMode = SetMapMode(hdcDraw, mfpict->mm);
1892 SetWindowExtEx(hdcDraw,
1893 mfpict->xExt,
1894 mfpict->yExt,
1895 &oldWindowExt);
1897 SetViewportExtEx(hdcDraw,
1898 lprcBounds->right - lprcBounds->left,
1899 lprcBounds->bottom - lprcBounds->top,
1900 &oldViewportExt);
1902 SetViewportOrgEx(hdcDraw,
1903 lprcBounds->left,
1904 lprcBounds->top,
1905 &oldViewportOrg);
1907 PlayMetaFile(hdcDraw, mfpict->hMF);
1909 SetWindowExtEx(hdcDraw,
1910 oldWindowExt.cx,
1911 oldWindowExt.cy,
1912 NULL);
1914 SetViewportExtEx(hdcDraw,
1915 oldViewportExt.cx,
1916 oldViewportExt.cy,
1917 NULL);
1919 SetViewportOrgEx(hdcDraw,
1920 oldViewportOrg.x,
1921 oldViewportOrg.y,
1922 NULL);
1924 SetMapMode(hdcDraw, prevMapMode);
1926 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
1928 return S_OK;
1930 case CF_DIB:
1932 BITMAPINFO *info;
1933 BYTE *bits;
1935 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
1936 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
1937 continue;
1939 bits = (BYTE *) info + bitmap_info_size( info, DIB_RGB_COLORS );
1940 StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top,
1941 lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top,
1942 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
1943 bits, info, DIB_RGB_COLORS, SRCCOPY );
1945 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
1946 return S_OK;
1951 WARN("no data could be found to be drawn\n");
1953 return OLE_E_BLANK;
1956 static HRESULT WINAPI DataCache_GetColorSet(
1957 IViewObject2* iface,
1958 DWORD dwDrawAspect,
1959 LONG lindex,
1960 void* pvAspect,
1961 DVTARGETDEVICE* ptd,
1962 HDC hicTargetDevice,
1963 LOGPALETTE** ppColorSet)
1965 FIXME("stub\n");
1966 return E_NOTIMPL;
1969 static HRESULT WINAPI DataCache_Freeze(
1970 IViewObject2* iface,
1971 DWORD dwDrawAspect,
1972 LONG lindex,
1973 void* pvAspect,
1974 DWORD* pdwFreeze)
1976 FIXME("stub\n");
1977 return E_NOTIMPL;
1980 static HRESULT WINAPI DataCache_Unfreeze(
1981 IViewObject2* iface,
1982 DWORD dwFreeze)
1984 FIXME("stub\n");
1985 return E_NOTIMPL;
1988 /************************************************************************
1989 * DataCache_SetAdvise (IViewObject2)
1991 * This sets-up an advisory sink with the data cache. When the object's
1992 * view changes, this sink is called.
1994 static HRESULT WINAPI DataCache_SetAdvise(
1995 IViewObject2* iface,
1996 DWORD aspects,
1997 DWORD advf,
1998 IAdviseSink* pAdvSink)
2000 DataCache *this = impl_from_IViewObject2(iface);
2002 TRACE("(%p, %x, %x, %p)\n", iface, aspects, advf, pAdvSink);
2005 * A call to this function removes the previous sink
2007 if (this->sinkInterface != NULL)
2009 IAdviseSink_Release(this->sinkInterface);
2010 this->sinkInterface = NULL;
2011 this->sinkAspects = 0;
2012 this->sinkAdviseFlag = 0;
2016 * Now, setup the new one.
2018 if (pAdvSink!=NULL)
2020 this->sinkInterface = pAdvSink;
2021 this->sinkAspects = aspects;
2022 this->sinkAdviseFlag = advf;
2024 IAdviseSink_AddRef(this->sinkInterface);
2028 * When the ADVF_PRIMEFIRST flag is set, we have to advise the
2029 * sink immediately.
2031 if (advf & ADVF_PRIMEFIRST)
2033 DataCache_FireOnViewChange(this, aspects, -1);
2036 return S_OK;
2039 /************************************************************************
2040 * DataCache_GetAdvise (IViewObject2)
2042 * This method queries the current state of the advise sink
2043 * installed on the data cache.
2045 static HRESULT WINAPI DataCache_GetAdvise(
2046 IViewObject2* iface,
2047 DWORD* pAspects,
2048 DWORD* pAdvf,
2049 IAdviseSink** ppAdvSink)
2051 DataCache *this = impl_from_IViewObject2(iface);
2053 TRACE("(%p, %p, %p, %p)\n", iface, pAspects, pAdvf, ppAdvSink);
2056 * Just copy all the requested values.
2058 if (pAspects!=NULL)
2059 *pAspects = this->sinkAspects;
2061 if (pAdvf!=NULL)
2062 *pAdvf = this->sinkAdviseFlag;
2064 if (ppAdvSink!=NULL)
2066 if (this->sinkInterface != NULL)
2067 IAdviseSink_QueryInterface(this->sinkInterface,
2068 &IID_IAdviseSink,
2069 (void**)ppAdvSink);
2070 else *ppAdvSink = NULL;
2073 return S_OK;
2076 /************************************************************************
2077 * DataCache_GetExtent (IViewObject2)
2079 * This method retrieves the "natural" size of this cached object.
2081 static HRESULT WINAPI DataCache_GetExtent(
2082 IViewObject2* iface,
2083 DWORD dwDrawAspect,
2084 LONG lindex,
2085 DVTARGETDEVICE* ptd,
2086 LPSIZEL lpsizel)
2088 DataCache *This = impl_from_IViewObject2(iface);
2089 HRESULT hres = E_FAIL;
2090 DataCacheEntry *cache_entry;
2092 TRACE("(%p, %x, %d, %p, %p)\n",
2093 iface, dwDrawAspect, lindex, ptd, lpsizel);
2095 if (lpsizel==NULL)
2096 return E_POINTER;
2098 lpsizel->cx = 0;
2099 lpsizel->cy = 0;
2101 if (lindex!=-1)
2102 FIXME("Unimplemented flag lindex = %d\n", lindex);
2105 * Right now, we support only the callback from
2106 * the default handler.
2108 if (ptd!=NULL)
2109 FIXME("Unimplemented ptd = %p\n", ptd);
2111 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2113 /* FIXME: compare ptd too */
2114 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
2115 (cache_entry->fmtetc.lindex != lindex))
2116 continue;
2118 /* if the data hasn't been loaded yet, do it now */
2119 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
2121 hres = DataCacheEntry_LoadData(cache_entry);
2122 if (FAILED(hres))
2123 continue;
2126 /* no data */
2127 if (cache_entry->stgmedium.tymed == TYMED_NULL)
2128 continue;
2131 switch (cache_entry->fmtetc.cfFormat)
2133 case CF_METAFILEPICT:
2135 METAFILEPICT *mfpict;
2137 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
2138 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
2139 continue;
2141 lpsizel->cx = mfpict->xExt;
2142 lpsizel->cy = mfpict->yExt;
2144 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
2146 return S_OK;
2148 case CF_DIB:
2150 BITMAPINFOHEADER *info;
2151 LONG x_pels_m, y_pels_m;
2154 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
2155 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
2156 continue;
2158 x_pels_m = info->biXPelsPerMeter;
2159 y_pels_m = info->biYPelsPerMeter;
2161 /* Size in units of 0.01mm (ie. MM_HIMETRIC) */
2162 if (x_pels_m != 0 && y_pels_m != 0)
2164 lpsizel->cx = info->biWidth * 100000 / x_pels_m;
2165 lpsizel->cy = info->biHeight * 100000 / y_pels_m;
2167 else
2169 HDC hdc = GetDC( 0 );
2170 lpsizel->cx = info->biWidth * 2540 / GetDeviceCaps( hdc, LOGPIXELSX );
2171 lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY );
2173 ReleaseDC( 0, hdc );
2176 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
2178 return S_OK;
2183 WARN("no data could be found to get the extents from\n");
2186 * This method returns OLE_E_BLANK when it fails.
2188 return OLE_E_BLANK;
2192 /*********************************************************
2193 * Method implementation for the IOleCache2
2194 * part of the DataCache class.
2197 /************************************************************************
2198 * DataCache_IOleCache2_QueryInterface (IUnknown)
2200 static HRESULT WINAPI DataCache_IOleCache2_QueryInterface(
2201 IOleCache2* iface,
2202 REFIID riid,
2203 void** ppvObject)
2205 DataCache *this = impl_from_IOleCache2(iface);
2207 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2210 /************************************************************************
2211 * DataCache_IOleCache2_AddRef (IUnknown)
2213 static ULONG WINAPI DataCache_IOleCache2_AddRef(
2214 IOleCache2* iface)
2216 DataCache *this = impl_from_IOleCache2(iface);
2218 return IUnknown_AddRef(this->outer_unk);
2221 /************************************************************************
2222 * DataCache_IOleCache2_Release (IUnknown)
2224 static ULONG WINAPI DataCache_IOleCache2_Release(
2225 IOleCache2* iface)
2227 DataCache *this = impl_from_IOleCache2(iface);
2229 return IUnknown_Release(this->outer_unk);
2232 /*****************************************************************************
2233 * setup_sink
2235 * Set up the sink connection to the running object.
2237 static HRESULT setup_sink(DataCache *This, DataCacheEntry *cache_entry)
2239 HRESULT hr = S_FALSE;
2240 DWORD flags;
2242 /* Clear the ADVFCACHE_* bits. Native also sets the two highest bits for some reason. */
2243 flags = cache_entry->advise_flags & ~(ADVFCACHE_NOHANDLER | ADVFCACHE_FORCEBUILTIN | ADVFCACHE_ONSAVE);
2245 if(This->running_object)
2246 if(!(flags & ADVF_NODATA))
2247 hr = IDataObject_DAdvise(This->running_object, &cache_entry->fmtetc, flags,
2248 &This->IAdviseSink_iface, &cache_entry->sink_id);
2249 return hr;
2252 static HRESULT WINAPI DataCache_Cache(
2253 IOleCache2* iface,
2254 FORMATETC* pformatetc,
2255 DWORD advf,
2256 DWORD* pdwConnection)
2258 DataCache *This = impl_from_IOleCache2(iface);
2259 DataCacheEntry *cache_entry;
2260 HRESULT hr;
2261 FORMATETC fmt_cpy;
2263 TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection);
2265 if (!pformatetc || !pdwConnection)
2266 return E_INVALIDARG;
2268 TRACE("pformatetc = %s\n", debugstr_formatetc(pformatetc));
2270 fmt_cpy = *pformatetc; /* No need for a deep copy */
2271 if (fmt_cpy.cfFormat == CF_BITMAP && fmt_cpy.tymed == TYMED_GDI)
2273 fmt_cpy.cfFormat = CF_DIB;
2274 fmt_cpy.tymed = TYMED_HGLOBAL;
2277 /* View caching DVASPECT_ICON gets converted to CF_METAFILEPICT */
2278 if (fmt_cpy.dwAspect == DVASPECT_ICON && fmt_cpy.cfFormat == 0)
2280 fmt_cpy.cfFormat = CF_METAFILEPICT;
2281 fmt_cpy.tymed = TYMED_MFPICT;
2284 *pdwConnection = 0;
2286 cache_entry = DataCache_GetEntryForFormatEtc(This, &fmt_cpy);
2287 if (cache_entry)
2289 TRACE("found an existing cache entry\n");
2290 *pdwConnection = cache_entry->id;
2291 return CACHE_S_SAMECACHE;
2294 hr = DataCache_CreateEntry(This, &fmt_cpy, advf, FALSE, &cache_entry);
2296 if (SUCCEEDED(hr))
2298 *pdwConnection = cache_entry->id;
2299 setup_sink(This, cache_entry);
2302 return hr;
2305 static HRESULT WINAPI DataCache_Uncache(
2306 IOleCache2* iface,
2307 DWORD dwConnection)
2309 DataCache *This = impl_from_IOleCache2(iface);
2310 DataCacheEntry *cache_entry;
2312 TRACE("(%d)\n", dwConnection);
2314 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2315 if (cache_entry->id == dwConnection)
2317 DataCacheEntry_Destroy(This, cache_entry);
2318 return S_OK;
2321 WARN("no connection found for %d\n", dwConnection);
2323 return OLE_E_NOCONNECTION;
2326 static HRESULT WINAPI DataCache_EnumCache(IOleCache2 *iface,
2327 IEnumSTATDATA **enum_stat)
2329 DataCache *This = impl_from_IOleCache2( iface );
2330 DataCacheEntry *cache_entry;
2331 int i = 0, count = 0;
2332 STATDATA *data;
2333 HRESULT hr;
2335 TRACE( "(%p, %p)\n", This, enum_stat );
2337 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2339 count++;
2340 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2341 count++;
2344 data = CoTaskMemAlloc( count * sizeof(*data) );
2345 if (!data) return E_OUTOFMEMORY;
2347 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2349 if (i == count) goto fail;
2350 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2351 if (FAILED(hr)) goto fail;
2352 data[i].advf = cache_entry->advise_flags;
2353 data[i].pAdvSink = NULL;
2354 data[i].dwConnection = cache_entry->id;
2355 i++;
2357 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2359 if (i == count) goto fail;
2360 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2361 if (FAILED(hr)) goto fail;
2362 data[i].formatetc.cfFormat = CF_BITMAP;
2363 data[i].formatetc.tymed = TYMED_GDI;
2364 data[i].advf = cache_entry->advise_flags;
2365 data[i].pAdvSink = NULL;
2366 data[i].dwConnection = cache_entry->id;
2367 i++;
2371 hr = EnumSTATDATA_Construct( NULL, 0, i, data, FALSE, enum_stat );
2372 if (SUCCEEDED(hr)) return hr;
2374 fail:
2375 while (i--) CoTaskMemFree( data[i].formatetc.ptd );
2376 CoTaskMemFree( data );
2377 return hr;
2380 static HRESULT WINAPI DataCache_InitCache( IOleCache2 *iface, IDataObject *data )
2382 TRACE( "(%p %p)\n", iface, data );
2383 return IOleCache2_UpdateCache( iface, data, UPDFCACHE_ALLBUTNODATACACHE, NULL );
2386 static HRESULT WINAPI DataCache_IOleCache2_SetData(
2387 IOleCache2* iface,
2388 FORMATETC* pformatetc,
2389 STGMEDIUM* pmedium,
2390 BOOL fRelease)
2392 DataCache *This = impl_from_IOleCache2(iface);
2393 DataCacheEntry *cache_entry;
2394 HRESULT hr;
2396 TRACE("(%p, %p, %s)\n", pformatetc, pmedium, fRelease ? "TRUE" : "FALSE");
2397 TRACE("formatetc = %s\n", debugstr_formatetc(pformatetc));
2399 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetc);
2400 if (cache_entry)
2402 hr = DataCacheEntry_SetData(cache_entry, pformatetc, pmedium, fRelease);
2404 if (SUCCEEDED(hr))
2405 DataCache_FireOnViewChange(This, cache_entry->fmtetc.dwAspect,
2406 cache_entry->fmtetc.lindex);
2408 return hr;
2410 WARN("cache entry not found\n");
2412 return OLE_E_BLANK;
2415 static BOOL entry_updateable( DataCacheEntry *entry, DWORD mode )
2417 BOOL is_blank = entry->stgmedium.tymed == TYMED_NULL;
2419 if ((mode & UPDFCACHE_ONLYIFBLANK) && !is_blank) return FALSE;
2421 if ((mode & UPDFCACHE_NODATACACHE) && (entry->advise_flags & ADVF_NODATA)) return TRUE;
2422 if ((mode & UPDFCACHE_ONSAVECACHE) && (entry->advise_flags & ADVFCACHE_ONSAVE)) return TRUE;
2423 if ((mode & UPDFCACHE_ONSTOPCACHE) && (entry->advise_flags & ADVF_DATAONSTOP)) return TRUE;
2424 if ((mode & UPDFCACHE_NORMALCACHE) && (entry->advise_flags == 0)) return TRUE;
2425 if ((mode & UPDFCACHE_IFBLANK) && (is_blank && !(entry->advise_flags & ADVF_NODATA))) return TRUE;
2427 return FALSE;
2430 static HRESULT WINAPI DataCache_UpdateCache( IOleCache2 *iface, IDataObject *data,
2431 DWORD mode, void *reserved )
2433 DataCache *This = impl_from_IOleCache2(iface);
2434 DataCacheEntry *cache_entry;
2435 STGMEDIUM med;
2436 HRESULT hr = S_OK;
2437 CLIPFORMAT view_list[] = { CF_METAFILEPICT, CF_ENHMETAFILE, CF_DIB, CF_BITMAP };
2438 FORMATETC fmt;
2439 int i, slots = 0;
2440 BOOL done_one = FALSE;
2442 TRACE( "(%p %p %08x %p)\n", iface, data, mode, reserved );
2444 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2446 slots++;
2448 if (!entry_updateable( cache_entry, mode ))
2450 done_one = TRUE;
2451 continue;
2454 fmt = cache_entry->fmtetc;
2456 if (fmt.cfFormat)
2458 hr = IDataObject_GetData( data, &fmt, &med );
2459 if (hr != S_OK && fmt.cfFormat == CF_DIB)
2461 fmt.cfFormat = CF_BITMAP;
2462 fmt.tymed = TYMED_GDI;
2463 hr = IDataObject_GetData( data, &fmt, &med );
2465 if (hr != S_OK && fmt.cfFormat == CF_ENHMETAFILE)
2467 fmt.cfFormat = CF_METAFILEPICT;
2468 fmt.tymed = TYMED_MFPICT;
2469 hr = IDataObject_GetData( data, &fmt, &med );
2471 if (hr == S_OK)
2473 hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2474 if (hr != S_OK) ReleaseStgMedium( &med );
2475 else done_one = TRUE;
2478 else
2480 for (i = 0; i < sizeof(view_list) / sizeof(view_list[0]); i++)
2482 fmt.cfFormat = view_list[i];
2483 fmt.tymed = tymed_from_cf( fmt.cfFormat );
2484 hr = IDataObject_QueryGetData( data, &fmt );
2485 if (hr == S_OK)
2487 hr = IDataObject_GetData( data, &fmt, &med );
2488 if (hr == S_OK)
2490 if (fmt.cfFormat == CF_BITMAP)
2492 cache_entry->fmtetc.cfFormat = CF_DIB;
2493 cache_entry->fmtetc.tymed = TYMED_HGLOBAL;
2495 else
2497 cache_entry->fmtetc.cfFormat = fmt.cfFormat;
2498 cache_entry->fmtetc.tymed = fmt.tymed;
2500 hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2501 if (hr != S_OK) ReleaseStgMedium( &med );
2502 else done_one = TRUE;
2503 break;
2510 return (!slots || done_one) ? S_OK : CACHE_E_NOCACHE_UPDATED;
2513 static HRESULT WINAPI DataCache_DiscardCache(
2514 IOleCache2* iface,
2515 DWORD dwDiscardOptions)
2517 DataCache *This = impl_from_IOleCache2(iface);
2518 DataCacheEntry *cache_entry;
2519 HRESULT hr = S_OK;
2521 TRACE("(%d)\n", dwDiscardOptions);
2523 if (dwDiscardOptions == DISCARDCACHE_SAVEIFDIRTY)
2524 hr = DataCache_Save(&This->IPersistStorage_iface, This->presentationStorage, TRUE);
2526 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2528 hr = DataCacheEntry_DiscardData(cache_entry);
2529 if (FAILED(hr))
2530 break;
2533 return hr;
2537 /*********************************************************
2538 * Method implementation for the IOleCacheControl
2539 * part of the DataCache class.
2542 /************************************************************************
2543 * DataCache_IOleCacheControl_QueryInterface (IUnknown)
2545 static HRESULT WINAPI DataCache_IOleCacheControl_QueryInterface(
2546 IOleCacheControl* iface,
2547 REFIID riid,
2548 void** ppvObject)
2550 DataCache *this = impl_from_IOleCacheControl(iface);
2552 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2555 /************************************************************************
2556 * DataCache_IOleCacheControl_AddRef (IUnknown)
2558 static ULONG WINAPI DataCache_IOleCacheControl_AddRef(
2559 IOleCacheControl* iface)
2561 DataCache *this = impl_from_IOleCacheControl(iface);
2563 return IUnknown_AddRef(this->outer_unk);
2566 /************************************************************************
2567 * DataCache_IOleCacheControl_Release (IUnknown)
2569 static ULONG WINAPI DataCache_IOleCacheControl_Release(
2570 IOleCacheControl* iface)
2572 DataCache *this = impl_from_IOleCacheControl(iface);
2574 return IUnknown_Release(this->outer_unk);
2577 /************************************************************************
2578 * DataCache_OnRun (IOleCacheControl)
2580 static HRESULT WINAPI DataCache_OnRun(IOleCacheControl* iface, IDataObject *data_obj)
2582 DataCache *This = impl_from_IOleCacheControl(iface);
2583 DataCacheEntry *cache_entry;
2585 TRACE("(%p)->(%p)\n", iface, data_obj);
2587 if(This->running_object) return S_OK;
2589 /* No reference is taken on the data object */
2590 This->running_object = data_obj;
2592 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2594 setup_sink(This, cache_entry);
2597 return S_OK;
2600 /************************************************************************
2601 * DataCache_OnStop (IOleCacheControl)
2603 static HRESULT WINAPI DataCache_OnStop(IOleCacheControl* iface)
2605 DataCache *This = impl_from_IOleCacheControl(iface);
2606 DataCacheEntry *cache_entry;
2608 TRACE("(%p)\n", iface);
2610 if(!This->running_object) return S_OK;
2612 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2614 if(cache_entry->sink_id)
2616 IDataObject_DUnadvise(This->running_object, cache_entry->sink_id);
2617 cache_entry->sink_id = 0;
2621 /* No ref taken in OnRun, so no Release call here */
2622 This->running_object = NULL;
2623 return S_OK;
2626 /************************************************************************
2627 * IAdviseSink methods.
2628 * This behaves as an internal object to the data cache. QI'ing its ptr doesn't
2629 * give access to the cache's other interfaces. We don't maintain a ref count,
2630 * the object exists as long as the cache is around.
2632 static HRESULT WINAPI DataCache_IAdviseSink_QueryInterface(IAdviseSink *iface, REFIID iid, void **obj)
2634 *obj = NULL;
2635 if (IsEqualIID(&IID_IUnknown, iid) ||
2636 IsEqualIID(&IID_IAdviseSink, iid))
2638 *obj = iface;
2641 if(*obj)
2643 IAdviseSink_AddRef(iface);
2644 return S_OK;
2646 return E_NOINTERFACE;
2649 static ULONG WINAPI DataCache_IAdviseSink_AddRef(IAdviseSink *iface)
2651 return 2;
2654 static ULONG WINAPI DataCache_IAdviseSink_Release(IAdviseSink *iface)
2656 return 1;
2659 static void WINAPI DataCache_OnDataChange(IAdviseSink *iface, FORMATETC *fmt, STGMEDIUM *med)
2661 DataCache *This = impl_from_IAdviseSink(iface);
2662 TRACE("(%p)->(%s, %p)\n", This, debugstr_formatetc(fmt), med);
2663 IOleCache2_SetData(&This->IOleCache2_iface, fmt, med, FALSE);
2666 static void WINAPI DataCache_OnViewChange(IAdviseSink *iface, DWORD aspect, LONG index)
2668 FIXME("stub\n");
2671 static void WINAPI DataCache_OnRename(IAdviseSink *iface, IMoniker *mk)
2673 FIXME("stub\n");
2676 static void WINAPI DataCache_OnSave(IAdviseSink *iface)
2678 FIXME("stub\n");
2681 static void WINAPI DataCache_OnClose(IAdviseSink *iface)
2683 FIXME("stub\n");
2687 * Virtual function tables for the DataCache class.
2689 static const IUnknownVtbl DataCache_NDIUnknown_VTable =
2691 DataCache_NDIUnknown_QueryInterface,
2692 DataCache_NDIUnknown_AddRef,
2693 DataCache_NDIUnknown_Release
2696 static const IDataObjectVtbl DataCache_IDataObject_VTable =
2698 DataCache_IDataObject_QueryInterface,
2699 DataCache_IDataObject_AddRef,
2700 DataCache_IDataObject_Release,
2701 DataCache_GetData,
2702 DataCache_GetDataHere,
2703 DataCache_QueryGetData,
2704 DataCache_GetCanonicalFormatEtc,
2705 DataCache_IDataObject_SetData,
2706 DataCache_EnumFormatEtc,
2707 DataCache_DAdvise,
2708 DataCache_DUnadvise,
2709 DataCache_EnumDAdvise
2712 static const IPersistStorageVtbl DataCache_IPersistStorage_VTable =
2714 DataCache_IPersistStorage_QueryInterface,
2715 DataCache_IPersistStorage_AddRef,
2716 DataCache_IPersistStorage_Release,
2717 DataCache_GetClassID,
2718 DataCache_IsDirty,
2719 DataCache_InitNew,
2720 DataCache_Load,
2721 DataCache_Save,
2722 DataCache_SaveCompleted,
2723 DataCache_HandsOffStorage
2726 static const IViewObject2Vtbl DataCache_IViewObject2_VTable =
2728 DataCache_IViewObject2_QueryInterface,
2729 DataCache_IViewObject2_AddRef,
2730 DataCache_IViewObject2_Release,
2731 DataCache_Draw,
2732 DataCache_GetColorSet,
2733 DataCache_Freeze,
2734 DataCache_Unfreeze,
2735 DataCache_SetAdvise,
2736 DataCache_GetAdvise,
2737 DataCache_GetExtent
2740 static const IOleCache2Vtbl DataCache_IOleCache2_VTable =
2742 DataCache_IOleCache2_QueryInterface,
2743 DataCache_IOleCache2_AddRef,
2744 DataCache_IOleCache2_Release,
2745 DataCache_Cache,
2746 DataCache_Uncache,
2747 DataCache_EnumCache,
2748 DataCache_InitCache,
2749 DataCache_IOleCache2_SetData,
2750 DataCache_UpdateCache,
2751 DataCache_DiscardCache
2754 static const IOleCacheControlVtbl DataCache_IOleCacheControl_VTable =
2756 DataCache_IOleCacheControl_QueryInterface,
2757 DataCache_IOleCacheControl_AddRef,
2758 DataCache_IOleCacheControl_Release,
2759 DataCache_OnRun,
2760 DataCache_OnStop
2763 static const IAdviseSinkVtbl DataCache_IAdviseSink_VTable =
2765 DataCache_IAdviseSink_QueryInterface,
2766 DataCache_IAdviseSink_AddRef,
2767 DataCache_IAdviseSink_Release,
2768 DataCache_OnDataChange,
2769 DataCache_OnViewChange,
2770 DataCache_OnRename,
2771 DataCache_OnSave,
2772 DataCache_OnClose
2775 /*********************************************************
2776 * Method implementation for DataCache class.
2778 static DataCache* DataCache_Construct(
2779 REFCLSID clsid,
2780 LPUNKNOWN pUnkOuter)
2782 DataCache* newObject = 0;
2785 * Allocate space for the object.
2787 newObject = HeapAlloc(GetProcessHeap(), 0, sizeof(DataCache));
2789 if (newObject==0)
2790 return newObject;
2793 * Initialize the virtual function table.
2795 newObject->IDataObject_iface.lpVtbl = &DataCache_IDataObject_VTable;
2796 newObject->IUnknown_inner.lpVtbl = &DataCache_NDIUnknown_VTable;
2797 newObject->IPersistStorage_iface.lpVtbl = &DataCache_IPersistStorage_VTable;
2798 newObject->IViewObject2_iface.lpVtbl = &DataCache_IViewObject2_VTable;
2799 newObject->IOleCache2_iface.lpVtbl = &DataCache_IOleCache2_VTable;
2800 newObject->IOleCacheControl_iface.lpVtbl = &DataCache_IOleCacheControl_VTable;
2801 newObject->IAdviseSink_iface.lpVtbl = &DataCache_IAdviseSink_VTable;
2802 newObject->outer_unk = pUnkOuter ? pUnkOuter : &newObject->IUnknown_inner;
2803 newObject->ref = 1;
2806 * Initialize the other members of the structure.
2808 newObject->sinkAspects = 0;
2809 newObject->sinkAdviseFlag = 0;
2810 newObject->sinkInterface = 0;
2811 newObject->clsid = CLSID_NULL;
2812 newObject->presentationStorage = NULL;
2813 list_init(&newObject->cache_list);
2814 newObject->last_cache_id = 2;
2815 newObject->dirty = FALSE;
2816 newObject->running_object = NULL;
2818 create_automatic_entry( newObject, clsid );
2819 newObject->clsid = *clsid;
2821 return newObject;
2824 /******************************************************************************
2825 * CreateDataCache [OLE32.@]
2827 * Creates a data cache to allow an object to render one or more of its views,
2828 * whether running or not.
2830 * PARAMS
2831 * pUnkOuter [I] Outer unknown for the object.
2832 * rclsid [I]
2833 * riid [I] IID of interface to return.
2834 * ppvObj [O] Address where the data cache object will be stored on return.
2836 * RETURNS
2837 * Success: S_OK.
2838 * Failure: HRESULT code.
2840 * NOTES
2841 * The following interfaces are supported by the returned data cache object:
2842 * IOleCache, IOleCache2, IOleCacheControl, IPersistStorage, IDataObject,
2843 * IViewObject and IViewObject2.
2845 HRESULT WINAPI CreateDataCache(
2846 LPUNKNOWN pUnkOuter,
2847 REFCLSID rclsid,
2848 REFIID riid,
2849 LPVOID* ppvObj)
2851 DataCache* newCache = NULL;
2852 HRESULT hr = S_OK;
2854 TRACE("(%s, %p, %s, %p)\n", debugstr_guid(rclsid), pUnkOuter, debugstr_guid(riid), ppvObj);
2857 * Sanity check
2859 if (ppvObj==0)
2860 return E_POINTER;
2862 *ppvObj = 0;
2865 * If this cache is constructed for aggregation, make sure
2866 * the caller is requesting the IUnknown interface.
2867 * This is necessary because it's the only time the non-delegating
2868 * IUnknown pointer can be returned to the outside.
2870 if ( pUnkOuter && !IsEqualIID(&IID_IUnknown, riid) )
2871 return E_INVALIDARG;
2874 * Try to construct a new instance of the class.
2876 newCache = DataCache_Construct(rclsid,
2877 pUnkOuter);
2879 if (newCache == 0)
2880 return E_OUTOFMEMORY;
2882 hr = IUnknown_QueryInterface(&newCache->IUnknown_inner, riid, ppvObj);
2883 IUnknown_Release(&newCache->IUnknown_inner);
2885 return hr;