ole32: A presentation cache for DVASPECT_ICON must have format CF_METAFILEPICT.
[wine.git] / dlls / ole32 / datacache.c
blob2bb0948c5d3709e2d30a80c75d82180f5121f514
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 /* the clipboard format of the data */
105 CLIPFORMAT data_cf;
106 /* cached data */
107 STGMEDIUM stgmedium;
109 * This stream pointer is set through a call to
110 * IPersistStorage_Load. This is where the visual
111 * representation of the object is stored.
113 IStream *stream;
114 enum stream_type stream_type;
115 /* connection ID */
116 DWORD id;
117 /* dirty flag */
118 BOOL dirty;
119 /* stream number (-1 if not set ) */
120 unsigned short stream_number;
121 /* sink id set when object is running */
122 DWORD sink_id;
123 /* Advise sink flags */
124 DWORD advise_flags;
125 } DataCacheEntry;
127 /****************************************************************************
128 * DataCache
130 struct DataCache
133 * List all interface here
135 IUnknown IUnknown_inner;
136 IDataObject IDataObject_iface;
137 IPersistStorage IPersistStorage_iface;
138 IViewObject2 IViewObject2_iface;
139 IOleCache2 IOleCache2_iface;
140 IOleCacheControl IOleCacheControl_iface;
142 /* The sink that is connected to a remote object.
143 The other interfaces are not available by QI'ing the sink and vice-versa */
144 IAdviseSink IAdviseSink_iface;
147 * Reference count of this object
149 LONG ref;
152 * IUnknown implementation of the outer object.
154 IUnknown *outer_unk;
157 * The user of this object can setup ONE advise sink
158 * connection with the object. These parameters describe
159 * that connection.
161 DWORD sinkAspects;
162 DWORD sinkAdviseFlag;
163 IAdviseSink *sinkInterface;
165 CLSID clsid;
166 IStorage *presentationStorage;
168 /* list of cache entries */
169 struct list cache_list;
170 /* last id assigned to an entry */
171 DWORD last_cache_id;
172 /* dirty flag */
173 BOOL dirty;
174 /* running object set by OnRun */
175 IDataObject *running_object;
178 typedef struct DataCache DataCache;
181 * Here, I define utility macros to help with the casting of the
182 * "this" parameter.
183 * There is a version to accommodate all of the VTables implemented
184 * by this object.
187 static inline DataCache *impl_from_IDataObject( IDataObject *iface )
189 return CONTAINING_RECORD(iface, DataCache, IDataObject_iface);
192 static inline DataCache *impl_from_IUnknown( IUnknown *iface )
194 return CONTAINING_RECORD(iface, DataCache, IUnknown_inner);
197 static inline DataCache *impl_from_IPersistStorage( IPersistStorage *iface )
199 return CONTAINING_RECORD(iface, DataCache, IPersistStorage_iface);
202 static inline DataCache *impl_from_IViewObject2( IViewObject2 *iface )
204 return CONTAINING_RECORD(iface, DataCache, IViewObject2_iface);
207 static inline DataCache *impl_from_IOleCache2( IOleCache2 *iface )
209 return CONTAINING_RECORD(iface, DataCache, IOleCache2_iface);
212 static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface )
214 return CONTAINING_RECORD(iface, DataCache, IOleCacheControl_iface);
217 static inline DataCache *impl_from_IAdviseSink( IAdviseSink *iface )
219 return CONTAINING_RECORD(iface, DataCache, IAdviseSink_iface);
222 const char *debugstr_formatetc(const FORMATETC *formatetc)
224 return wine_dbg_sprintf("{ cfFormat = 0x%x, ptd = %p, dwAspect = %d, lindex = %d, tymed = %d }",
225 formatetc->cfFormat, formatetc->ptd, formatetc->dwAspect,
226 formatetc->lindex, formatetc->tymed);
229 /***********************************************************************
230 * bitmap_info_size
232 * Return the size of the bitmap info structure including color table.
234 static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
236 unsigned int colors, size, masks = 0;
238 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
240 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
241 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
242 return sizeof(BITMAPCOREHEADER) + colors *
243 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
245 else /* assume BITMAPINFOHEADER */
247 colors = info->bmiHeader.biClrUsed;
248 if (colors > 256) /* buffer overflow otherwise */
249 colors = 256;
250 if (!colors && (info->bmiHeader.biBitCount <= 8))
251 colors = 1 << info->bmiHeader.biBitCount;
252 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
253 size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
254 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
258 static void DataCacheEntry_Destroy(DataCache *cache, DataCacheEntry *cache_entry)
260 list_remove(&cache_entry->entry);
261 if (cache_entry->stream)
262 IStream_Release(cache_entry->stream);
263 CoTaskMemFree(cache_entry->fmtetc.ptd);
264 ReleaseStgMedium(&cache_entry->stgmedium);
265 if(cache_entry->sink_id)
266 IDataObject_DUnadvise(cache->running_object, cache_entry->sink_id);
268 HeapFree(GetProcessHeap(), 0, cache_entry);
271 static void DataCache_Destroy(
272 DataCache* ptrToDestroy)
274 DataCacheEntry *cache_entry, *next_cache_entry;
276 TRACE("()\n");
278 if (ptrToDestroy->sinkInterface != NULL)
280 IAdviseSink_Release(ptrToDestroy->sinkInterface);
281 ptrToDestroy->sinkInterface = NULL;
284 LIST_FOR_EACH_ENTRY_SAFE(cache_entry, next_cache_entry, &ptrToDestroy->cache_list, DataCacheEntry, entry)
285 DataCacheEntry_Destroy(ptrToDestroy, cache_entry);
287 if (ptrToDestroy->presentationStorage != NULL)
289 IStorage_Release(ptrToDestroy->presentationStorage);
290 ptrToDestroy->presentationStorage = NULL;
294 * Free the datacache pointer.
296 HeapFree(GetProcessHeap(), 0, ptrToDestroy);
299 static DataCacheEntry *DataCache_GetEntryForFormatEtc(DataCache *This, const FORMATETC *formatetc)
301 DataCacheEntry *cache_entry;
302 FORMATETC fmt = *formatetc;
304 if (fmt.cfFormat == CF_BITMAP)
306 fmt.cfFormat = CF_DIB;
307 fmt.tymed = TYMED_HGLOBAL;
310 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
312 /* FIXME: also compare DVTARGETDEVICEs */
313 if ((fmt.cfFormat == cache_entry->fmtetc.cfFormat) &&
314 (fmt.dwAspect == cache_entry->fmtetc.dwAspect) &&
315 (fmt.lindex == cache_entry->fmtetc.lindex) &&
316 ((fmt.tymed == cache_entry->fmtetc.tymed) || !cache_entry->fmtetc.cfFormat)) /* tymed is ignored for view caching */
317 return cache_entry;
319 return NULL;
322 /* checks that the clipformat and tymed are valid and returns an error if they
323 * aren't and CACHE_S_NOTSUPPORTED if they are valid, but can't be rendered by
324 * DataCache_Draw */
325 static HRESULT check_valid_formatetc( const FORMATETC *fmt )
327 /* DVASPECT_ICON must be CF_METAFILEPICT */
328 if (fmt->dwAspect == DVASPECT_ICON && fmt->cfFormat != CF_METAFILEPICT)
329 return DV_E_FORMATETC;
331 if (!fmt->cfFormat || !fmt->tymed ||
332 (fmt->cfFormat == CF_METAFILEPICT && fmt->tymed == TYMED_MFPICT) ||
333 (fmt->cfFormat == CF_BITMAP && fmt->tymed == TYMED_GDI) ||
334 (fmt->cfFormat == CF_DIB && fmt->tymed == TYMED_HGLOBAL) ||
335 (fmt->cfFormat == CF_ENHMETAFILE && fmt->tymed == TYMED_ENHMF))
336 return S_OK;
337 else if (fmt->tymed == TYMED_HGLOBAL)
338 return CACHE_S_FORMATETC_NOTSUPPORTED;
339 else
341 WARN("invalid clipformat/tymed combination: %d/%d\n", fmt->cfFormat, fmt->tymed);
342 return DV_E_TYMED;
346 static BOOL init_cache_entry(DataCacheEntry *entry, const FORMATETC *fmt, DWORD advf,
347 DWORD id)
349 HRESULT hr;
351 hr = copy_formatetc(&entry->fmtetc, fmt);
352 if (FAILED(hr)) return FALSE;
354 entry->data_cf = 0;
355 entry->stgmedium.tymed = TYMED_NULL;
356 entry->stgmedium.pUnkForRelease = NULL;
357 entry->stream = NULL;
358 entry->stream_type = no_stream;
359 entry->id = id;
360 entry->dirty = TRUE;
361 entry->stream_number = -1;
362 entry->sink_id = 0;
363 entry->advise_flags = advf;
365 return TRUE;
368 static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc, DWORD advf,
369 BOOL automatic, DataCacheEntry **cache_entry)
371 HRESULT hr;
372 DWORD id = automatic ? 1 : This->last_cache_id;
373 DataCacheEntry *entry;
375 hr = check_valid_formatetc( formatetc );
376 if (FAILED(hr))
377 return hr;
378 if (hr == CACHE_S_FORMATETC_NOTSUPPORTED)
379 TRACE("creating unsupported format %d\n", formatetc->cfFormat);
381 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
382 if (!entry)
383 return E_OUTOFMEMORY;
385 if (!init_cache_entry(entry, formatetc, advf, id))
386 goto fail;
388 if (automatic)
389 list_add_head(&This->cache_list, &entry->entry);
390 else
392 list_add_tail(&This->cache_list, &entry->entry);
393 This->last_cache_id++;
396 if (cache_entry) *cache_entry = entry;
397 return hr;
399 fail:
400 HeapFree(GetProcessHeap(), 0, entry);
401 return E_OUTOFMEMORY;
404 /************************************************************************
405 * DataCache_FireOnViewChange
407 * This method will fire an OnViewChange notification to the advise
408 * sink registered with the datacache.
410 * See IAdviseSink::OnViewChange for more details.
412 static void DataCache_FireOnViewChange(
413 DataCache* this,
414 DWORD aspect,
415 LONG lindex)
417 TRACE("(%p, %x, %d)\n", this, aspect, lindex);
420 * The sink supplies a filter when it registers
421 * we make sure we only send the notifications when that
422 * filter matches.
424 if ((this->sinkAspects & aspect) != 0)
426 if (this->sinkInterface != NULL)
428 IAdviseSink_OnViewChange(this->sinkInterface,
429 aspect,
430 lindex);
433 * Some sinks want to be unregistered automatically when
434 * the first notification goes out.
436 if ( (this->sinkAdviseFlag & ADVF_ONLYONCE) != 0)
438 IAdviseSink_Release(this->sinkInterface);
440 this->sinkInterface = NULL;
441 this->sinkAspects = 0;
442 this->sinkAdviseFlag = 0;
448 /* Helper for DataCacheEntry_OpenPresStream */
449 static BOOL DataCache_IsPresentationStream(const STATSTG *elem)
451 /* The presentation streams have names of the form "\002OlePresXXX",
452 * where XXX goes from 000 to 999. */
453 static const WCHAR OlePres[] = { 2,'O','l','e','P','r','e','s' };
455 LPCWSTR name = elem->pwcsName;
457 return (elem->type == STGTY_STREAM)
458 && (strlenW(name) == 11)
459 && (strncmpW(name, OlePres, 8) == 0)
460 && (name[8] >= '0') && (name[8] <= '9')
461 && (name[9] >= '0') && (name[9] <= '9')
462 && (name[10] >= '0') && (name[10] <= '9');
465 static HRESULT read_clipformat(IStream *stream, CLIPFORMAT *clipformat)
467 DWORD length;
468 HRESULT hr;
469 ULONG read;
471 *clipformat = 0;
473 hr = IStream_Read(stream, &length, sizeof(length), &read);
474 if (hr != S_OK || read != sizeof(length))
475 return DV_E_CLIPFORMAT;
476 if (length == -1)
478 DWORD cf;
479 hr = IStream_Read(stream, &cf, sizeof(cf), &read);
480 if (hr != S_OK || read != sizeof(cf))
481 return DV_E_CLIPFORMAT;
482 *clipformat = cf;
484 else
486 char *format_name = HeapAlloc(GetProcessHeap(), 0, length);
487 if (!format_name)
488 return E_OUTOFMEMORY;
489 hr = IStream_Read(stream, format_name, length, &read);
490 if (hr != S_OK || read != length || format_name[length - 1] != '\0')
492 HeapFree(GetProcessHeap(), 0, format_name);
493 return DV_E_CLIPFORMAT;
495 *clipformat = RegisterClipboardFormatA(format_name);
496 HeapFree(GetProcessHeap(), 0, format_name);
498 return S_OK;
501 static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat)
503 DWORD length;
504 HRESULT hr;
506 if (clipformat < 0xc000)
507 length = -1;
508 else
509 length = GetClipboardFormatNameA(clipformat, NULL, 0);
510 hr = IStream_Write(stream, &length, sizeof(length), NULL);
511 if (FAILED(hr))
512 return hr;
513 if (clipformat < 0xc000)
515 DWORD cf = clipformat;
516 hr = IStream_Write(stream, &cf, sizeof(cf), NULL);
518 else
520 char *format_name = HeapAlloc(GetProcessHeap(), 0, length);
521 if (!format_name)
522 return E_OUTOFMEMORY;
523 GetClipboardFormatNameA(clipformat, format_name, length);
524 hr = IStream_Write(stream, format_name, length, NULL);
525 HeapFree(GetProcessHeap(), 0, format_name);
527 return hr;
530 /************************************************************************
531 * DataCacheEntry_OpenPresStream
533 * This method will find the stream for the given presentation. It makes
534 * no attempt at fallback.
536 * Param:
537 * this - Pointer to the DataCache object
538 * drawAspect - The aspect of the object that we wish to draw.
539 * pStm - A returned stream. It points to the beginning of the
540 * - presentation data, including the header.
542 * Errors:
543 * S_OK The requested stream has been opened.
544 * OLE_E_BLANK The requested stream could not be found.
545 * Quite a few others I'm too lazy to map correctly.
547 * Notes:
548 * Algorithm: Scan the elements of the presentation storage, looking
549 * for presentation streams. For each presentation stream,
550 * load the header and check to see if the aspect matches.
552 * If a fallback is desired, just opening the first presentation stream
553 * is a possibility.
555 static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStream **ppStm)
557 HRESULT hr;
558 LARGE_INTEGER offset;
560 if (cache_entry->stream)
562 /* Rewind the stream before returning it. */
563 offset.QuadPart = 0;
565 hr = IStream_Seek( cache_entry->stream, offset, STREAM_SEEK_SET, NULL );
566 if (SUCCEEDED( hr ))
568 *ppStm = cache_entry->stream;
569 IStream_AddRef( cache_entry->stream );
572 else
573 hr = OLE_E_BLANK;
575 return hr;
579 static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm )
581 HRESULT hr;
582 STATSTG stat;
583 ULARGE_INTEGER current_pos;
584 void *bits;
585 METAFILEPICT *mfpict;
586 HGLOBAL hmfpict;
587 PresentationDataHeader header;
588 CLIPFORMAT clipformat;
589 static const LARGE_INTEGER offset_zero;
590 ULONG read;
592 if (cache_entry->stream_type != pres_stream)
594 FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type );
595 return E_FAIL;
598 hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
599 if (FAILED( hr )) return hr;
601 hr = read_clipformat( stm, &clipformat );
602 if (FAILED( hr )) return hr;
604 hr = IStream_Read( stm, &header, sizeof(header), &read );
605 if (hr != S_OK || read != sizeof(header)) return E_FAIL;
607 hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, &current_pos );
608 if (FAILED( hr )) return hr;
610 stat.cbSize.QuadPart -= current_pos.QuadPart;
612 hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) );
613 if (!hmfpict) return E_OUTOFMEMORY;
614 mfpict = GlobalLock( hmfpict );
616 bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart);
617 if (!bits)
619 GlobalFree( hmfpict );
620 return E_OUTOFMEMORY;
623 hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read );
624 if (hr != S_OK || read != stat.cbSize.u.LowPart) hr = E_FAIL;
626 if (SUCCEEDED( hr ))
628 /* FIXME: get this from the stream */
629 mfpict->mm = MM_ANISOTROPIC;
630 mfpict->xExt = header.dwObjectExtentX;
631 mfpict->yExt = header.dwObjectExtentY;
632 mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits );
633 if (!mfpict->hMF)
634 hr = E_FAIL;
637 GlobalUnlock( hmfpict );
638 if (SUCCEEDED( hr ))
640 cache_entry->data_cf = cache_entry->fmtetc.cfFormat;
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->data_cf = cache_entry->fmtetc.cfFormat;
726 cache_entry->stgmedium.tymed = TYMED_HGLOBAL;
727 cache_entry->stgmedium.u.hGlobal = hglobal;
729 return S_OK;
731 fail:
732 GlobalUnlock( hglobal );
733 GlobalFree( hglobal );
734 return E_FAIL;
738 /************************************************************************
739 * DataCacheEntry_LoadData
741 * This method will read information for the requested presentation
742 * into the given structure.
744 * Param:
745 * This - The entry to load the data from.
747 * Returns:
748 * This method returns a metafile handle if it is successful.
749 * it will return 0 if not.
751 static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry)
753 HRESULT hr;
754 IStream *stm;
756 hr = DataCacheEntry_OpenPresStream( cache_entry, &stm );
757 if (FAILED(hr)) return hr;
759 switch (cache_entry->fmtetc.cfFormat)
761 case CF_METAFILEPICT:
762 hr = load_mf_pict( cache_entry, stm );
763 break;
765 case CF_DIB:
766 hr = load_dib( cache_entry, stm );
767 break;
769 default:
770 FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat );
771 hr = E_NOTIMPL;
774 IStream_Release( stm );
775 return hr;
778 static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *cache_entry,
779 IStorage *storage, IStream **stream)
781 WCHAR wszName[] = {2,'O','l','e','P','r','e','s',
782 '0' + (cache_entry->stream_number / 100) % 10,
783 '0' + (cache_entry->stream_number / 10) % 10,
784 '0' + cache_entry->stream_number % 10, 0};
786 /* FIXME: cache the created stream in This? */
787 return IStorage_CreateStream(storage, wszName,
788 STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
789 0, 0, stream);
792 static HRESULT DataCacheEntry_Save(DataCacheEntry *cache_entry, IStorage *storage,
793 BOOL same_as_load)
795 PresentationDataHeader header;
796 HRESULT hr;
797 IStream *pres_stream;
798 void *data = NULL;
800 TRACE("stream_number = %d, fmtetc = %s\n", cache_entry->stream_number, debugstr_formatetc(&cache_entry->fmtetc));
802 hr = DataCacheEntry_CreateStream(cache_entry, storage, &pres_stream);
803 if (FAILED(hr))
804 return hr;
806 hr = write_clipformat(pres_stream, cache_entry->data_cf);
807 if (FAILED(hr))
808 return hr;
810 if (cache_entry->fmtetc.ptd)
811 FIXME("ptd not serialized\n");
812 header.unknown3 = 4;
813 header.dvAspect = cache_entry->fmtetc.dwAspect;
814 header.lindex = cache_entry->fmtetc.lindex;
815 header.advf = cache_entry->advise_flags;
816 header.unknown7 = 0;
817 header.dwObjectExtentX = 0;
818 header.dwObjectExtentY = 0;
819 header.dwSize = 0;
821 /* size the data */
822 switch (cache_entry->data_cf)
824 case CF_METAFILEPICT:
826 if (cache_entry->stgmedium.tymed != TYMED_NULL)
828 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
829 if (!mfpict)
831 IStream_Release(pres_stream);
832 return DV_E_STGMEDIUM;
834 header.dwObjectExtentX = mfpict->xExt;
835 header.dwObjectExtentY = mfpict->yExt;
836 header.dwSize = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
837 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
839 break;
841 default:
842 break;
846 * Write the header.
848 hr = IStream_Write(pres_stream, &header, sizeof(PresentationDataHeader),
849 NULL);
850 if (FAILED(hr))
852 IStream_Release(pres_stream);
853 return hr;
856 /* get the data */
857 switch (cache_entry->data_cf)
859 case CF_METAFILEPICT:
861 if (cache_entry->stgmedium.tymed != TYMED_NULL)
863 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
864 if (!mfpict)
866 IStream_Release(pres_stream);
867 return DV_E_STGMEDIUM;
869 data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
870 GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data);
871 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
873 break;
875 default:
876 break;
879 if (data)
880 hr = IStream_Write(pres_stream, data, header.dwSize, NULL);
881 HeapFree(GetProcessHeap(), 0, data);
883 IStream_Release(pres_stream);
884 return hr;
887 /* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL.
888 * does no checking of whether src_stgm has a supported tymed, so this should be
889 * done in the caller */
890 static HRESULT copy_stg_medium(CLIPFORMAT cf, STGMEDIUM *dest_stgm,
891 const STGMEDIUM *src_stgm)
893 if (src_stgm->tymed == TYMED_MFPICT)
895 const METAFILEPICT *src_mfpict = GlobalLock(src_stgm->u.hMetaFilePict);
896 METAFILEPICT *dest_mfpict;
898 if (!src_mfpict)
899 return DV_E_STGMEDIUM;
900 dest_stgm->u.hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT));
901 dest_mfpict = GlobalLock(dest_stgm->u.hMetaFilePict);
902 if (!dest_mfpict)
904 GlobalUnlock(src_stgm->u.hMetaFilePict);
905 return E_OUTOFMEMORY;
907 *dest_mfpict = *src_mfpict;
908 dest_mfpict->hMF = CopyMetaFileW(src_mfpict->hMF, NULL);
909 GlobalUnlock(src_stgm->u.hMetaFilePict);
910 GlobalUnlock(dest_stgm->u.hMetaFilePict);
912 else if (src_stgm->tymed != TYMED_NULL)
914 dest_stgm->u.hGlobal = OleDuplicateData(src_stgm->u.hGlobal, cf,
915 GMEM_MOVEABLE);
916 if (!dest_stgm->u.hGlobal)
917 return E_OUTOFMEMORY;
919 dest_stgm->tymed = src_stgm->tymed;
920 dest_stgm->pUnkForRelease = src_stgm->pUnkForRelease;
921 if (dest_stgm->pUnkForRelease)
922 IUnknown_AddRef(dest_stgm->pUnkForRelease);
923 return S_OK;
926 static HGLOBAL synthesize_dib( HBITMAP bm )
928 HDC hdc = GetDC( 0 );
929 BITMAPINFOHEADER header;
930 BITMAPINFO *bmi;
931 HGLOBAL ret = 0;
932 DWORD header_size;
934 memset( &header, 0, sizeof(header) );
935 header.biSize = sizeof(header);
936 if (!GetDIBits( hdc, bm, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done;
938 header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS );
939 if (!(ret = GlobalAlloc( GMEM_MOVEABLE, header_size + header.biSizeImage ))) goto done;
940 bmi = GlobalLock( ret );
941 memset( bmi, 0, header_size );
942 memcpy( bmi, &header, header.biSize );
943 GetDIBits( hdc, bm, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS );
944 GlobalUnlock( ret );
946 done:
947 ReleaseDC( 0, hdc );
948 return ret;
951 static HBITMAP synthesize_bitmap( HGLOBAL dib )
953 HBITMAP ret = 0;
954 BITMAPINFO *bmi;
955 HDC hdc = GetDC( 0 );
957 if ((bmi = GlobalLock( dib )))
959 /* FIXME: validate data size */
960 ret = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT,
961 (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ),
962 bmi, DIB_RGB_COLORS );
963 GlobalUnlock( dib );
965 ReleaseDC( 0, hdc );
966 return ret;
969 static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry,
970 const FORMATETC *formatetc,
971 STGMEDIUM *stgmedium,
972 BOOL fRelease)
974 STGMEDIUM dib_copy;
976 if ((!cache_entry->fmtetc.cfFormat && !formatetc->cfFormat) ||
977 (cache_entry->fmtetc.tymed == TYMED_NULL && formatetc->tymed == TYMED_NULL) ||
978 stgmedium->tymed == TYMED_NULL)
980 WARN("invalid formatetc\n");
981 return DV_E_FORMATETC;
984 cache_entry->dirty = TRUE;
985 ReleaseStgMedium(&cache_entry->stgmedium);
986 cache_entry->data_cf = cache_entry->fmtetc.cfFormat ? cache_entry->fmtetc.cfFormat : formatetc->cfFormat;
988 if (formatetc->cfFormat == CF_BITMAP)
990 dib_copy.tymed = TYMED_HGLOBAL;
991 dib_copy.u.hGlobal = synthesize_dib( stgmedium->u.hBitmap );
992 dib_copy.pUnkForRelease = NULL;
993 if (fRelease) ReleaseStgMedium(stgmedium);
994 stgmedium = &dib_copy;
995 fRelease = TRUE;
998 if (fRelease)
1000 cache_entry->stgmedium = *stgmedium;
1001 return S_OK;
1003 else
1004 return copy_stg_medium(cache_entry->data_cf,
1005 &cache_entry->stgmedium, stgmedium);
1008 static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, FORMATETC *fmt, STGMEDIUM *stgmedium)
1010 if (cache_entry->stgmedium.tymed == TYMED_NULL && cache_entry->stream)
1012 HRESULT hr = DataCacheEntry_LoadData(cache_entry);
1013 if (FAILED(hr))
1014 return hr;
1016 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1017 return OLE_E_BLANK;
1019 if (fmt->cfFormat == CF_BITMAP)
1021 stgmedium->tymed = TYMED_GDI;
1022 stgmedium->u.hBitmap = synthesize_bitmap( cache_entry->stgmedium.u.hGlobal );
1023 stgmedium->pUnkForRelease = NULL;
1024 return S_OK;
1026 return copy_stg_medium(cache_entry->data_cf, stgmedium, &cache_entry->stgmedium);
1029 static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry)
1031 ReleaseStgMedium(&cache_entry->stgmedium);
1032 cache_entry->data_cf = cache_entry->fmtetc.cfFormat;
1033 return S_OK;
1036 static inline void DataCacheEntry_HandsOffStorage(DataCacheEntry *cache_entry)
1038 if (cache_entry->stream)
1040 IStream_Release(cache_entry->stream);
1041 cache_entry->stream = NULL;
1045 static inline DWORD tymed_from_cf( DWORD cf )
1047 switch( cf )
1049 case CF_BITMAP: return TYMED_GDI;
1050 case CF_METAFILEPICT: return TYMED_MFPICT;
1051 case CF_ENHMETAFILE: return TYMED_ENHMF;
1052 case CF_DIB:
1053 default: return TYMED_HGLOBAL;
1057 /****************************************************************
1058 * create_automatic_entry
1060 * Creates an appropriate cache entry for one of the CLSID_Picture_
1061 * classes. The connection id of the entry is one. Any pre-existing
1062 * automatic entry is re-assigned a new connection id, and moved to
1063 * the end of the list.
1065 static HRESULT create_automatic_entry(DataCache *cache, const CLSID *clsid)
1067 static const struct data
1069 const CLSID *clsid;
1070 FORMATETC fmt;
1071 } data[] =
1073 { &CLSID_Picture_Dib, { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
1074 { &CLSID_Picture_Metafile, { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
1075 { &CLSID_Picture_EnhMetafile, { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
1076 { NULL }
1078 const struct data *ptr = data;
1079 struct list *head;
1080 DataCacheEntry *entry;
1082 if (IsEqualCLSID( &cache->clsid, clsid )) return S_OK;
1084 /* move and reassign any pre-existing automatic entry */
1085 if ((head = list_head( &cache->cache_list )))
1087 entry = LIST_ENTRY( head, DataCacheEntry, entry );
1088 if (entry->id == 1)
1090 list_remove( &entry->entry );
1091 entry->id = cache->last_cache_id++;
1092 list_add_tail( &cache->cache_list, &entry->entry );
1096 while (ptr->clsid)
1098 if (IsEqualCLSID( clsid, ptr->clsid ))
1099 return DataCache_CreateEntry( cache, &ptr->fmt, 0, TRUE, NULL );
1100 ptr++;
1102 return S_OK;
1105 /*********************************************************
1106 * Method implementation for the non delegating IUnknown
1107 * part of the DataCache class.
1110 /************************************************************************
1111 * DataCache_NDIUnknown_QueryInterface (IUnknown)
1113 * This version of QueryInterface will not delegate its implementation
1114 * to the outer unknown.
1116 static HRESULT WINAPI DataCache_NDIUnknown_QueryInterface(
1117 IUnknown* iface,
1118 REFIID riid,
1119 void** ppvObject)
1121 DataCache *this = impl_from_IUnknown(iface);
1123 if ( ppvObject==0 )
1124 return E_INVALIDARG;
1126 *ppvObject = 0;
1128 if (IsEqualIID(&IID_IUnknown, riid))
1130 if (this->outer_unk == iface) /* non-aggregated, return IUnknown from IOleCache2 */
1131 *ppvObject = &this->IOleCache2_iface;
1132 else
1133 *ppvObject = iface;
1135 else if (IsEqualIID(&IID_IDataObject, riid))
1137 *ppvObject = &this->IDataObject_iface;
1139 else if ( IsEqualIID(&IID_IPersistStorage, riid) ||
1140 IsEqualIID(&IID_IPersist, riid) )
1142 *ppvObject = &this->IPersistStorage_iface;
1144 else if ( IsEqualIID(&IID_IViewObject, riid) ||
1145 IsEqualIID(&IID_IViewObject2, riid) )
1147 *ppvObject = &this->IViewObject2_iface;
1149 else if ( IsEqualIID(&IID_IOleCache, riid) ||
1150 IsEqualIID(&IID_IOleCache2, riid) )
1152 *ppvObject = &this->IOleCache2_iface;
1154 else if ( IsEqualIID(&IID_IOleCacheControl, riid) )
1156 *ppvObject = &this->IOleCacheControl_iface;
1159 if ((*ppvObject)==0)
1161 WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid));
1162 return E_NOINTERFACE;
1165 IUnknown_AddRef((IUnknown*)*ppvObject);
1167 return S_OK;
1170 /************************************************************************
1171 * DataCache_NDIUnknown_AddRef (IUnknown)
1173 * This version of QueryInterface will not delegate its implementation
1174 * to the outer unknown.
1176 static ULONG WINAPI DataCache_NDIUnknown_AddRef(
1177 IUnknown* iface)
1179 DataCache *this = impl_from_IUnknown(iface);
1180 return InterlockedIncrement(&this->ref);
1183 /************************************************************************
1184 * DataCache_NDIUnknown_Release (IUnknown)
1186 * This version of QueryInterface will not delegate its implementation
1187 * to the outer unknown.
1189 static ULONG WINAPI DataCache_NDIUnknown_Release(
1190 IUnknown* iface)
1192 DataCache *this = impl_from_IUnknown(iface);
1193 ULONG ref;
1195 ref = InterlockedDecrement(&this->ref);
1197 if (ref == 0) DataCache_Destroy(this);
1199 return ref;
1202 /*********************************************************
1203 * Method implementation for the IDataObject
1204 * part of the DataCache class.
1207 /************************************************************************
1208 * DataCache_IDataObject_QueryInterface (IUnknown)
1210 static HRESULT WINAPI DataCache_IDataObject_QueryInterface(
1211 IDataObject* iface,
1212 REFIID riid,
1213 void** ppvObject)
1215 DataCache *this = impl_from_IDataObject(iface);
1217 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1220 /************************************************************************
1221 * DataCache_IDataObject_AddRef (IUnknown)
1223 static ULONG WINAPI DataCache_IDataObject_AddRef(
1224 IDataObject* iface)
1226 DataCache *this = impl_from_IDataObject(iface);
1228 return IUnknown_AddRef(this->outer_unk);
1231 /************************************************************************
1232 * DataCache_IDataObject_Release (IUnknown)
1234 static ULONG WINAPI DataCache_IDataObject_Release(
1235 IDataObject* iface)
1237 DataCache *this = impl_from_IDataObject(iface);
1239 return IUnknown_Release(this->outer_unk);
1242 /************************************************************************
1243 * DataCache_GetData
1245 * Get Data from a source dataobject using format pformatetcIn->cfFormat
1247 static HRESULT WINAPI DataCache_GetData(
1248 IDataObject* iface,
1249 LPFORMATETC pformatetcIn,
1250 STGMEDIUM* pmedium)
1252 DataCache *This = impl_from_IDataObject(iface);
1253 DataCacheEntry *cache_entry;
1255 TRACE("(%p, %s, %p)\n", iface, debugstr_formatetc(pformatetcIn), pmedium);
1257 memset(pmedium, 0, sizeof(*pmedium));
1259 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetcIn);
1260 if (!cache_entry)
1261 return OLE_E_BLANK;
1263 return DataCacheEntry_GetData(cache_entry, pformatetcIn, pmedium);
1266 static HRESULT WINAPI DataCache_GetDataHere(
1267 IDataObject* iface,
1268 LPFORMATETC pformatetc,
1269 STGMEDIUM* pmedium)
1271 FIXME("stub\n");
1272 return E_NOTIMPL;
1275 static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt )
1277 DataCache *This = impl_from_IDataObject( iface );
1278 DataCacheEntry *cache_entry;
1280 TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) );
1281 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1283 return cache_entry ? S_OK : S_FALSE;
1286 /************************************************************************
1287 * DataCache_EnumFormatEtc (IDataObject)
1289 * The data cache doesn't implement this method.
1291 static HRESULT WINAPI DataCache_GetCanonicalFormatEtc(
1292 IDataObject* iface,
1293 LPFORMATETC pformatectIn,
1294 LPFORMATETC pformatetcOut)
1296 TRACE("()\n");
1297 return E_NOTIMPL;
1300 /************************************************************************
1301 * DataCache_IDataObject_SetData (IDataObject)
1303 * This method is delegated to the IOleCache2 implementation.
1305 static HRESULT WINAPI DataCache_IDataObject_SetData(
1306 IDataObject* iface,
1307 LPFORMATETC pformatetc,
1308 STGMEDIUM* pmedium,
1309 BOOL fRelease)
1311 IOleCache2* oleCache = NULL;
1312 HRESULT hres;
1314 TRACE("(%p, %p, %p, %d)\n", iface, pformatetc, pmedium, fRelease);
1316 hres = IDataObject_QueryInterface(iface, &IID_IOleCache2, (void**)&oleCache);
1318 if (FAILED(hres))
1319 return E_UNEXPECTED;
1321 hres = IOleCache2_SetData(oleCache, pformatetc, pmedium, fRelease);
1323 IOleCache2_Release(oleCache);
1325 return hres;
1328 /************************************************************************
1329 * DataCache_EnumFormatEtc (IDataObject)
1331 * The data cache doesn't implement this method.
1333 static HRESULT WINAPI DataCache_EnumFormatEtc(
1334 IDataObject* iface,
1335 DWORD dwDirection,
1336 IEnumFORMATETC** ppenumFormatEtc)
1338 TRACE("()\n");
1339 return E_NOTIMPL;
1342 /************************************************************************
1343 * DataCache_DAdvise (IDataObject)
1345 * The data cache doesn't support connections.
1347 static HRESULT WINAPI DataCache_DAdvise(
1348 IDataObject* iface,
1349 FORMATETC* pformatetc,
1350 DWORD advf,
1351 IAdviseSink* pAdvSink,
1352 DWORD* pdwConnection)
1354 TRACE("()\n");
1355 return OLE_E_ADVISENOTSUPPORTED;
1358 /************************************************************************
1359 * DataCache_DUnadvise (IDataObject)
1361 * The data cache doesn't support connections.
1363 static HRESULT WINAPI DataCache_DUnadvise(
1364 IDataObject* iface,
1365 DWORD dwConnection)
1367 TRACE("()\n");
1368 return OLE_E_NOCONNECTION;
1371 /************************************************************************
1372 * DataCache_EnumDAdvise (IDataObject)
1374 * The data cache doesn't support connections.
1376 static HRESULT WINAPI DataCache_EnumDAdvise(
1377 IDataObject* iface,
1378 IEnumSTATDATA** ppenumAdvise)
1380 TRACE("()\n");
1381 return OLE_E_ADVISENOTSUPPORTED;
1384 /*********************************************************
1385 * Method implementation for the IDataObject
1386 * part of the DataCache class.
1389 /************************************************************************
1390 * DataCache_IPersistStorage_QueryInterface (IUnknown)
1392 static HRESULT WINAPI DataCache_IPersistStorage_QueryInterface(
1393 IPersistStorage* iface,
1394 REFIID riid,
1395 void** ppvObject)
1397 DataCache *this = impl_from_IPersistStorage(iface);
1399 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1402 /************************************************************************
1403 * DataCache_IPersistStorage_AddRef (IUnknown)
1405 static ULONG WINAPI DataCache_IPersistStorage_AddRef(
1406 IPersistStorage* iface)
1408 DataCache *this = impl_from_IPersistStorage(iface);
1410 return IUnknown_AddRef(this->outer_unk);
1413 /************************************************************************
1414 * DataCache_IPersistStorage_Release (IUnknown)
1416 static ULONG WINAPI DataCache_IPersistStorage_Release(
1417 IPersistStorage* iface)
1419 DataCache *this = impl_from_IPersistStorage(iface);
1421 return IUnknown_Release(this->outer_unk);
1424 /************************************************************************
1425 * DataCache_GetClassID (IPersistStorage)
1428 static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid)
1430 DataCache *This = impl_from_IPersistStorage( iface );
1432 TRACE( "(%p, %p) returning %s\n", iface, clsid, debugstr_guid(&This->clsid) );
1433 *clsid = This->clsid;
1435 return S_OK;
1438 /************************************************************************
1439 * DataCache_IsDirty (IPersistStorage)
1441 static HRESULT WINAPI DataCache_IsDirty(
1442 IPersistStorage* iface)
1444 DataCache *This = impl_from_IPersistStorage(iface);
1445 DataCacheEntry *cache_entry;
1447 TRACE("(%p)\n", iface);
1449 if (This->dirty)
1450 return S_OK;
1452 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1453 if (cache_entry->dirty)
1454 return S_OK;
1456 return S_FALSE;
1459 /************************************************************************
1460 * DataCache_InitNew (IPersistStorage)
1462 * The data cache implementation of IPersistStorage_InitNew simply stores
1463 * the storage pointer.
1465 static HRESULT WINAPI DataCache_InitNew(
1466 IPersistStorage* iface,
1467 IStorage* pStg)
1469 DataCache *This = impl_from_IPersistStorage(iface);
1470 CLSID clsid;
1471 HRESULT hr;
1473 TRACE("(%p, %p)\n", iface, pStg);
1475 if (This->presentationStorage != NULL)
1476 return CO_E_ALREADYINITIALIZED;
1478 This->presentationStorage = pStg;
1480 IStorage_AddRef(This->presentationStorage);
1481 This->dirty = TRUE;
1482 ReadClassStg( pStg, &clsid );
1483 hr = create_automatic_entry( This, &clsid );
1484 if (FAILED(hr))
1486 IStorage_Release( pStg );
1487 This->presentationStorage = NULL;
1488 return hr;
1490 This->clsid = clsid;
1492 return S_OK;
1496 static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, DWORD advf, IStream *stm,
1497 enum stream_type type )
1499 DataCacheEntry *cache_entry;
1500 HRESULT hr = S_OK;
1502 TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) );
1504 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1505 if (!cache_entry)
1506 hr = DataCache_CreateEntry( This, fmt, advf, FALSE, &cache_entry );
1507 if (SUCCEEDED( hr ))
1509 DataCacheEntry_DiscardData( cache_entry );
1510 if (cache_entry->stream) IStream_Release( cache_entry->stream );
1511 cache_entry->stream = stm;
1512 IStream_AddRef( stm );
1513 cache_entry->stream_type = type;
1514 cache_entry->dirty = FALSE;
1516 return hr;
1519 static HRESULT parse_pres_streams( DataCache *This, IStorage *stg )
1521 HRESULT hr;
1522 IEnumSTATSTG *stat_enum;
1523 STATSTG stat;
1524 IStream *stm;
1525 PresentationDataHeader header;
1526 ULONG actual_read;
1527 CLIPFORMAT clipformat;
1528 FORMATETC fmtetc;
1530 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stat_enum );
1531 if (FAILED( hr )) return hr;
1533 while ((hr = IEnumSTATSTG_Next( stat_enum, 1, &stat, NULL )) == S_OK)
1535 if (DataCache_IsPresentationStream( &stat ))
1537 hr = IStorage_OpenStream( stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1538 0, &stm );
1539 if (SUCCEEDED( hr ))
1541 hr = read_clipformat( stm, &clipformat );
1543 if (hr == S_OK)
1544 hr = IStream_Read( stm, &header, sizeof(header), &actual_read );
1546 if (hr == S_OK && actual_read == sizeof(header))
1548 fmtetc.cfFormat = clipformat;
1549 fmtetc.ptd = NULL; /* FIXME */
1550 fmtetc.dwAspect = header.dvAspect;
1551 fmtetc.lindex = header.lindex;
1552 fmtetc.tymed = tymed_from_cf( clipformat );
1554 add_cache_entry( This, &fmtetc, header.advf, stm, pres_stream );
1556 IStream_Release( stm );
1559 CoTaskMemFree( stat.pwcsName );
1561 IEnumSTATSTG_Release( stat_enum );
1563 return S_OK;
1566 static const FORMATETC static_dib_fmt = { CF_DIB, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1568 static HRESULT parse_contents_stream( DataCache *This, IStorage *stg, IStream *stm )
1570 HRESULT hr;
1571 STATSTG stat;
1572 const FORMATETC *fmt;
1574 hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
1575 if (FAILED( hr )) return hr;
1577 if (IsEqualCLSID( &stat.clsid, &CLSID_Picture_Dib ))
1578 fmt = &static_dib_fmt;
1579 else
1581 FIXME("unsupported format %s\n", debugstr_guid( &stat.clsid ));
1582 return E_FAIL;
1585 return add_cache_entry( This, fmt, 0, stm, contents_stream );
1588 static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0};
1590 /************************************************************************
1591 * DataCache_Load (IPersistStorage)
1593 * The data cache implementation of IPersistStorage_Load doesn't
1594 * actually load anything. Instead, it holds on to the storage pointer
1595 * and it will load the presentation information when the
1596 * IDataObject_GetData or IViewObject2_Draw methods are called.
1598 static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *pStg )
1600 DataCache *This = impl_from_IPersistStorage(iface);
1601 HRESULT hr;
1602 IStream *stm;
1603 CLSID clsid;
1604 DataCacheEntry *entry, *cursor2;
1606 TRACE("(%p, %p)\n", iface, pStg);
1608 IPersistStorage_HandsOffStorage( iface );
1610 LIST_FOR_EACH_ENTRY_SAFE( entry, cursor2, &This->cache_list, DataCacheEntry, entry )
1611 DataCacheEntry_Destroy( This, entry );
1613 ReadClassStg( pStg, &clsid );
1614 hr = create_automatic_entry( This, &clsid );
1615 if (FAILED( hr )) return hr;
1617 This->clsid = clsid;
1619 hr = IStorage_OpenStream( pStg, CONTENTS, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1620 0, &stm );
1621 if (SUCCEEDED( hr ))
1623 hr = parse_contents_stream( This, pStg, stm );
1624 IStream_Release( stm );
1627 if (FAILED(hr))
1628 hr = parse_pres_streams( This, pStg );
1630 if (SUCCEEDED( hr ))
1632 This->dirty = FALSE;
1633 This->presentationStorage = pStg;
1634 IStorage_AddRef( This->presentationStorage );
1637 return hr;
1640 /************************************************************************
1641 * DataCache_Save (IPersistStorage)
1643 * Until we actually connect to a running object and retrieve new
1644 * information to it, we never have to save anything. However, it is
1645 * our responsibility to copy the information when saving to a new
1646 * storage.
1648 static HRESULT WINAPI DataCache_Save(
1649 IPersistStorage* iface,
1650 IStorage* pStg,
1651 BOOL fSameAsLoad)
1653 DataCache *This = impl_from_IPersistStorage(iface);
1654 DataCacheEntry *cache_entry;
1655 BOOL dirty = FALSE;
1656 HRESULT hr = S_OK;
1657 unsigned short stream_number = 0;
1659 TRACE("(%p, %p, %d)\n", iface, pStg, fSameAsLoad);
1661 dirty = This->dirty;
1662 if (!dirty)
1664 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1666 dirty = cache_entry->dirty;
1667 if (dirty)
1668 break;
1672 /* assign stream numbers to the cache entries */
1673 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1675 if (cache_entry->stream_number != stream_number)
1677 cache_entry->dirty = TRUE; /* needs to be written out again */
1678 cache_entry->stream_number = stream_number;
1680 stream_number++;
1683 /* write out the cache entries */
1684 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1686 if (!fSameAsLoad || cache_entry->dirty)
1688 hr = DataCacheEntry_Save(cache_entry, pStg, fSameAsLoad);
1689 if (FAILED(hr))
1690 break;
1692 cache_entry->dirty = FALSE;
1696 This->dirty = FALSE;
1697 return hr;
1700 /************************************************************************
1701 * DataCache_SaveCompleted (IPersistStorage)
1703 * This method is called to tell the cache to release the storage
1704 * pointer it's currently holding.
1706 static HRESULT WINAPI DataCache_SaveCompleted(
1707 IPersistStorage* iface,
1708 IStorage* pStgNew)
1710 TRACE("(%p, %p)\n", iface, pStgNew);
1712 if (pStgNew)
1714 IPersistStorage_HandsOffStorage(iface);
1716 DataCache_Load(iface, pStgNew);
1719 return S_OK;
1722 /************************************************************************
1723 * DataCache_HandsOffStorage (IPersistStorage)
1725 * This method is called to tell the cache to release the storage
1726 * pointer it's currently holding.
1728 static HRESULT WINAPI DataCache_HandsOffStorage(
1729 IPersistStorage* iface)
1731 DataCache *this = impl_from_IPersistStorage(iface);
1732 DataCacheEntry *cache_entry;
1734 TRACE("(%p)\n", iface);
1736 if (this->presentationStorage != NULL)
1738 IStorage_Release(this->presentationStorage);
1739 this->presentationStorage = NULL;
1742 LIST_FOR_EACH_ENTRY(cache_entry, &this->cache_list, DataCacheEntry, entry)
1743 DataCacheEntry_HandsOffStorage(cache_entry);
1745 return S_OK;
1748 /*********************************************************
1749 * Method implementation for the IViewObject2
1750 * part of the DataCache class.
1753 /************************************************************************
1754 * DataCache_IViewObject2_QueryInterface (IUnknown)
1756 static HRESULT WINAPI DataCache_IViewObject2_QueryInterface(
1757 IViewObject2* iface,
1758 REFIID riid,
1759 void** ppvObject)
1761 DataCache *this = impl_from_IViewObject2(iface);
1763 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1766 /************************************************************************
1767 * DataCache_IViewObject2_AddRef (IUnknown)
1769 static ULONG WINAPI DataCache_IViewObject2_AddRef(
1770 IViewObject2* iface)
1772 DataCache *this = impl_from_IViewObject2(iface);
1774 return IUnknown_AddRef(this->outer_unk);
1777 /************************************************************************
1778 * DataCache_IViewObject2_Release (IUnknown)
1780 static ULONG WINAPI DataCache_IViewObject2_Release(
1781 IViewObject2* iface)
1783 DataCache *this = impl_from_IViewObject2(iface);
1785 return IUnknown_Release(this->outer_unk);
1788 /************************************************************************
1789 * DataCache_Draw (IViewObject2)
1791 * This method will draw the cached representation of the object
1792 * to the given device context.
1794 static HRESULT WINAPI DataCache_Draw(
1795 IViewObject2* iface,
1796 DWORD dwDrawAspect,
1797 LONG lindex,
1798 void* pvAspect,
1799 DVTARGETDEVICE* ptd,
1800 HDC hdcTargetDev,
1801 HDC hdcDraw,
1802 LPCRECTL lprcBounds,
1803 LPCRECTL lprcWBounds,
1804 BOOL (CALLBACK *pfnContinue)(ULONG_PTR dwContinue),
1805 ULONG_PTR dwContinue)
1807 DataCache *This = impl_from_IViewObject2(iface);
1808 HRESULT hres;
1809 DataCacheEntry *cache_entry;
1811 TRACE("(%p, %x, %d, %p, %p, %p, %p, %p, %p, %lx)\n",
1812 iface,
1813 dwDrawAspect,
1814 lindex,
1815 pvAspect,
1816 hdcTargetDev,
1817 hdcDraw,
1818 lprcBounds,
1819 lprcWBounds,
1820 pfnContinue,
1821 dwContinue);
1823 if (lprcBounds==NULL)
1824 return E_INVALIDARG;
1826 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1828 /* FIXME: compare ptd too */
1829 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
1830 (cache_entry->fmtetc.lindex != lindex))
1831 continue;
1833 /* if the data hasn't been loaded yet, do it now */
1834 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
1836 hres = DataCacheEntry_LoadData(cache_entry);
1837 if (FAILED(hres))
1838 continue;
1841 /* no data */
1842 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1843 continue;
1845 if (pfnContinue && !pfnContinue(dwContinue)) return E_ABORT;
1847 switch (cache_entry->data_cf)
1849 case CF_METAFILEPICT:
1852 * We have to be careful not to modify the state of the
1853 * DC.
1855 INT prevMapMode;
1856 SIZE oldWindowExt;
1857 SIZE oldViewportExt;
1858 POINT oldViewportOrg;
1859 METAFILEPICT *mfpict;
1861 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
1862 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
1863 continue;
1865 prevMapMode = SetMapMode(hdcDraw, mfpict->mm);
1867 SetWindowExtEx(hdcDraw,
1868 mfpict->xExt,
1869 mfpict->yExt,
1870 &oldWindowExt);
1872 SetViewportExtEx(hdcDraw,
1873 lprcBounds->right - lprcBounds->left,
1874 lprcBounds->bottom - lprcBounds->top,
1875 &oldViewportExt);
1877 SetViewportOrgEx(hdcDraw,
1878 lprcBounds->left,
1879 lprcBounds->top,
1880 &oldViewportOrg);
1882 PlayMetaFile(hdcDraw, mfpict->hMF);
1884 SetWindowExtEx(hdcDraw,
1885 oldWindowExt.cx,
1886 oldWindowExt.cy,
1887 NULL);
1889 SetViewportExtEx(hdcDraw,
1890 oldViewportExt.cx,
1891 oldViewportExt.cy,
1892 NULL);
1894 SetViewportOrgEx(hdcDraw,
1895 oldViewportOrg.x,
1896 oldViewportOrg.y,
1897 NULL);
1899 SetMapMode(hdcDraw, prevMapMode);
1901 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
1903 return S_OK;
1905 case CF_DIB:
1907 BITMAPINFO *info;
1908 BYTE *bits;
1910 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
1911 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
1912 continue;
1914 bits = (BYTE *) info + bitmap_info_size( info, DIB_RGB_COLORS );
1915 StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top,
1916 lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top,
1917 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
1918 bits, info, DIB_RGB_COLORS, SRCCOPY );
1920 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
1921 return S_OK;
1926 WARN("no data could be found to be drawn\n");
1928 return OLE_E_BLANK;
1931 static HRESULT WINAPI DataCache_GetColorSet(
1932 IViewObject2* iface,
1933 DWORD dwDrawAspect,
1934 LONG lindex,
1935 void* pvAspect,
1936 DVTARGETDEVICE* ptd,
1937 HDC hicTargetDevice,
1938 LOGPALETTE** ppColorSet)
1940 FIXME("stub\n");
1941 return E_NOTIMPL;
1944 static HRESULT WINAPI DataCache_Freeze(
1945 IViewObject2* iface,
1946 DWORD dwDrawAspect,
1947 LONG lindex,
1948 void* pvAspect,
1949 DWORD* pdwFreeze)
1951 FIXME("stub\n");
1952 return E_NOTIMPL;
1955 static HRESULT WINAPI DataCache_Unfreeze(
1956 IViewObject2* iface,
1957 DWORD dwFreeze)
1959 FIXME("stub\n");
1960 return E_NOTIMPL;
1963 /************************************************************************
1964 * DataCache_SetAdvise (IViewObject2)
1966 * This sets-up an advisory sink with the data cache. When the object's
1967 * view changes, this sink is called.
1969 static HRESULT WINAPI DataCache_SetAdvise(
1970 IViewObject2* iface,
1971 DWORD aspects,
1972 DWORD advf,
1973 IAdviseSink* pAdvSink)
1975 DataCache *this = impl_from_IViewObject2(iface);
1977 TRACE("(%p, %x, %x, %p)\n", iface, aspects, advf, pAdvSink);
1980 * A call to this function removes the previous sink
1982 if (this->sinkInterface != NULL)
1984 IAdviseSink_Release(this->sinkInterface);
1985 this->sinkInterface = NULL;
1986 this->sinkAspects = 0;
1987 this->sinkAdviseFlag = 0;
1991 * Now, setup the new one.
1993 if (pAdvSink!=NULL)
1995 this->sinkInterface = pAdvSink;
1996 this->sinkAspects = aspects;
1997 this->sinkAdviseFlag = advf;
1999 IAdviseSink_AddRef(this->sinkInterface);
2003 * When the ADVF_PRIMEFIRST flag is set, we have to advise the
2004 * sink immediately.
2006 if (advf & ADVF_PRIMEFIRST)
2008 DataCache_FireOnViewChange(this, aspects, -1);
2011 return S_OK;
2014 /************************************************************************
2015 * DataCache_GetAdvise (IViewObject2)
2017 * This method queries the current state of the advise sink
2018 * installed on the data cache.
2020 static HRESULT WINAPI DataCache_GetAdvise(
2021 IViewObject2* iface,
2022 DWORD* pAspects,
2023 DWORD* pAdvf,
2024 IAdviseSink** ppAdvSink)
2026 DataCache *this = impl_from_IViewObject2(iface);
2028 TRACE("(%p, %p, %p, %p)\n", iface, pAspects, pAdvf, ppAdvSink);
2031 * Just copy all the requested values.
2033 if (pAspects!=NULL)
2034 *pAspects = this->sinkAspects;
2036 if (pAdvf!=NULL)
2037 *pAdvf = this->sinkAdviseFlag;
2039 if (ppAdvSink!=NULL)
2041 if (this->sinkInterface != NULL)
2042 IAdviseSink_QueryInterface(this->sinkInterface,
2043 &IID_IAdviseSink,
2044 (void**)ppAdvSink);
2045 else *ppAdvSink = NULL;
2048 return S_OK;
2051 /************************************************************************
2052 * DataCache_GetExtent (IViewObject2)
2054 * This method retrieves the "natural" size of this cached object.
2056 static HRESULT WINAPI DataCache_GetExtent(
2057 IViewObject2* iface,
2058 DWORD dwDrawAspect,
2059 LONG lindex,
2060 DVTARGETDEVICE* ptd,
2061 LPSIZEL lpsizel)
2063 DataCache *This = impl_from_IViewObject2(iface);
2064 HRESULT hres = E_FAIL;
2065 DataCacheEntry *cache_entry;
2067 TRACE("(%p, %x, %d, %p, %p)\n",
2068 iface, dwDrawAspect, lindex, ptd, lpsizel);
2070 if (lpsizel==NULL)
2071 return E_POINTER;
2073 lpsizel->cx = 0;
2074 lpsizel->cy = 0;
2076 if (lindex!=-1)
2077 FIXME("Unimplemented flag lindex = %d\n", lindex);
2080 * Right now, we support only the callback from
2081 * the default handler.
2083 if (ptd!=NULL)
2084 FIXME("Unimplemented ptd = %p\n", ptd);
2086 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2088 /* FIXME: compare ptd too */
2089 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
2090 (cache_entry->fmtetc.lindex != lindex))
2091 continue;
2093 /* if the data hasn't been loaded yet, do it now */
2094 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
2096 hres = DataCacheEntry_LoadData(cache_entry);
2097 if (FAILED(hres))
2098 continue;
2101 /* no data */
2102 if (cache_entry->stgmedium.tymed == TYMED_NULL)
2103 continue;
2106 switch (cache_entry->data_cf)
2108 case CF_METAFILEPICT:
2110 METAFILEPICT *mfpict;
2112 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
2113 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
2114 continue;
2116 lpsizel->cx = mfpict->xExt;
2117 lpsizel->cy = mfpict->yExt;
2119 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
2121 return S_OK;
2123 case CF_DIB:
2125 BITMAPINFOHEADER *info;
2126 LONG x_pels_m, y_pels_m;
2129 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
2130 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
2131 continue;
2133 x_pels_m = info->biXPelsPerMeter;
2134 y_pels_m = info->biYPelsPerMeter;
2136 /* Size in units of 0.01mm (ie. MM_HIMETRIC) */
2137 if (x_pels_m != 0 && y_pels_m != 0)
2139 lpsizel->cx = info->biWidth * 100000 / x_pels_m;
2140 lpsizel->cy = info->biHeight * 100000 / y_pels_m;
2142 else
2144 HDC hdc = GetDC( 0 );
2145 lpsizel->cx = info->biWidth * 2540 / GetDeviceCaps( hdc, LOGPIXELSX );
2146 lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY );
2148 ReleaseDC( 0, hdc );
2151 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
2153 return S_OK;
2158 WARN("no data could be found to get the extents from\n");
2161 * This method returns OLE_E_BLANK when it fails.
2163 return OLE_E_BLANK;
2167 /*********************************************************
2168 * Method implementation for the IOleCache2
2169 * part of the DataCache class.
2172 /************************************************************************
2173 * DataCache_IOleCache2_QueryInterface (IUnknown)
2175 static HRESULT WINAPI DataCache_IOleCache2_QueryInterface(
2176 IOleCache2* iface,
2177 REFIID riid,
2178 void** ppvObject)
2180 DataCache *this = impl_from_IOleCache2(iface);
2182 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2185 /************************************************************************
2186 * DataCache_IOleCache2_AddRef (IUnknown)
2188 static ULONG WINAPI DataCache_IOleCache2_AddRef(
2189 IOleCache2* iface)
2191 DataCache *this = impl_from_IOleCache2(iface);
2193 return IUnknown_AddRef(this->outer_unk);
2196 /************************************************************************
2197 * DataCache_IOleCache2_Release (IUnknown)
2199 static ULONG WINAPI DataCache_IOleCache2_Release(
2200 IOleCache2* iface)
2202 DataCache *this = impl_from_IOleCache2(iface);
2204 return IUnknown_Release(this->outer_unk);
2207 /*****************************************************************************
2208 * setup_sink
2210 * Set up the sink connection to the running object.
2212 static HRESULT setup_sink(DataCache *This, DataCacheEntry *cache_entry)
2214 HRESULT hr = S_FALSE;
2215 DWORD flags;
2217 /* Clear the ADVFCACHE_* bits. Native also sets the two highest bits for some reason. */
2218 flags = cache_entry->advise_flags & ~(ADVFCACHE_NOHANDLER | ADVFCACHE_FORCEBUILTIN | ADVFCACHE_ONSAVE);
2220 if(This->running_object)
2221 if(!(flags & ADVF_NODATA))
2222 hr = IDataObject_DAdvise(This->running_object, &cache_entry->fmtetc, flags,
2223 &This->IAdviseSink_iface, &cache_entry->sink_id);
2224 return hr;
2227 static HRESULT WINAPI DataCache_Cache(
2228 IOleCache2* iface,
2229 FORMATETC* pformatetc,
2230 DWORD advf,
2231 DWORD* pdwConnection)
2233 DataCache *This = impl_from_IOleCache2(iface);
2234 DataCacheEntry *cache_entry;
2235 HRESULT hr;
2236 FORMATETC fmt_cpy;
2238 TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection);
2240 if (!pformatetc || !pdwConnection)
2241 return E_INVALIDARG;
2243 TRACE("pformatetc = %s\n", debugstr_formatetc(pformatetc));
2245 fmt_cpy = *pformatetc; /* No need for a deep copy */
2246 if (fmt_cpy.cfFormat == CF_BITMAP && fmt_cpy.tymed == TYMED_GDI)
2248 fmt_cpy.cfFormat = CF_DIB;
2249 fmt_cpy.tymed = TYMED_HGLOBAL;
2252 /* View caching DVASPECT_ICON gets converted to CF_METAFILEPICT */
2253 if (fmt_cpy.dwAspect == DVASPECT_ICON && fmt_cpy.cfFormat == 0)
2255 fmt_cpy.cfFormat = CF_METAFILEPICT;
2256 fmt_cpy.tymed = TYMED_MFPICT;
2259 *pdwConnection = 0;
2261 cache_entry = DataCache_GetEntryForFormatEtc(This, &fmt_cpy);
2262 if (cache_entry)
2264 TRACE("found an existing cache entry\n");
2265 *pdwConnection = cache_entry->id;
2266 return CACHE_S_SAMECACHE;
2269 hr = DataCache_CreateEntry(This, &fmt_cpy, advf, FALSE, &cache_entry);
2271 if (SUCCEEDED(hr))
2273 *pdwConnection = cache_entry->id;
2274 setup_sink(This, cache_entry);
2277 return hr;
2280 static HRESULT WINAPI DataCache_Uncache(
2281 IOleCache2* iface,
2282 DWORD dwConnection)
2284 DataCache *This = impl_from_IOleCache2(iface);
2285 DataCacheEntry *cache_entry;
2287 TRACE("(%d)\n", dwConnection);
2289 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2290 if (cache_entry->id == dwConnection)
2292 DataCacheEntry_Destroy(This, cache_entry);
2293 return S_OK;
2296 WARN("no connection found for %d\n", dwConnection);
2298 return OLE_E_NOCONNECTION;
2301 static HRESULT WINAPI DataCache_EnumCache(IOleCache2 *iface,
2302 IEnumSTATDATA **enum_stat)
2304 DataCache *This = impl_from_IOleCache2( iface );
2305 DataCacheEntry *cache_entry;
2306 int i = 0, count = 0;
2307 STATDATA *data;
2308 HRESULT hr;
2310 TRACE( "(%p, %p)\n", This, enum_stat );
2312 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2314 count++;
2315 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2316 count++;
2319 data = CoTaskMemAlloc( count * sizeof(*data) );
2320 if (!data) return E_OUTOFMEMORY;
2322 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2324 if (i == count) goto fail;
2325 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2326 if (FAILED(hr)) goto fail;
2327 data[i].advf = cache_entry->advise_flags;
2328 data[i].pAdvSink = NULL;
2329 data[i].dwConnection = cache_entry->id;
2330 i++;
2332 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2334 if (i == count) goto fail;
2335 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2336 if (FAILED(hr)) goto fail;
2337 data[i].formatetc.cfFormat = CF_BITMAP;
2338 data[i].formatetc.tymed = TYMED_GDI;
2339 data[i].advf = cache_entry->advise_flags;
2340 data[i].pAdvSink = NULL;
2341 data[i].dwConnection = cache_entry->id;
2342 i++;
2346 hr = EnumSTATDATA_Construct( NULL, 0, i, data, FALSE, enum_stat );
2347 if (SUCCEEDED(hr)) return hr;
2349 fail:
2350 while (i--) CoTaskMemFree( data[i].formatetc.ptd );
2351 CoTaskMemFree( data );
2352 return hr;
2355 static HRESULT WINAPI DataCache_InitCache(
2356 IOleCache2* iface,
2357 IDataObject* pDataObject)
2359 FIXME("stub\n");
2360 return E_NOTIMPL;
2363 static HRESULT WINAPI DataCache_IOleCache2_SetData(
2364 IOleCache2* iface,
2365 FORMATETC* pformatetc,
2366 STGMEDIUM* pmedium,
2367 BOOL fRelease)
2369 DataCache *This = impl_from_IOleCache2(iface);
2370 DataCacheEntry *cache_entry;
2371 HRESULT hr;
2373 TRACE("(%p, %p, %s)\n", pformatetc, pmedium, fRelease ? "TRUE" : "FALSE");
2374 TRACE("formatetc = %s\n", debugstr_formatetc(pformatetc));
2376 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetc);
2377 if (cache_entry)
2379 hr = DataCacheEntry_SetData(cache_entry, pformatetc, pmedium, fRelease);
2381 if (SUCCEEDED(hr))
2382 DataCache_FireOnViewChange(This, cache_entry->fmtetc.dwAspect,
2383 cache_entry->fmtetc.lindex);
2385 return hr;
2387 WARN("cache entry not found\n");
2389 return OLE_E_BLANK;
2392 static HRESULT WINAPI DataCache_UpdateCache(
2393 IOleCache2* iface,
2394 LPDATAOBJECT pDataObject,
2395 DWORD grfUpdf,
2396 LPVOID pReserved)
2398 FIXME("(%p, 0x%x, %p): stub\n", pDataObject, grfUpdf, pReserved);
2399 return E_NOTIMPL;
2402 static HRESULT WINAPI DataCache_DiscardCache(
2403 IOleCache2* iface,
2404 DWORD dwDiscardOptions)
2406 DataCache *This = impl_from_IOleCache2(iface);
2407 DataCacheEntry *cache_entry;
2408 HRESULT hr = S_OK;
2410 TRACE("(%d)\n", dwDiscardOptions);
2412 if (dwDiscardOptions == DISCARDCACHE_SAVEIFDIRTY)
2413 hr = DataCache_Save(&This->IPersistStorage_iface, This->presentationStorage, TRUE);
2415 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2417 hr = DataCacheEntry_DiscardData(cache_entry);
2418 if (FAILED(hr))
2419 break;
2422 return hr;
2426 /*********************************************************
2427 * Method implementation for the IOleCacheControl
2428 * part of the DataCache class.
2431 /************************************************************************
2432 * DataCache_IOleCacheControl_QueryInterface (IUnknown)
2434 static HRESULT WINAPI DataCache_IOleCacheControl_QueryInterface(
2435 IOleCacheControl* iface,
2436 REFIID riid,
2437 void** ppvObject)
2439 DataCache *this = impl_from_IOleCacheControl(iface);
2441 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2444 /************************************************************************
2445 * DataCache_IOleCacheControl_AddRef (IUnknown)
2447 static ULONG WINAPI DataCache_IOleCacheControl_AddRef(
2448 IOleCacheControl* iface)
2450 DataCache *this = impl_from_IOleCacheControl(iface);
2452 return IUnknown_AddRef(this->outer_unk);
2455 /************************************************************************
2456 * DataCache_IOleCacheControl_Release (IUnknown)
2458 static ULONG WINAPI DataCache_IOleCacheControl_Release(
2459 IOleCacheControl* iface)
2461 DataCache *this = impl_from_IOleCacheControl(iface);
2463 return IUnknown_Release(this->outer_unk);
2466 /************************************************************************
2467 * DataCache_OnRun (IOleCacheControl)
2469 static HRESULT WINAPI DataCache_OnRun(IOleCacheControl* iface, IDataObject *data_obj)
2471 DataCache *This = impl_from_IOleCacheControl(iface);
2472 DataCacheEntry *cache_entry;
2474 TRACE("(%p)->(%p)\n", iface, data_obj);
2476 if(This->running_object) return S_OK;
2478 /* No reference is taken on the data object */
2479 This->running_object = data_obj;
2481 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2483 setup_sink(This, cache_entry);
2486 return S_OK;
2489 /************************************************************************
2490 * DataCache_OnStop (IOleCacheControl)
2492 static HRESULT WINAPI DataCache_OnStop(IOleCacheControl* iface)
2494 DataCache *This = impl_from_IOleCacheControl(iface);
2495 DataCacheEntry *cache_entry;
2497 TRACE("(%p)\n", iface);
2499 if(!This->running_object) return S_OK;
2501 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2503 if(cache_entry->sink_id)
2505 IDataObject_DUnadvise(This->running_object, cache_entry->sink_id);
2506 cache_entry->sink_id = 0;
2510 /* No ref taken in OnRun, so no Release call here */
2511 This->running_object = NULL;
2512 return S_OK;
2515 /************************************************************************
2516 * IAdviseSink methods.
2517 * This behaves as an internal object to the data cache. QI'ing its ptr doesn't
2518 * give access to the cache's other interfaces. We don't maintain a ref count,
2519 * the object exists as long as the cache is around.
2521 static HRESULT WINAPI DataCache_IAdviseSink_QueryInterface(IAdviseSink *iface, REFIID iid, void **obj)
2523 *obj = NULL;
2524 if (IsEqualIID(&IID_IUnknown, iid) ||
2525 IsEqualIID(&IID_IAdviseSink, iid))
2527 *obj = iface;
2530 if(*obj)
2532 IAdviseSink_AddRef(iface);
2533 return S_OK;
2535 return E_NOINTERFACE;
2538 static ULONG WINAPI DataCache_IAdviseSink_AddRef(IAdviseSink *iface)
2540 return 2;
2543 static ULONG WINAPI DataCache_IAdviseSink_Release(IAdviseSink *iface)
2545 return 1;
2548 static void WINAPI DataCache_OnDataChange(IAdviseSink *iface, FORMATETC *fmt, STGMEDIUM *med)
2550 DataCache *This = impl_from_IAdviseSink(iface);
2551 TRACE("(%p)->(%s, %p)\n", This, debugstr_formatetc(fmt), med);
2552 IOleCache2_SetData(&This->IOleCache2_iface, fmt, med, FALSE);
2555 static void WINAPI DataCache_OnViewChange(IAdviseSink *iface, DWORD aspect, LONG index)
2557 FIXME("stub\n");
2560 static void WINAPI DataCache_OnRename(IAdviseSink *iface, IMoniker *mk)
2562 FIXME("stub\n");
2565 static void WINAPI DataCache_OnSave(IAdviseSink *iface)
2567 FIXME("stub\n");
2570 static void WINAPI DataCache_OnClose(IAdviseSink *iface)
2572 FIXME("stub\n");
2576 * Virtual function tables for the DataCache class.
2578 static const IUnknownVtbl DataCache_NDIUnknown_VTable =
2580 DataCache_NDIUnknown_QueryInterface,
2581 DataCache_NDIUnknown_AddRef,
2582 DataCache_NDIUnknown_Release
2585 static const IDataObjectVtbl DataCache_IDataObject_VTable =
2587 DataCache_IDataObject_QueryInterface,
2588 DataCache_IDataObject_AddRef,
2589 DataCache_IDataObject_Release,
2590 DataCache_GetData,
2591 DataCache_GetDataHere,
2592 DataCache_QueryGetData,
2593 DataCache_GetCanonicalFormatEtc,
2594 DataCache_IDataObject_SetData,
2595 DataCache_EnumFormatEtc,
2596 DataCache_DAdvise,
2597 DataCache_DUnadvise,
2598 DataCache_EnumDAdvise
2601 static const IPersistStorageVtbl DataCache_IPersistStorage_VTable =
2603 DataCache_IPersistStorage_QueryInterface,
2604 DataCache_IPersistStorage_AddRef,
2605 DataCache_IPersistStorage_Release,
2606 DataCache_GetClassID,
2607 DataCache_IsDirty,
2608 DataCache_InitNew,
2609 DataCache_Load,
2610 DataCache_Save,
2611 DataCache_SaveCompleted,
2612 DataCache_HandsOffStorage
2615 static const IViewObject2Vtbl DataCache_IViewObject2_VTable =
2617 DataCache_IViewObject2_QueryInterface,
2618 DataCache_IViewObject2_AddRef,
2619 DataCache_IViewObject2_Release,
2620 DataCache_Draw,
2621 DataCache_GetColorSet,
2622 DataCache_Freeze,
2623 DataCache_Unfreeze,
2624 DataCache_SetAdvise,
2625 DataCache_GetAdvise,
2626 DataCache_GetExtent
2629 static const IOleCache2Vtbl DataCache_IOleCache2_VTable =
2631 DataCache_IOleCache2_QueryInterface,
2632 DataCache_IOleCache2_AddRef,
2633 DataCache_IOleCache2_Release,
2634 DataCache_Cache,
2635 DataCache_Uncache,
2636 DataCache_EnumCache,
2637 DataCache_InitCache,
2638 DataCache_IOleCache2_SetData,
2639 DataCache_UpdateCache,
2640 DataCache_DiscardCache
2643 static const IOleCacheControlVtbl DataCache_IOleCacheControl_VTable =
2645 DataCache_IOleCacheControl_QueryInterface,
2646 DataCache_IOleCacheControl_AddRef,
2647 DataCache_IOleCacheControl_Release,
2648 DataCache_OnRun,
2649 DataCache_OnStop
2652 static const IAdviseSinkVtbl DataCache_IAdviseSink_VTable =
2654 DataCache_IAdviseSink_QueryInterface,
2655 DataCache_IAdviseSink_AddRef,
2656 DataCache_IAdviseSink_Release,
2657 DataCache_OnDataChange,
2658 DataCache_OnViewChange,
2659 DataCache_OnRename,
2660 DataCache_OnSave,
2661 DataCache_OnClose
2664 /*********************************************************
2665 * Method implementation for DataCache class.
2667 static DataCache* DataCache_Construct(
2668 REFCLSID clsid,
2669 LPUNKNOWN pUnkOuter)
2671 DataCache* newObject = 0;
2674 * Allocate space for the object.
2676 newObject = HeapAlloc(GetProcessHeap(), 0, sizeof(DataCache));
2678 if (newObject==0)
2679 return newObject;
2682 * Initialize the virtual function table.
2684 newObject->IDataObject_iface.lpVtbl = &DataCache_IDataObject_VTable;
2685 newObject->IUnknown_inner.lpVtbl = &DataCache_NDIUnknown_VTable;
2686 newObject->IPersistStorage_iface.lpVtbl = &DataCache_IPersistStorage_VTable;
2687 newObject->IViewObject2_iface.lpVtbl = &DataCache_IViewObject2_VTable;
2688 newObject->IOleCache2_iface.lpVtbl = &DataCache_IOleCache2_VTable;
2689 newObject->IOleCacheControl_iface.lpVtbl = &DataCache_IOleCacheControl_VTable;
2690 newObject->IAdviseSink_iface.lpVtbl = &DataCache_IAdviseSink_VTable;
2691 newObject->outer_unk = pUnkOuter ? pUnkOuter : &newObject->IUnknown_inner;
2692 newObject->ref = 1;
2695 * Initialize the other members of the structure.
2697 newObject->sinkAspects = 0;
2698 newObject->sinkAdviseFlag = 0;
2699 newObject->sinkInterface = 0;
2700 newObject->clsid = CLSID_NULL;
2701 newObject->presentationStorage = NULL;
2702 list_init(&newObject->cache_list);
2703 newObject->last_cache_id = 2;
2704 newObject->dirty = FALSE;
2705 newObject->running_object = NULL;
2707 create_automatic_entry( newObject, clsid );
2708 newObject->clsid = *clsid;
2710 return newObject;
2713 /******************************************************************************
2714 * CreateDataCache [OLE32.@]
2716 * Creates a data cache to allow an object to render one or more of its views,
2717 * whether running or not.
2719 * PARAMS
2720 * pUnkOuter [I] Outer unknown for the object.
2721 * rclsid [I]
2722 * riid [I] IID of interface to return.
2723 * ppvObj [O] Address where the data cache object will be stored on return.
2725 * RETURNS
2726 * Success: S_OK.
2727 * Failure: HRESULT code.
2729 * NOTES
2730 * The following interfaces are supported by the returned data cache object:
2731 * IOleCache, IOleCache2, IOleCacheControl, IPersistStorage, IDataObject,
2732 * IViewObject and IViewObject2.
2734 HRESULT WINAPI CreateDataCache(
2735 LPUNKNOWN pUnkOuter,
2736 REFCLSID rclsid,
2737 REFIID riid,
2738 LPVOID* ppvObj)
2740 DataCache* newCache = NULL;
2741 HRESULT hr = S_OK;
2743 TRACE("(%s, %p, %s, %p)\n", debugstr_guid(rclsid), pUnkOuter, debugstr_guid(riid), ppvObj);
2746 * Sanity check
2748 if (ppvObj==0)
2749 return E_POINTER;
2751 *ppvObj = 0;
2754 * If this cache is constructed for aggregation, make sure
2755 * the caller is requesting the IUnknown interface.
2756 * This is necessary because it's the only time the non-delegating
2757 * IUnknown pointer can be returned to the outside.
2759 if ( pUnkOuter && !IsEqualIID(&IID_IUnknown, riid) )
2760 return E_INVALIDARG;
2763 * Try to construct a new instance of the class.
2765 newCache = DataCache_Construct(rclsid,
2766 pUnkOuter);
2768 if (newCache == 0)
2769 return E_OUTOFMEMORY;
2771 hr = IUnknown_QueryInterface(&newCache->IUnknown_inner, riid, ppvObj);
2772 IUnknown_Release(&newCache->IUnknown_inner);
2774 return hr;