ole32: Implement IOleCache2_UpdateCache().
[wine.git] / dlls / ole32 / datacache.c
blob2c5e45134ed44f165003be41300f757cd59d4236
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 == -1)
475 DWORD cf;
476 hr = IStream_Read(stream, &cf, sizeof(cf), &read);
477 if (hr != S_OK || read != sizeof(cf))
478 return DV_E_CLIPFORMAT;
479 *clipformat = cf;
481 else
483 char *format_name = HeapAlloc(GetProcessHeap(), 0, length);
484 if (!format_name)
485 return E_OUTOFMEMORY;
486 hr = IStream_Read(stream, format_name, length, &read);
487 if (hr != S_OK || read != length || format_name[length - 1] != '\0')
489 HeapFree(GetProcessHeap(), 0, format_name);
490 return DV_E_CLIPFORMAT;
492 *clipformat = RegisterClipboardFormatA(format_name);
493 HeapFree(GetProcessHeap(), 0, format_name);
495 return S_OK;
498 static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat)
500 DWORD length;
501 HRESULT hr;
503 if (clipformat < 0xc000)
504 length = -1;
505 else
506 length = GetClipboardFormatNameA(clipformat, NULL, 0);
507 hr = IStream_Write(stream, &length, sizeof(length), NULL);
508 if (FAILED(hr))
509 return hr;
510 if (clipformat < 0xc000)
512 DWORD cf = clipformat;
513 hr = IStream_Write(stream, &cf, sizeof(cf), NULL);
515 else
517 char *format_name = HeapAlloc(GetProcessHeap(), 0, length);
518 if (!format_name)
519 return E_OUTOFMEMORY;
520 GetClipboardFormatNameA(clipformat, format_name, length);
521 hr = IStream_Write(stream, format_name, length, NULL);
522 HeapFree(GetProcessHeap(), 0, format_name);
524 return hr;
527 /************************************************************************
528 * DataCacheEntry_OpenPresStream
530 * This method will find the stream for the given presentation. It makes
531 * no attempt at fallback.
533 * Param:
534 * this - Pointer to the DataCache object
535 * drawAspect - The aspect of the object that we wish to draw.
536 * pStm - A returned stream. It points to the beginning of the
537 * - presentation data, including the header.
539 * Errors:
540 * S_OK The requested stream has been opened.
541 * OLE_E_BLANK The requested stream could not be found.
542 * Quite a few others I'm too lazy to map correctly.
544 * Notes:
545 * Algorithm: Scan the elements of the presentation storage, looking
546 * for presentation streams. For each presentation stream,
547 * load the header and check to see if the aspect matches.
549 * If a fallback is desired, just opening the first presentation stream
550 * is a possibility.
552 static HRESULT DataCacheEntry_OpenPresStream(DataCacheEntry *cache_entry, IStream **ppStm)
554 HRESULT hr;
555 LARGE_INTEGER offset;
557 if (cache_entry->stream)
559 /* Rewind the stream before returning it. */
560 offset.QuadPart = 0;
562 hr = IStream_Seek( cache_entry->stream, offset, STREAM_SEEK_SET, NULL );
563 if (SUCCEEDED( hr ))
565 *ppStm = cache_entry->stream;
566 IStream_AddRef( cache_entry->stream );
569 else
570 hr = OLE_E_BLANK;
572 return hr;
576 static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm )
578 HRESULT hr;
579 STATSTG stat;
580 ULARGE_INTEGER current_pos;
581 void *bits;
582 METAFILEPICT *mfpict;
583 HGLOBAL hmfpict;
584 PresentationDataHeader header;
585 CLIPFORMAT clipformat;
586 static const LARGE_INTEGER offset_zero;
587 ULONG read;
589 if (cache_entry->stream_type != pres_stream)
591 FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type );
592 return E_FAIL;
595 hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
596 if (FAILED( hr )) return hr;
598 hr = read_clipformat( stm, &clipformat );
599 if (FAILED( hr )) return hr;
601 hr = IStream_Read( stm, &header, sizeof(header), &read );
602 if (hr != S_OK || read != sizeof(header)) return E_FAIL;
604 hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, &current_pos );
605 if (FAILED( hr )) return hr;
607 stat.cbSize.QuadPart -= current_pos.QuadPart;
609 hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) );
610 if (!hmfpict) return E_OUTOFMEMORY;
611 mfpict = GlobalLock( hmfpict );
613 bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart);
614 if (!bits)
616 GlobalFree( hmfpict );
617 return E_OUTOFMEMORY;
620 hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read );
621 if (hr != S_OK || read != stat.cbSize.u.LowPart) hr = E_FAIL;
623 if (SUCCEEDED( hr ))
625 /* FIXME: get this from the stream */
626 mfpict->mm = MM_ANISOTROPIC;
627 mfpict->xExt = header.dwObjectExtentX;
628 mfpict->yExt = header.dwObjectExtentY;
629 mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits );
630 if (!mfpict->hMF)
631 hr = E_FAIL;
634 GlobalUnlock( hmfpict );
635 if (SUCCEEDED( hr ))
637 cache_entry->stgmedium.tymed = TYMED_MFPICT;
638 cache_entry->stgmedium.u.hMetaFilePict = hmfpict;
640 else
641 GlobalFree( hmfpict );
643 HeapFree( GetProcessHeap(), 0, bits );
645 return hr;
648 static HRESULT load_dib( DataCacheEntry *cache_entry, IStream *stm )
650 HRESULT hr;
651 STATSTG stat;
652 void *dib;
653 HGLOBAL hglobal;
654 ULONG read, info_size, bi_size;
655 BITMAPFILEHEADER file;
656 BITMAPINFOHEADER *info;
658 if (cache_entry->stream_type != contents_stream)
660 FIXME( "Unimplemented for stream type %d\n", cache_entry->stream_type );
661 return E_FAIL;
664 hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
665 if (FAILED( hr )) return hr;
667 if (stat.cbSize.QuadPart < sizeof(file) + sizeof(DWORD)) return E_FAIL;
668 hr = IStream_Read( stm, &file, sizeof(file), &read );
669 if (hr != S_OK || read != sizeof(file)) return E_FAIL;
670 stat.cbSize.QuadPart -= sizeof(file);
672 hglobal = GlobalAlloc( GMEM_MOVEABLE, stat.cbSize.u.LowPart );
673 if (!hglobal) return E_OUTOFMEMORY;
674 dib = GlobalLock( hglobal );
676 hr = IStream_Read( stm, dib, sizeof(DWORD), &read );
677 if (hr != S_OK || read != sizeof(DWORD)) goto fail;
678 bi_size = *(DWORD *)dib;
679 if (stat.cbSize.QuadPart < bi_size) goto fail;
681 hr = IStream_Read( stm, (char *)dib + sizeof(DWORD), bi_size - sizeof(DWORD), &read );
682 if (hr != S_OK || read != bi_size - sizeof(DWORD)) goto fail;
684 info_size = bitmap_info_size( dib, DIB_RGB_COLORS );
685 if (stat.cbSize.QuadPart < info_size) goto fail;
686 if (info_size > bi_size)
688 hr = IStream_Read( stm, (char *)dib + bi_size, info_size - bi_size, &read );
689 if (hr != S_OK || read != info_size - bi_size) goto fail;
691 stat.cbSize.QuadPart -= info_size;
693 if (file.bfOffBits)
695 LARGE_INTEGER skip;
697 skip.QuadPart = file.bfOffBits - sizeof(file) - info_size;
698 if (stat.cbSize.QuadPart < skip.QuadPart) goto fail;
699 hr = IStream_Seek( stm, skip, STREAM_SEEK_CUR, NULL );
700 if (hr != S_OK) goto fail;
701 stat.cbSize.QuadPart -= skip.QuadPart;
704 hr = IStream_Read( stm, (char *)dib + info_size, stat.cbSize.u.LowPart, &read );
705 if (hr != S_OK || read != stat.cbSize.QuadPart) goto fail;
707 if (bi_size >= sizeof(*info))
709 info = (BITMAPINFOHEADER *)dib;
710 if (info->biXPelsPerMeter == 0 || info->biYPelsPerMeter == 0)
712 HDC hdc = GetDC( 0 );
713 info->biXPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSX ), 10000, 254 );
714 info->biYPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSY ), 10000, 254 );
715 ReleaseDC( 0, hdc );
719 GlobalUnlock( hglobal );
721 cache_entry->stgmedium.tymed = TYMED_HGLOBAL;
722 cache_entry->stgmedium.u.hGlobal = hglobal;
724 return S_OK;
726 fail:
727 GlobalUnlock( hglobal );
728 GlobalFree( hglobal );
729 return E_FAIL;
733 /************************************************************************
734 * DataCacheEntry_LoadData
736 * This method will read information for the requested presentation
737 * into the given structure.
739 * Param:
740 * This - The entry to load the data from.
742 * Returns:
743 * This method returns a metafile handle if it is successful.
744 * it will return 0 if not.
746 static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry)
748 HRESULT hr;
749 IStream *stm;
751 hr = DataCacheEntry_OpenPresStream( cache_entry, &stm );
752 if (FAILED(hr)) return hr;
754 switch (cache_entry->fmtetc.cfFormat)
756 case CF_METAFILEPICT:
757 hr = load_mf_pict( cache_entry, stm );
758 break;
760 case CF_DIB:
761 hr = load_dib( cache_entry, stm );
762 break;
764 default:
765 FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat );
766 hr = E_NOTIMPL;
769 IStream_Release( stm );
770 return hr;
773 static HRESULT DataCacheEntry_CreateStream(DataCacheEntry *cache_entry,
774 IStorage *storage, IStream **stream)
776 WCHAR wszName[] = {2,'O','l','e','P','r','e','s',
777 '0' + (cache_entry->stream_number / 100) % 10,
778 '0' + (cache_entry->stream_number / 10) % 10,
779 '0' + cache_entry->stream_number % 10, 0};
781 /* FIXME: cache the created stream in This? */
782 return IStorage_CreateStream(storage, wszName,
783 STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
784 0, 0, stream);
787 static HRESULT DataCacheEntry_Save(DataCacheEntry *cache_entry, IStorage *storage,
788 BOOL same_as_load)
790 PresentationDataHeader header;
791 HRESULT hr;
792 IStream *pres_stream;
793 void *data = NULL;
795 TRACE("stream_number = %d, fmtetc = %s\n", cache_entry->stream_number, debugstr_formatetc(&cache_entry->fmtetc));
797 hr = DataCacheEntry_CreateStream(cache_entry, storage, &pres_stream);
798 if (FAILED(hr))
799 return hr;
801 hr = write_clipformat(pres_stream, cache_entry->fmtetc.cfFormat);
802 if (FAILED(hr))
803 return hr;
805 if (cache_entry->fmtetc.ptd)
806 FIXME("ptd not serialized\n");
807 header.unknown3 = 4;
808 header.dvAspect = cache_entry->fmtetc.dwAspect;
809 header.lindex = cache_entry->fmtetc.lindex;
810 header.advf = cache_entry->advise_flags;
811 header.unknown7 = 0;
812 header.dwObjectExtentX = 0;
813 header.dwObjectExtentY = 0;
814 header.dwSize = 0;
816 /* size the data */
817 switch (cache_entry->fmtetc.cfFormat)
819 case CF_METAFILEPICT:
821 if (cache_entry->stgmedium.tymed != TYMED_NULL)
823 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
824 if (!mfpict)
826 IStream_Release(pres_stream);
827 return DV_E_STGMEDIUM;
829 header.dwObjectExtentX = mfpict->xExt;
830 header.dwObjectExtentY = mfpict->yExt;
831 header.dwSize = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
832 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
834 break;
836 default:
837 break;
841 * Write the header.
843 hr = IStream_Write(pres_stream, &header, sizeof(PresentationDataHeader),
844 NULL);
845 if (FAILED(hr))
847 IStream_Release(pres_stream);
848 return hr;
851 /* get the data */
852 switch (cache_entry->fmtetc.cfFormat)
854 case CF_METAFILEPICT:
856 if (cache_entry->stgmedium.tymed != TYMED_NULL)
858 const METAFILEPICT *mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict);
859 if (!mfpict)
861 IStream_Release(pres_stream);
862 return DV_E_STGMEDIUM;
864 data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
865 GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data);
866 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
868 break;
870 default:
871 break;
874 if (data)
875 hr = IStream_Write(pres_stream, data, header.dwSize, NULL);
876 HeapFree(GetProcessHeap(), 0, data);
878 IStream_Release(pres_stream);
879 return hr;
882 /* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL.
883 * does no checking of whether src_stgm has a supported tymed, so this should be
884 * done in the caller */
885 static HRESULT copy_stg_medium(CLIPFORMAT cf, STGMEDIUM *dest_stgm,
886 const STGMEDIUM *src_stgm)
888 if (src_stgm->tymed == TYMED_MFPICT)
890 const METAFILEPICT *src_mfpict = GlobalLock(src_stgm->u.hMetaFilePict);
891 METAFILEPICT *dest_mfpict;
893 if (!src_mfpict)
894 return DV_E_STGMEDIUM;
895 dest_stgm->u.hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT));
896 dest_mfpict = GlobalLock(dest_stgm->u.hMetaFilePict);
897 if (!dest_mfpict)
899 GlobalUnlock(src_stgm->u.hMetaFilePict);
900 return E_OUTOFMEMORY;
902 *dest_mfpict = *src_mfpict;
903 dest_mfpict->hMF = CopyMetaFileW(src_mfpict->hMF, NULL);
904 GlobalUnlock(src_stgm->u.hMetaFilePict);
905 GlobalUnlock(dest_stgm->u.hMetaFilePict);
907 else if (src_stgm->tymed != TYMED_NULL)
909 dest_stgm->u.hGlobal = OleDuplicateData(src_stgm->u.hGlobal, cf,
910 GMEM_MOVEABLE);
911 if (!dest_stgm->u.hGlobal)
912 return E_OUTOFMEMORY;
914 dest_stgm->tymed = src_stgm->tymed;
915 dest_stgm->pUnkForRelease = src_stgm->pUnkForRelease;
916 if (dest_stgm->pUnkForRelease)
917 IUnknown_AddRef(dest_stgm->pUnkForRelease);
918 return S_OK;
921 static HGLOBAL synthesize_dib( HBITMAP bm )
923 HDC hdc = GetDC( 0 );
924 BITMAPINFOHEADER header;
925 BITMAPINFO *bmi;
926 HGLOBAL ret = 0;
927 DWORD header_size;
929 memset( &header, 0, sizeof(header) );
930 header.biSize = sizeof(header);
931 if (!GetDIBits( hdc, bm, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done;
933 header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS );
934 if (!(ret = GlobalAlloc( GMEM_MOVEABLE, header_size + header.biSizeImage ))) goto done;
935 bmi = GlobalLock( ret );
936 memset( bmi, 0, header_size );
937 memcpy( bmi, &header, header.biSize );
938 GetDIBits( hdc, bm, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS );
939 GlobalUnlock( ret );
941 done:
942 ReleaseDC( 0, hdc );
943 return ret;
946 static HBITMAP synthesize_bitmap( HGLOBAL dib )
948 HBITMAP ret = 0;
949 BITMAPINFO *bmi;
950 HDC hdc = GetDC( 0 );
952 if ((bmi = GlobalLock( dib )))
954 /* FIXME: validate data size */
955 ret = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT,
956 (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ),
957 bmi, DIB_RGB_COLORS );
958 GlobalUnlock( dib );
960 ReleaseDC( 0, hdc );
961 return ret;
964 static HENHMETAFILE synthesize_emf( HMETAFILEPICT data )
966 METAFILEPICT *pict;
967 HENHMETAFILE emf = 0;
968 UINT size;
969 void *bits;
971 if (!(pict = GlobalLock( data ))) return 0;
973 size = GetMetaFileBitsEx( pict->hMF, 0, NULL );
974 if ((bits = HeapAlloc( GetProcessHeap(), 0, size )))
976 GetMetaFileBitsEx( pict->hMF, size, bits );
977 emf = SetWinMetaFileBits( size, bits, NULL, pict );
978 HeapFree( GetProcessHeap(), 0, bits );
981 GlobalUnlock( data );
982 return emf;
985 static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry,
986 const FORMATETC *formatetc,
987 STGMEDIUM *stgmedium,
988 BOOL fRelease)
990 STGMEDIUM copy;
992 if ((!cache_entry->fmtetc.cfFormat && !formatetc->cfFormat) ||
993 (cache_entry->fmtetc.tymed == TYMED_NULL && formatetc->tymed == TYMED_NULL) ||
994 stgmedium->tymed == TYMED_NULL)
996 WARN("invalid formatetc\n");
997 return DV_E_FORMATETC;
1000 cache_entry->dirty = TRUE;
1001 ReleaseStgMedium(&cache_entry->stgmedium);
1003 if (formatetc->cfFormat == CF_BITMAP)
1005 copy.tymed = TYMED_HGLOBAL;
1006 copy.u.hGlobal = synthesize_dib( stgmedium->u.hBitmap );
1007 copy.pUnkForRelease = NULL;
1008 if (fRelease) ReleaseStgMedium(stgmedium);
1009 stgmedium = &copy;
1010 fRelease = TRUE;
1012 else if (formatetc->cfFormat == CF_METAFILEPICT && cache_entry->fmtetc.cfFormat == CF_ENHMETAFILE)
1014 copy.tymed = TYMED_ENHMF;
1015 copy.u.hEnhMetaFile = synthesize_emf( stgmedium->u.hMetaFilePict );
1016 if (fRelease) ReleaseStgMedium(stgmedium);
1017 stgmedium = &copy;
1018 fRelease = TRUE;
1021 if (fRelease)
1023 cache_entry->stgmedium = *stgmedium;
1024 return S_OK;
1026 else
1027 return copy_stg_medium(cache_entry->fmtetc.cfFormat, &cache_entry->stgmedium, stgmedium);
1030 static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, FORMATETC *fmt, STGMEDIUM *stgmedium)
1032 if (cache_entry->stgmedium.tymed == TYMED_NULL && cache_entry->stream)
1034 HRESULT hr = DataCacheEntry_LoadData(cache_entry);
1035 if (FAILED(hr))
1036 return hr;
1038 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1039 return OLE_E_BLANK;
1041 if (fmt->cfFormat == CF_BITMAP)
1043 stgmedium->tymed = TYMED_GDI;
1044 stgmedium->u.hBitmap = synthesize_bitmap( cache_entry->stgmedium.u.hGlobal );
1045 stgmedium->pUnkForRelease = NULL;
1046 return S_OK;
1048 return copy_stg_medium(cache_entry->fmtetc.cfFormat, stgmedium, &cache_entry->stgmedium);
1051 static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry)
1053 ReleaseStgMedium(&cache_entry->stgmedium);
1054 return S_OK;
1057 static inline void DataCacheEntry_HandsOffStorage(DataCacheEntry *cache_entry)
1059 if (cache_entry->stream)
1061 IStream_Release(cache_entry->stream);
1062 cache_entry->stream = NULL;
1066 static inline DWORD tymed_from_cf( DWORD cf )
1068 switch( cf )
1070 case CF_BITMAP: return TYMED_GDI;
1071 case CF_METAFILEPICT: return TYMED_MFPICT;
1072 case CF_ENHMETAFILE: return TYMED_ENHMF;
1073 case CF_DIB:
1074 default: return TYMED_HGLOBAL;
1078 /****************************************************************
1079 * create_automatic_entry
1081 * Creates an appropriate cache entry for one of the CLSID_Picture_
1082 * classes. The connection id of the entry is one. Any pre-existing
1083 * automatic entry is re-assigned a new connection id, and moved to
1084 * the end of the list.
1086 static HRESULT create_automatic_entry(DataCache *cache, const CLSID *clsid)
1088 static const struct data
1090 const CLSID *clsid;
1091 FORMATETC fmt;
1092 } data[] =
1094 { &CLSID_Picture_Dib, { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
1095 { &CLSID_Picture_Metafile, { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
1096 { &CLSID_Picture_EnhMetafile, { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
1097 { NULL }
1099 const struct data *ptr = data;
1100 struct list *head;
1101 DataCacheEntry *entry;
1103 if (IsEqualCLSID( &cache->clsid, clsid )) return S_OK;
1105 /* move and reassign any pre-existing automatic entry */
1106 if ((head = list_head( &cache->cache_list )))
1108 entry = LIST_ENTRY( head, DataCacheEntry, entry );
1109 if (entry->id == 1)
1111 list_remove( &entry->entry );
1112 entry->id = cache->last_cache_id++;
1113 list_add_tail( &cache->cache_list, &entry->entry );
1117 while (ptr->clsid)
1119 if (IsEqualCLSID( clsid, ptr->clsid ))
1120 return DataCache_CreateEntry( cache, &ptr->fmt, 0, TRUE, NULL );
1121 ptr++;
1123 return S_OK;
1126 /*********************************************************
1127 * Method implementation for the non delegating IUnknown
1128 * part of the DataCache class.
1131 /************************************************************************
1132 * DataCache_NDIUnknown_QueryInterface (IUnknown)
1134 * This version of QueryInterface will not delegate its implementation
1135 * to the outer unknown.
1137 static HRESULT WINAPI DataCache_NDIUnknown_QueryInterface(
1138 IUnknown* iface,
1139 REFIID riid,
1140 void** ppvObject)
1142 DataCache *this = impl_from_IUnknown(iface);
1144 if ( ppvObject==0 )
1145 return E_INVALIDARG;
1147 *ppvObject = 0;
1149 if (IsEqualIID(&IID_IUnknown, riid))
1151 if (this->outer_unk == iface) /* non-aggregated, return IUnknown from IOleCache2 */
1152 *ppvObject = &this->IOleCache2_iface;
1153 else
1154 *ppvObject = iface;
1156 else if (IsEqualIID(&IID_IDataObject, riid))
1158 *ppvObject = &this->IDataObject_iface;
1160 else if ( IsEqualIID(&IID_IPersistStorage, riid) ||
1161 IsEqualIID(&IID_IPersist, riid) )
1163 *ppvObject = &this->IPersistStorage_iface;
1165 else if ( IsEqualIID(&IID_IViewObject, riid) ||
1166 IsEqualIID(&IID_IViewObject2, riid) )
1168 *ppvObject = &this->IViewObject2_iface;
1170 else if ( IsEqualIID(&IID_IOleCache, riid) ||
1171 IsEqualIID(&IID_IOleCache2, riid) )
1173 *ppvObject = &this->IOleCache2_iface;
1175 else if ( IsEqualIID(&IID_IOleCacheControl, riid) )
1177 *ppvObject = &this->IOleCacheControl_iface;
1180 if ((*ppvObject)==0)
1182 WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid));
1183 return E_NOINTERFACE;
1186 IUnknown_AddRef((IUnknown*)*ppvObject);
1188 return S_OK;
1191 /************************************************************************
1192 * DataCache_NDIUnknown_AddRef (IUnknown)
1194 * This version of QueryInterface will not delegate its implementation
1195 * to the outer unknown.
1197 static ULONG WINAPI DataCache_NDIUnknown_AddRef(
1198 IUnknown* iface)
1200 DataCache *this = impl_from_IUnknown(iface);
1201 return InterlockedIncrement(&this->ref);
1204 /************************************************************************
1205 * DataCache_NDIUnknown_Release (IUnknown)
1207 * This version of QueryInterface will not delegate its implementation
1208 * to the outer unknown.
1210 static ULONG WINAPI DataCache_NDIUnknown_Release(
1211 IUnknown* iface)
1213 DataCache *this = impl_from_IUnknown(iface);
1214 ULONG ref;
1216 ref = InterlockedDecrement(&this->ref);
1218 if (ref == 0) DataCache_Destroy(this);
1220 return ref;
1223 /*********************************************************
1224 * Method implementation for the IDataObject
1225 * part of the DataCache class.
1228 /************************************************************************
1229 * DataCache_IDataObject_QueryInterface (IUnknown)
1231 static HRESULT WINAPI DataCache_IDataObject_QueryInterface(
1232 IDataObject* iface,
1233 REFIID riid,
1234 void** ppvObject)
1236 DataCache *this = impl_from_IDataObject(iface);
1238 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1241 /************************************************************************
1242 * DataCache_IDataObject_AddRef (IUnknown)
1244 static ULONG WINAPI DataCache_IDataObject_AddRef(
1245 IDataObject* iface)
1247 DataCache *this = impl_from_IDataObject(iface);
1249 return IUnknown_AddRef(this->outer_unk);
1252 /************************************************************************
1253 * DataCache_IDataObject_Release (IUnknown)
1255 static ULONG WINAPI DataCache_IDataObject_Release(
1256 IDataObject* iface)
1258 DataCache *this = impl_from_IDataObject(iface);
1260 return IUnknown_Release(this->outer_unk);
1263 /************************************************************************
1264 * DataCache_GetData
1266 * Get Data from a source dataobject using format pformatetcIn->cfFormat
1268 static HRESULT WINAPI DataCache_GetData(
1269 IDataObject* iface,
1270 LPFORMATETC pformatetcIn,
1271 STGMEDIUM* pmedium)
1273 DataCache *This = impl_from_IDataObject(iface);
1274 DataCacheEntry *cache_entry;
1276 TRACE("(%p, %s, %p)\n", iface, debugstr_formatetc(pformatetcIn), pmedium);
1278 memset(pmedium, 0, sizeof(*pmedium));
1280 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetcIn);
1281 if (!cache_entry)
1282 return OLE_E_BLANK;
1284 return DataCacheEntry_GetData(cache_entry, pformatetcIn, pmedium);
1287 static HRESULT WINAPI DataCache_GetDataHere(
1288 IDataObject* iface,
1289 LPFORMATETC pformatetc,
1290 STGMEDIUM* pmedium)
1292 FIXME("stub\n");
1293 return E_NOTIMPL;
1296 static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt )
1298 DataCache *This = impl_from_IDataObject( iface );
1299 DataCacheEntry *cache_entry;
1301 TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) );
1302 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1304 return cache_entry ? S_OK : S_FALSE;
1307 /************************************************************************
1308 * DataCache_EnumFormatEtc (IDataObject)
1310 * The data cache doesn't implement this method.
1312 static HRESULT WINAPI DataCache_GetCanonicalFormatEtc(
1313 IDataObject* iface,
1314 LPFORMATETC pformatectIn,
1315 LPFORMATETC pformatetcOut)
1317 TRACE("()\n");
1318 return E_NOTIMPL;
1321 /************************************************************************
1322 * DataCache_IDataObject_SetData (IDataObject)
1324 * This method is delegated to the IOleCache2 implementation.
1326 static HRESULT WINAPI DataCache_IDataObject_SetData(
1327 IDataObject* iface,
1328 LPFORMATETC pformatetc,
1329 STGMEDIUM* pmedium,
1330 BOOL fRelease)
1332 IOleCache2* oleCache = NULL;
1333 HRESULT hres;
1335 TRACE("(%p, %p, %p, %d)\n", iface, pformatetc, pmedium, fRelease);
1337 hres = IDataObject_QueryInterface(iface, &IID_IOleCache2, (void**)&oleCache);
1339 if (FAILED(hres))
1340 return E_UNEXPECTED;
1342 hres = IOleCache2_SetData(oleCache, pformatetc, pmedium, fRelease);
1344 IOleCache2_Release(oleCache);
1346 return hres;
1349 /************************************************************************
1350 * DataCache_EnumFormatEtc (IDataObject)
1352 * The data cache doesn't implement this method.
1354 static HRESULT WINAPI DataCache_EnumFormatEtc(
1355 IDataObject* iface,
1356 DWORD dwDirection,
1357 IEnumFORMATETC** ppenumFormatEtc)
1359 TRACE("()\n");
1360 return E_NOTIMPL;
1363 /************************************************************************
1364 * DataCache_DAdvise (IDataObject)
1366 * The data cache doesn't support connections.
1368 static HRESULT WINAPI DataCache_DAdvise(
1369 IDataObject* iface,
1370 FORMATETC* pformatetc,
1371 DWORD advf,
1372 IAdviseSink* pAdvSink,
1373 DWORD* pdwConnection)
1375 TRACE("()\n");
1376 return OLE_E_ADVISENOTSUPPORTED;
1379 /************************************************************************
1380 * DataCache_DUnadvise (IDataObject)
1382 * The data cache doesn't support connections.
1384 static HRESULT WINAPI DataCache_DUnadvise(
1385 IDataObject* iface,
1386 DWORD dwConnection)
1388 TRACE("()\n");
1389 return OLE_E_NOCONNECTION;
1392 /************************************************************************
1393 * DataCache_EnumDAdvise (IDataObject)
1395 * The data cache doesn't support connections.
1397 static HRESULT WINAPI DataCache_EnumDAdvise(
1398 IDataObject* iface,
1399 IEnumSTATDATA** ppenumAdvise)
1401 TRACE("()\n");
1402 return OLE_E_ADVISENOTSUPPORTED;
1405 /*********************************************************
1406 * Method implementation for the IDataObject
1407 * part of the DataCache class.
1410 /************************************************************************
1411 * DataCache_IPersistStorage_QueryInterface (IUnknown)
1413 static HRESULT WINAPI DataCache_IPersistStorage_QueryInterface(
1414 IPersistStorage* iface,
1415 REFIID riid,
1416 void** ppvObject)
1418 DataCache *this = impl_from_IPersistStorage(iface);
1420 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1423 /************************************************************************
1424 * DataCache_IPersistStorage_AddRef (IUnknown)
1426 static ULONG WINAPI DataCache_IPersistStorage_AddRef(
1427 IPersistStorage* iface)
1429 DataCache *this = impl_from_IPersistStorage(iface);
1431 return IUnknown_AddRef(this->outer_unk);
1434 /************************************************************************
1435 * DataCache_IPersistStorage_Release (IUnknown)
1437 static ULONG WINAPI DataCache_IPersistStorage_Release(
1438 IPersistStorage* iface)
1440 DataCache *this = impl_from_IPersistStorage(iface);
1442 return IUnknown_Release(this->outer_unk);
1445 /************************************************************************
1446 * DataCache_GetClassID (IPersistStorage)
1449 static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid)
1451 DataCache *This = impl_from_IPersistStorage( iface );
1453 TRACE( "(%p, %p) returning %s\n", iface, clsid, debugstr_guid(&This->clsid) );
1454 *clsid = This->clsid;
1456 return S_OK;
1459 /************************************************************************
1460 * DataCache_IsDirty (IPersistStorage)
1462 static HRESULT WINAPI DataCache_IsDirty(
1463 IPersistStorage* iface)
1465 DataCache *This = impl_from_IPersistStorage(iface);
1466 DataCacheEntry *cache_entry;
1468 TRACE("(%p)\n", iface);
1470 if (This->dirty)
1471 return S_OK;
1473 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1474 if (cache_entry->dirty)
1475 return S_OK;
1477 return S_FALSE;
1480 /************************************************************************
1481 * DataCache_InitNew (IPersistStorage)
1483 * The data cache implementation of IPersistStorage_InitNew simply stores
1484 * the storage pointer.
1486 static HRESULT WINAPI DataCache_InitNew(
1487 IPersistStorage* iface,
1488 IStorage* pStg)
1490 DataCache *This = impl_from_IPersistStorage(iface);
1491 CLSID clsid;
1492 HRESULT hr;
1494 TRACE("(%p, %p)\n", iface, pStg);
1496 if (This->presentationStorage != NULL)
1497 return CO_E_ALREADYINITIALIZED;
1499 This->presentationStorage = pStg;
1501 IStorage_AddRef(This->presentationStorage);
1502 This->dirty = TRUE;
1503 ReadClassStg( pStg, &clsid );
1504 hr = create_automatic_entry( This, &clsid );
1505 if (FAILED(hr))
1507 IStorage_Release( pStg );
1508 This->presentationStorage = NULL;
1509 return hr;
1511 This->clsid = clsid;
1513 return S_OK;
1517 static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, DWORD advf, IStream *stm,
1518 enum stream_type type )
1520 DataCacheEntry *cache_entry;
1521 HRESULT hr = S_OK;
1523 TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) );
1525 cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1526 if (!cache_entry)
1527 hr = DataCache_CreateEntry( This, fmt, advf, FALSE, &cache_entry );
1528 if (SUCCEEDED( hr ))
1530 DataCacheEntry_DiscardData( cache_entry );
1531 if (cache_entry->stream) IStream_Release( cache_entry->stream );
1532 cache_entry->stream = stm;
1533 IStream_AddRef( stm );
1534 cache_entry->stream_type = type;
1535 cache_entry->dirty = FALSE;
1537 return hr;
1540 static HRESULT parse_pres_streams( DataCache *This, IStorage *stg )
1542 HRESULT hr;
1543 IEnumSTATSTG *stat_enum;
1544 STATSTG stat;
1545 IStream *stm;
1546 PresentationDataHeader header;
1547 ULONG actual_read;
1548 CLIPFORMAT clipformat;
1549 FORMATETC fmtetc;
1551 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stat_enum );
1552 if (FAILED( hr )) return hr;
1554 while ((hr = IEnumSTATSTG_Next( stat_enum, 1, &stat, NULL )) == S_OK)
1556 if (DataCache_IsPresentationStream( &stat ))
1558 hr = IStorage_OpenStream( stg, stat.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1559 0, &stm );
1560 if (SUCCEEDED( hr ))
1562 hr = read_clipformat( stm, &clipformat );
1564 if (hr == S_OK)
1565 hr = IStream_Read( stm, &header, sizeof(header), &actual_read );
1567 if (hr == S_OK && actual_read == sizeof(header))
1569 fmtetc.cfFormat = clipformat;
1570 fmtetc.ptd = NULL; /* FIXME */
1571 fmtetc.dwAspect = header.dvAspect;
1572 fmtetc.lindex = header.lindex;
1573 fmtetc.tymed = tymed_from_cf( clipformat );
1575 add_cache_entry( This, &fmtetc, header.advf, stm, pres_stream );
1577 IStream_Release( stm );
1580 CoTaskMemFree( stat.pwcsName );
1582 IEnumSTATSTG_Release( stat_enum );
1584 return S_OK;
1587 static const FORMATETC static_dib_fmt = { CF_DIB, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
1589 static HRESULT parse_contents_stream( DataCache *This, IStorage *stg, IStream *stm )
1591 HRESULT hr;
1592 STATSTG stat;
1593 const FORMATETC *fmt;
1595 hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
1596 if (FAILED( hr )) return hr;
1598 if (IsEqualCLSID( &stat.clsid, &CLSID_Picture_Dib ))
1599 fmt = &static_dib_fmt;
1600 else
1602 FIXME("unsupported format %s\n", debugstr_guid( &stat.clsid ));
1603 return E_FAIL;
1606 return add_cache_entry( This, fmt, 0, stm, contents_stream );
1609 static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0};
1611 /************************************************************************
1612 * DataCache_Load (IPersistStorage)
1614 * The data cache implementation of IPersistStorage_Load doesn't
1615 * actually load anything. Instead, it holds on to the storage pointer
1616 * and it will load the presentation information when the
1617 * IDataObject_GetData or IViewObject2_Draw methods are called.
1619 static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *pStg )
1621 DataCache *This = impl_from_IPersistStorage(iface);
1622 HRESULT hr;
1623 IStream *stm;
1624 CLSID clsid;
1625 DataCacheEntry *entry, *cursor2;
1627 TRACE("(%p, %p)\n", iface, pStg);
1629 IPersistStorage_HandsOffStorage( iface );
1631 LIST_FOR_EACH_ENTRY_SAFE( entry, cursor2, &This->cache_list, DataCacheEntry, entry )
1632 DataCacheEntry_Destroy( This, entry );
1634 ReadClassStg( pStg, &clsid );
1635 hr = create_automatic_entry( This, &clsid );
1636 if (FAILED( hr )) return hr;
1638 This->clsid = clsid;
1640 hr = IStorage_OpenStream( pStg, CONTENTS, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
1641 0, &stm );
1642 if (SUCCEEDED( hr ))
1644 hr = parse_contents_stream( This, pStg, stm );
1645 IStream_Release( stm );
1648 if (FAILED(hr))
1649 hr = parse_pres_streams( This, pStg );
1651 if (SUCCEEDED( hr ))
1653 This->dirty = FALSE;
1654 This->presentationStorage = pStg;
1655 IStorage_AddRef( This->presentationStorage );
1658 return hr;
1661 /************************************************************************
1662 * DataCache_Save (IPersistStorage)
1664 * Until we actually connect to a running object and retrieve new
1665 * information to it, we never have to save anything. However, it is
1666 * our responsibility to copy the information when saving to a new
1667 * storage.
1669 static HRESULT WINAPI DataCache_Save(
1670 IPersistStorage* iface,
1671 IStorage* pStg,
1672 BOOL fSameAsLoad)
1674 DataCache *This = impl_from_IPersistStorage(iface);
1675 DataCacheEntry *cache_entry;
1676 BOOL dirty = FALSE;
1677 HRESULT hr = S_OK;
1678 unsigned short stream_number = 0;
1680 TRACE("(%p, %p, %d)\n", iface, pStg, fSameAsLoad);
1682 dirty = This->dirty;
1683 if (!dirty)
1685 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1687 dirty = cache_entry->dirty;
1688 if (dirty)
1689 break;
1693 /* assign stream numbers to the cache entries */
1694 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1696 if (cache_entry->stream_number != stream_number)
1698 cache_entry->dirty = TRUE; /* needs to be written out again */
1699 cache_entry->stream_number = stream_number;
1701 stream_number++;
1704 /* write out the cache entries */
1705 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1707 if (!fSameAsLoad || cache_entry->dirty)
1709 hr = DataCacheEntry_Save(cache_entry, pStg, fSameAsLoad);
1710 if (FAILED(hr))
1711 break;
1713 cache_entry->dirty = FALSE;
1717 This->dirty = FALSE;
1718 return hr;
1721 /************************************************************************
1722 * DataCache_SaveCompleted (IPersistStorage)
1724 * This method is called to tell the cache to release the storage
1725 * pointer it's currently holding.
1727 static HRESULT WINAPI DataCache_SaveCompleted(
1728 IPersistStorage* iface,
1729 IStorage* pStgNew)
1731 TRACE("(%p, %p)\n", iface, pStgNew);
1733 if (pStgNew)
1735 IPersistStorage_HandsOffStorage(iface);
1737 DataCache_Load(iface, pStgNew);
1740 return S_OK;
1743 /************************************************************************
1744 * DataCache_HandsOffStorage (IPersistStorage)
1746 * This method is called to tell the cache to release the storage
1747 * pointer it's currently holding.
1749 static HRESULT WINAPI DataCache_HandsOffStorage(
1750 IPersistStorage* iface)
1752 DataCache *this = impl_from_IPersistStorage(iface);
1753 DataCacheEntry *cache_entry;
1755 TRACE("(%p)\n", iface);
1757 if (this->presentationStorage != NULL)
1759 IStorage_Release(this->presentationStorage);
1760 this->presentationStorage = NULL;
1763 LIST_FOR_EACH_ENTRY(cache_entry, &this->cache_list, DataCacheEntry, entry)
1764 DataCacheEntry_HandsOffStorage(cache_entry);
1766 return S_OK;
1769 /*********************************************************
1770 * Method implementation for the IViewObject2
1771 * part of the DataCache class.
1774 /************************************************************************
1775 * DataCache_IViewObject2_QueryInterface (IUnknown)
1777 static HRESULT WINAPI DataCache_IViewObject2_QueryInterface(
1778 IViewObject2* iface,
1779 REFIID riid,
1780 void** ppvObject)
1782 DataCache *this = impl_from_IViewObject2(iface);
1784 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1787 /************************************************************************
1788 * DataCache_IViewObject2_AddRef (IUnknown)
1790 static ULONG WINAPI DataCache_IViewObject2_AddRef(
1791 IViewObject2* iface)
1793 DataCache *this = impl_from_IViewObject2(iface);
1795 return IUnknown_AddRef(this->outer_unk);
1798 /************************************************************************
1799 * DataCache_IViewObject2_Release (IUnknown)
1801 static ULONG WINAPI DataCache_IViewObject2_Release(
1802 IViewObject2* iface)
1804 DataCache *this = impl_from_IViewObject2(iface);
1806 return IUnknown_Release(this->outer_unk);
1809 /************************************************************************
1810 * DataCache_Draw (IViewObject2)
1812 * This method will draw the cached representation of the object
1813 * to the given device context.
1815 static HRESULT WINAPI DataCache_Draw(
1816 IViewObject2* iface,
1817 DWORD dwDrawAspect,
1818 LONG lindex,
1819 void* pvAspect,
1820 DVTARGETDEVICE* ptd,
1821 HDC hdcTargetDev,
1822 HDC hdcDraw,
1823 LPCRECTL lprcBounds,
1824 LPCRECTL lprcWBounds,
1825 BOOL (CALLBACK *pfnContinue)(ULONG_PTR dwContinue),
1826 ULONG_PTR dwContinue)
1828 DataCache *This = impl_from_IViewObject2(iface);
1829 HRESULT hres;
1830 DataCacheEntry *cache_entry;
1832 TRACE("(%p, %x, %d, %p, %p, %p, %p, %p, %p, %lx)\n",
1833 iface,
1834 dwDrawAspect,
1835 lindex,
1836 pvAspect,
1837 hdcTargetDev,
1838 hdcDraw,
1839 lprcBounds,
1840 lprcWBounds,
1841 pfnContinue,
1842 dwContinue);
1844 if (lprcBounds==NULL)
1845 return E_INVALIDARG;
1847 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1849 /* FIXME: compare ptd too */
1850 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
1851 (cache_entry->fmtetc.lindex != lindex))
1852 continue;
1854 /* if the data hasn't been loaded yet, do it now */
1855 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
1857 hres = DataCacheEntry_LoadData(cache_entry);
1858 if (FAILED(hres))
1859 continue;
1862 /* no data */
1863 if (cache_entry->stgmedium.tymed == TYMED_NULL)
1864 continue;
1866 if (pfnContinue && !pfnContinue(dwContinue)) return E_ABORT;
1868 switch (cache_entry->fmtetc.cfFormat)
1870 case CF_METAFILEPICT:
1873 * We have to be careful not to modify the state of the
1874 * DC.
1876 INT prevMapMode;
1877 SIZE oldWindowExt;
1878 SIZE oldViewportExt;
1879 POINT oldViewportOrg;
1880 METAFILEPICT *mfpict;
1882 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
1883 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
1884 continue;
1886 prevMapMode = SetMapMode(hdcDraw, mfpict->mm);
1888 SetWindowExtEx(hdcDraw,
1889 mfpict->xExt,
1890 mfpict->yExt,
1891 &oldWindowExt);
1893 SetViewportExtEx(hdcDraw,
1894 lprcBounds->right - lprcBounds->left,
1895 lprcBounds->bottom - lprcBounds->top,
1896 &oldViewportExt);
1898 SetViewportOrgEx(hdcDraw,
1899 lprcBounds->left,
1900 lprcBounds->top,
1901 &oldViewportOrg);
1903 PlayMetaFile(hdcDraw, mfpict->hMF);
1905 SetWindowExtEx(hdcDraw,
1906 oldWindowExt.cx,
1907 oldWindowExt.cy,
1908 NULL);
1910 SetViewportExtEx(hdcDraw,
1911 oldViewportExt.cx,
1912 oldViewportExt.cy,
1913 NULL);
1915 SetViewportOrgEx(hdcDraw,
1916 oldViewportOrg.x,
1917 oldViewportOrg.y,
1918 NULL);
1920 SetMapMode(hdcDraw, prevMapMode);
1922 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
1924 return S_OK;
1926 case CF_DIB:
1928 BITMAPINFO *info;
1929 BYTE *bits;
1931 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
1932 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
1933 continue;
1935 bits = (BYTE *) info + bitmap_info_size( info, DIB_RGB_COLORS );
1936 StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top,
1937 lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top,
1938 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
1939 bits, info, DIB_RGB_COLORS, SRCCOPY );
1941 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
1942 return S_OK;
1947 WARN("no data could be found to be drawn\n");
1949 return OLE_E_BLANK;
1952 static HRESULT WINAPI DataCache_GetColorSet(
1953 IViewObject2* iface,
1954 DWORD dwDrawAspect,
1955 LONG lindex,
1956 void* pvAspect,
1957 DVTARGETDEVICE* ptd,
1958 HDC hicTargetDevice,
1959 LOGPALETTE** ppColorSet)
1961 FIXME("stub\n");
1962 return E_NOTIMPL;
1965 static HRESULT WINAPI DataCache_Freeze(
1966 IViewObject2* iface,
1967 DWORD dwDrawAspect,
1968 LONG lindex,
1969 void* pvAspect,
1970 DWORD* pdwFreeze)
1972 FIXME("stub\n");
1973 return E_NOTIMPL;
1976 static HRESULT WINAPI DataCache_Unfreeze(
1977 IViewObject2* iface,
1978 DWORD dwFreeze)
1980 FIXME("stub\n");
1981 return E_NOTIMPL;
1984 /************************************************************************
1985 * DataCache_SetAdvise (IViewObject2)
1987 * This sets-up an advisory sink with the data cache. When the object's
1988 * view changes, this sink is called.
1990 static HRESULT WINAPI DataCache_SetAdvise(
1991 IViewObject2* iface,
1992 DWORD aspects,
1993 DWORD advf,
1994 IAdviseSink* pAdvSink)
1996 DataCache *this = impl_from_IViewObject2(iface);
1998 TRACE("(%p, %x, %x, %p)\n", iface, aspects, advf, pAdvSink);
2001 * A call to this function removes the previous sink
2003 if (this->sinkInterface != NULL)
2005 IAdviseSink_Release(this->sinkInterface);
2006 this->sinkInterface = NULL;
2007 this->sinkAspects = 0;
2008 this->sinkAdviseFlag = 0;
2012 * Now, setup the new one.
2014 if (pAdvSink!=NULL)
2016 this->sinkInterface = pAdvSink;
2017 this->sinkAspects = aspects;
2018 this->sinkAdviseFlag = advf;
2020 IAdviseSink_AddRef(this->sinkInterface);
2024 * When the ADVF_PRIMEFIRST flag is set, we have to advise the
2025 * sink immediately.
2027 if (advf & ADVF_PRIMEFIRST)
2029 DataCache_FireOnViewChange(this, aspects, -1);
2032 return S_OK;
2035 /************************************************************************
2036 * DataCache_GetAdvise (IViewObject2)
2038 * This method queries the current state of the advise sink
2039 * installed on the data cache.
2041 static HRESULT WINAPI DataCache_GetAdvise(
2042 IViewObject2* iface,
2043 DWORD* pAspects,
2044 DWORD* pAdvf,
2045 IAdviseSink** ppAdvSink)
2047 DataCache *this = impl_from_IViewObject2(iface);
2049 TRACE("(%p, %p, %p, %p)\n", iface, pAspects, pAdvf, ppAdvSink);
2052 * Just copy all the requested values.
2054 if (pAspects!=NULL)
2055 *pAspects = this->sinkAspects;
2057 if (pAdvf!=NULL)
2058 *pAdvf = this->sinkAdviseFlag;
2060 if (ppAdvSink!=NULL)
2062 if (this->sinkInterface != NULL)
2063 IAdviseSink_QueryInterface(this->sinkInterface,
2064 &IID_IAdviseSink,
2065 (void**)ppAdvSink);
2066 else *ppAdvSink = NULL;
2069 return S_OK;
2072 /************************************************************************
2073 * DataCache_GetExtent (IViewObject2)
2075 * This method retrieves the "natural" size of this cached object.
2077 static HRESULT WINAPI DataCache_GetExtent(
2078 IViewObject2* iface,
2079 DWORD dwDrawAspect,
2080 LONG lindex,
2081 DVTARGETDEVICE* ptd,
2082 LPSIZEL lpsizel)
2084 DataCache *This = impl_from_IViewObject2(iface);
2085 HRESULT hres = E_FAIL;
2086 DataCacheEntry *cache_entry;
2088 TRACE("(%p, %x, %d, %p, %p)\n",
2089 iface, dwDrawAspect, lindex, ptd, lpsizel);
2091 if (lpsizel==NULL)
2092 return E_POINTER;
2094 lpsizel->cx = 0;
2095 lpsizel->cy = 0;
2097 if (lindex!=-1)
2098 FIXME("Unimplemented flag lindex = %d\n", lindex);
2101 * Right now, we support only the callback from
2102 * the default handler.
2104 if (ptd!=NULL)
2105 FIXME("Unimplemented ptd = %p\n", ptd);
2107 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2109 /* FIXME: compare ptd too */
2110 if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
2111 (cache_entry->fmtetc.lindex != lindex))
2112 continue;
2114 /* if the data hasn't been loaded yet, do it now */
2115 if ((cache_entry->stgmedium.tymed == TYMED_NULL) && cache_entry->stream)
2117 hres = DataCacheEntry_LoadData(cache_entry);
2118 if (FAILED(hres))
2119 continue;
2122 /* no data */
2123 if (cache_entry->stgmedium.tymed == TYMED_NULL)
2124 continue;
2127 switch (cache_entry->fmtetc.cfFormat)
2129 case CF_METAFILEPICT:
2131 METAFILEPICT *mfpict;
2133 if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
2134 !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
2135 continue;
2137 lpsizel->cx = mfpict->xExt;
2138 lpsizel->cy = mfpict->yExt;
2140 GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
2142 return S_OK;
2144 case CF_DIB:
2146 BITMAPINFOHEADER *info;
2147 LONG x_pels_m, y_pels_m;
2150 if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
2151 !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
2152 continue;
2154 x_pels_m = info->biXPelsPerMeter;
2155 y_pels_m = info->biYPelsPerMeter;
2157 /* Size in units of 0.01mm (ie. MM_HIMETRIC) */
2158 if (x_pels_m != 0 && y_pels_m != 0)
2160 lpsizel->cx = info->biWidth * 100000 / x_pels_m;
2161 lpsizel->cy = info->biHeight * 100000 / y_pels_m;
2163 else
2165 HDC hdc = GetDC( 0 );
2166 lpsizel->cx = info->biWidth * 2540 / GetDeviceCaps( hdc, LOGPIXELSX );
2167 lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY );
2169 ReleaseDC( 0, hdc );
2172 GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
2174 return S_OK;
2179 WARN("no data could be found to get the extents from\n");
2182 * This method returns OLE_E_BLANK when it fails.
2184 return OLE_E_BLANK;
2188 /*********************************************************
2189 * Method implementation for the IOleCache2
2190 * part of the DataCache class.
2193 /************************************************************************
2194 * DataCache_IOleCache2_QueryInterface (IUnknown)
2196 static HRESULT WINAPI DataCache_IOleCache2_QueryInterface(
2197 IOleCache2* iface,
2198 REFIID riid,
2199 void** ppvObject)
2201 DataCache *this = impl_from_IOleCache2(iface);
2203 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2206 /************************************************************************
2207 * DataCache_IOleCache2_AddRef (IUnknown)
2209 static ULONG WINAPI DataCache_IOleCache2_AddRef(
2210 IOleCache2* iface)
2212 DataCache *this = impl_from_IOleCache2(iface);
2214 return IUnknown_AddRef(this->outer_unk);
2217 /************************************************************************
2218 * DataCache_IOleCache2_Release (IUnknown)
2220 static ULONG WINAPI DataCache_IOleCache2_Release(
2221 IOleCache2* iface)
2223 DataCache *this = impl_from_IOleCache2(iface);
2225 return IUnknown_Release(this->outer_unk);
2228 /*****************************************************************************
2229 * setup_sink
2231 * Set up the sink connection to the running object.
2233 static HRESULT setup_sink(DataCache *This, DataCacheEntry *cache_entry)
2235 HRESULT hr = S_FALSE;
2236 DWORD flags;
2238 /* Clear the ADVFCACHE_* bits. Native also sets the two highest bits for some reason. */
2239 flags = cache_entry->advise_flags & ~(ADVFCACHE_NOHANDLER | ADVFCACHE_FORCEBUILTIN | ADVFCACHE_ONSAVE);
2241 if(This->running_object)
2242 if(!(flags & ADVF_NODATA))
2243 hr = IDataObject_DAdvise(This->running_object, &cache_entry->fmtetc, flags,
2244 &This->IAdviseSink_iface, &cache_entry->sink_id);
2245 return hr;
2248 static HRESULT WINAPI DataCache_Cache(
2249 IOleCache2* iface,
2250 FORMATETC* pformatetc,
2251 DWORD advf,
2252 DWORD* pdwConnection)
2254 DataCache *This = impl_from_IOleCache2(iface);
2255 DataCacheEntry *cache_entry;
2256 HRESULT hr;
2257 FORMATETC fmt_cpy;
2259 TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection);
2261 if (!pformatetc || !pdwConnection)
2262 return E_INVALIDARG;
2264 TRACE("pformatetc = %s\n", debugstr_formatetc(pformatetc));
2266 fmt_cpy = *pformatetc; /* No need for a deep copy */
2267 if (fmt_cpy.cfFormat == CF_BITMAP && fmt_cpy.tymed == TYMED_GDI)
2269 fmt_cpy.cfFormat = CF_DIB;
2270 fmt_cpy.tymed = TYMED_HGLOBAL;
2273 /* View caching DVASPECT_ICON gets converted to CF_METAFILEPICT */
2274 if (fmt_cpy.dwAspect == DVASPECT_ICON && fmt_cpy.cfFormat == 0)
2276 fmt_cpy.cfFormat = CF_METAFILEPICT;
2277 fmt_cpy.tymed = TYMED_MFPICT;
2280 *pdwConnection = 0;
2282 cache_entry = DataCache_GetEntryForFormatEtc(This, &fmt_cpy);
2283 if (cache_entry)
2285 TRACE("found an existing cache entry\n");
2286 *pdwConnection = cache_entry->id;
2287 return CACHE_S_SAMECACHE;
2290 hr = DataCache_CreateEntry(This, &fmt_cpy, advf, FALSE, &cache_entry);
2292 if (SUCCEEDED(hr))
2294 *pdwConnection = cache_entry->id;
2295 setup_sink(This, cache_entry);
2298 return hr;
2301 static HRESULT WINAPI DataCache_Uncache(
2302 IOleCache2* iface,
2303 DWORD dwConnection)
2305 DataCache *This = impl_from_IOleCache2(iface);
2306 DataCacheEntry *cache_entry;
2308 TRACE("(%d)\n", dwConnection);
2310 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2311 if (cache_entry->id == dwConnection)
2313 DataCacheEntry_Destroy(This, cache_entry);
2314 return S_OK;
2317 WARN("no connection found for %d\n", dwConnection);
2319 return OLE_E_NOCONNECTION;
2322 static HRESULT WINAPI DataCache_EnumCache(IOleCache2 *iface,
2323 IEnumSTATDATA **enum_stat)
2325 DataCache *This = impl_from_IOleCache2( iface );
2326 DataCacheEntry *cache_entry;
2327 int i = 0, count = 0;
2328 STATDATA *data;
2329 HRESULT hr;
2331 TRACE( "(%p, %p)\n", This, enum_stat );
2333 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2335 count++;
2336 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2337 count++;
2340 data = CoTaskMemAlloc( count * sizeof(*data) );
2341 if (!data) return E_OUTOFMEMORY;
2343 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2345 if (i == count) goto fail;
2346 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2347 if (FAILED(hr)) goto fail;
2348 data[i].advf = cache_entry->advise_flags;
2349 data[i].pAdvSink = NULL;
2350 data[i].dwConnection = cache_entry->id;
2351 i++;
2353 if (cache_entry->fmtetc.cfFormat == CF_DIB)
2355 if (i == count) goto fail;
2356 hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2357 if (FAILED(hr)) goto fail;
2358 data[i].formatetc.cfFormat = CF_BITMAP;
2359 data[i].formatetc.tymed = TYMED_GDI;
2360 data[i].advf = cache_entry->advise_flags;
2361 data[i].pAdvSink = NULL;
2362 data[i].dwConnection = cache_entry->id;
2363 i++;
2367 hr = EnumSTATDATA_Construct( NULL, 0, i, data, FALSE, enum_stat );
2368 if (SUCCEEDED(hr)) return hr;
2370 fail:
2371 while (i--) CoTaskMemFree( data[i].formatetc.ptd );
2372 CoTaskMemFree( data );
2373 return hr;
2376 static HRESULT WINAPI DataCache_InitCache(
2377 IOleCache2* iface,
2378 IDataObject* pDataObject)
2380 FIXME("stub\n");
2381 return E_NOTIMPL;
2384 static HRESULT WINAPI DataCache_IOleCache2_SetData(
2385 IOleCache2* iface,
2386 FORMATETC* pformatetc,
2387 STGMEDIUM* pmedium,
2388 BOOL fRelease)
2390 DataCache *This = impl_from_IOleCache2(iface);
2391 DataCacheEntry *cache_entry;
2392 HRESULT hr;
2394 TRACE("(%p, %p, %s)\n", pformatetc, pmedium, fRelease ? "TRUE" : "FALSE");
2395 TRACE("formatetc = %s\n", debugstr_formatetc(pformatetc));
2397 cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetc);
2398 if (cache_entry)
2400 hr = DataCacheEntry_SetData(cache_entry, pformatetc, pmedium, fRelease);
2402 if (SUCCEEDED(hr))
2403 DataCache_FireOnViewChange(This, cache_entry->fmtetc.dwAspect,
2404 cache_entry->fmtetc.lindex);
2406 return hr;
2408 WARN("cache entry not found\n");
2410 return OLE_E_BLANK;
2413 static BOOL entry_updateable( DataCacheEntry *entry, DWORD mode )
2415 BOOL is_blank = entry->stgmedium.tymed == TYMED_NULL;
2417 if ((mode & UPDFCACHE_ONLYIFBLANK) && !is_blank) return FALSE;
2419 if ((mode & UPDFCACHE_NODATACACHE) && (entry->advise_flags & ADVF_NODATA)) return TRUE;
2420 if ((mode & UPDFCACHE_ONSAVECACHE) && (entry->advise_flags & ADVFCACHE_ONSAVE)) return TRUE;
2421 if ((mode & UPDFCACHE_ONSTOPCACHE) && (entry->advise_flags & ADVF_DATAONSTOP)) return TRUE;
2422 if ((mode & UPDFCACHE_NORMALCACHE) && (entry->advise_flags == 0)) return TRUE;
2423 if ((mode & UPDFCACHE_IFBLANK) && (is_blank && !(entry->advise_flags & ADVF_NODATA))) return TRUE;
2425 return FALSE;
2428 static HRESULT WINAPI DataCache_UpdateCache( IOleCache2 *iface, IDataObject *data,
2429 DWORD mode, void *reserved )
2431 DataCache *This = impl_from_IOleCache2(iface);
2432 DataCacheEntry *cache_entry;
2433 STGMEDIUM med;
2434 HRESULT hr = S_OK;
2435 CLIPFORMAT view_list[] = { CF_METAFILEPICT, CF_ENHMETAFILE, CF_DIB, CF_BITMAP };
2436 FORMATETC fmt;
2437 int i, slots = 0;
2438 BOOL done_one = FALSE;
2440 TRACE( "(%p %p %08x %p)\n", iface, data, mode, reserved );
2442 LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2444 slots++;
2446 if (!entry_updateable( cache_entry, mode ))
2448 done_one = TRUE;
2449 continue;
2452 fmt = cache_entry->fmtetc;
2454 if (fmt.cfFormat)
2456 hr = IDataObject_GetData( data, &fmt, &med );
2457 if (hr != S_OK && fmt.cfFormat == CF_DIB)
2459 fmt.cfFormat = CF_BITMAP;
2460 fmt.tymed = TYMED_GDI;
2461 hr = IDataObject_GetData( data, &fmt, &med );
2463 if (hr != S_OK && fmt.cfFormat == CF_ENHMETAFILE)
2465 fmt.cfFormat = CF_METAFILEPICT;
2466 fmt.tymed = TYMED_MFPICT;
2467 hr = IDataObject_GetData( data, &fmt, &med );
2469 if (hr == S_OK)
2471 hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2472 if (hr != S_OK) ReleaseStgMedium( &med );
2473 else done_one = TRUE;
2476 else
2478 for (i = 0; i < sizeof(view_list) / sizeof(view_list[0]); i++)
2480 fmt.cfFormat = view_list[i];
2481 fmt.tymed = tymed_from_cf( fmt.cfFormat );
2482 hr = IDataObject_QueryGetData( data, &fmt );
2483 if (hr == S_OK)
2485 hr = IDataObject_GetData( data, &fmt, &med );
2486 if (hr == S_OK)
2488 if (fmt.cfFormat == CF_BITMAP)
2490 cache_entry->fmtetc.cfFormat = CF_DIB;
2491 cache_entry->fmtetc.tymed = TYMED_HGLOBAL;
2493 else
2495 cache_entry->fmtetc.cfFormat = fmt.cfFormat;
2496 cache_entry->fmtetc.tymed = fmt.tymed;
2498 hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2499 if (hr != S_OK) ReleaseStgMedium( &med );
2500 else done_one = TRUE;
2501 break;
2508 return (!slots || done_one) ? S_OK : CACHE_E_NOCACHE_UPDATED;
2511 static HRESULT WINAPI DataCache_DiscardCache(
2512 IOleCache2* iface,
2513 DWORD dwDiscardOptions)
2515 DataCache *This = impl_from_IOleCache2(iface);
2516 DataCacheEntry *cache_entry;
2517 HRESULT hr = S_OK;
2519 TRACE("(%d)\n", dwDiscardOptions);
2521 if (dwDiscardOptions == DISCARDCACHE_SAVEIFDIRTY)
2522 hr = DataCache_Save(&This->IPersistStorage_iface, This->presentationStorage, TRUE);
2524 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2526 hr = DataCacheEntry_DiscardData(cache_entry);
2527 if (FAILED(hr))
2528 break;
2531 return hr;
2535 /*********************************************************
2536 * Method implementation for the IOleCacheControl
2537 * part of the DataCache class.
2540 /************************************************************************
2541 * DataCache_IOleCacheControl_QueryInterface (IUnknown)
2543 static HRESULT WINAPI DataCache_IOleCacheControl_QueryInterface(
2544 IOleCacheControl* iface,
2545 REFIID riid,
2546 void** ppvObject)
2548 DataCache *this = impl_from_IOleCacheControl(iface);
2550 return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2553 /************************************************************************
2554 * DataCache_IOleCacheControl_AddRef (IUnknown)
2556 static ULONG WINAPI DataCache_IOleCacheControl_AddRef(
2557 IOleCacheControl* iface)
2559 DataCache *this = impl_from_IOleCacheControl(iface);
2561 return IUnknown_AddRef(this->outer_unk);
2564 /************************************************************************
2565 * DataCache_IOleCacheControl_Release (IUnknown)
2567 static ULONG WINAPI DataCache_IOleCacheControl_Release(
2568 IOleCacheControl* iface)
2570 DataCache *this = impl_from_IOleCacheControl(iface);
2572 return IUnknown_Release(this->outer_unk);
2575 /************************************************************************
2576 * DataCache_OnRun (IOleCacheControl)
2578 static HRESULT WINAPI DataCache_OnRun(IOleCacheControl* iface, IDataObject *data_obj)
2580 DataCache *This = impl_from_IOleCacheControl(iface);
2581 DataCacheEntry *cache_entry;
2583 TRACE("(%p)->(%p)\n", iface, data_obj);
2585 if(This->running_object) return S_OK;
2587 /* No reference is taken on the data object */
2588 This->running_object = data_obj;
2590 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2592 setup_sink(This, cache_entry);
2595 return S_OK;
2598 /************************************************************************
2599 * DataCache_OnStop (IOleCacheControl)
2601 static HRESULT WINAPI DataCache_OnStop(IOleCacheControl* iface)
2603 DataCache *This = impl_from_IOleCacheControl(iface);
2604 DataCacheEntry *cache_entry;
2606 TRACE("(%p)\n", iface);
2608 if(!This->running_object) return S_OK;
2610 LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2612 if(cache_entry->sink_id)
2614 IDataObject_DUnadvise(This->running_object, cache_entry->sink_id);
2615 cache_entry->sink_id = 0;
2619 /* No ref taken in OnRun, so no Release call here */
2620 This->running_object = NULL;
2621 return S_OK;
2624 /************************************************************************
2625 * IAdviseSink methods.
2626 * This behaves as an internal object to the data cache. QI'ing its ptr doesn't
2627 * give access to the cache's other interfaces. We don't maintain a ref count,
2628 * the object exists as long as the cache is around.
2630 static HRESULT WINAPI DataCache_IAdviseSink_QueryInterface(IAdviseSink *iface, REFIID iid, void **obj)
2632 *obj = NULL;
2633 if (IsEqualIID(&IID_IUnknown, iid) ||
2634 IsEqualIID(&IID_IAdviseSink, iid))
2636 *obj = iface;
2639 if(*obj)
2641 IAdviseSink_AddRef(iface);
2642 return S_OK;
2644 return E_NOINTERFACE;
2647 static ULONG WINAPI DataCache_IAdviseSink_AddRef(IAdviseSink *iface)
2649 return 2;
2652 static ULONG WINAPI DataCache_IAdviseSink_Release(IAdviseSink *iface)
2654 return 1;
2657 static void WINAPI DataCache_OnDataChange(IAdviseSink *iface, FORMATETC *fmt, STGMEDIUM *med)
2659 DataCache *This = impl_from_IAdviseSink(iface);
2660 TRACE("(%p)->(%s, %p)\n", This, debugstr_formatetc(fmt), med);
2661 IOleCache2_SetData(&This->IOleCache2_iface, fmt, med, FALSE);
2664 static void WINAPI DataCache_OnViewChange(IAdviseSink *iface, DWORD aspect, LONG index)
2666 FIXME("stub\n");
2669 static void WINAPI DataCache_OnRename(IAdviseSink *iface, IMoniker *mk)
2671 FIXME("stub\n");
2674 static void WINAPI DataCache_OnSave(IAdviseSink *iface)
2676 FIXME("stub\n");
2679 static void WINAPI DataCache_OnClose(IAdviseSink *iface)
2681 FIXME("stub\n");
2685 * Virtual function tables for the DataCache class.
2687 static const IUnknownVtbl DataCache_NDIUnknown_VTable =
2689 DataCache_NDIUnknown_QueryInterface,
2690 DataCache_NDIUnknown_AddRef,
2691 DataCache_NDIUnknown_Release
2694 static const IDataObjectVtbl DataCache_IDataObject_VTable =
2696 DataCache_IDataObject_QueryInterface,
2697 DataCache_IDataObject_AddRef,
2698 DataCache_IDataObject_Release,
2699 DataCache_GetData,
2700 DataCache_GetDataHere,
2701 DataCache_QueryGetData,
2702 DataCache_GetCanonicalFormatEtc,
2703 DataCache_IDataObject_SetData,
2704 DataCache_EnumFormatEtc,
2705 DataCache_DAdvise,
2706 DataCache_DUnadvise,
2707 DataCache_EnumDAdvise
2710 static const IPersistStorageVtbl DataCache_IPersistStorage_VTable =
2712 DataCache_IPersistStorage_QueryInterface,
2713 DataCache_IPersistStorage_AddRef,
2714 DataCache_IPersistStorage_Release,
2715 DataCache_GetClassID,
2716 DataCache_IsDirty,
2717 DataCache_InitNew,
2718 DataCache_Load,
2719 DataCache_Save,
2720 DataCache_SaveCompleted,
2721 DataCache_HandsOffStorage
2724 static const IViewObject2Vtbl DataCache_IViewObject2_VTable =
2726 DataCache_IViewObject2_QueryInterface,
2727 DataCache_IViewObject2_AddRef,
2728 DataCache_IViewObject2_Release,
2729 DataCache_Draw,
2730 DataCache_GetColorSet,
2731 DataCache_Freeze,
2732 DataCache_Unfreeze,
2733 DataCache_SetAdvise,
2734 DataCache_GetAdvise,
2735 DataCache_GetExtent
2738 static const IOleCache2Vtbl DataCache_IOleCache2_VTable =
2740 DataCache_IOleCache2_QueryInterface,
2741 DataCache_IOleCache2_AddRef,
2742 DataCache_IOleCache2_Release,
2743 DataCache_Cache,
2744 DataCache_Uncache,
2745 DataCache_EnumCache,
2746 DataCache_InitCache,
2747 DataCache_IOleCache2_SetData,
2748 DataCache_UpdateCache,
2749 DataCache_DiscardCache
2752 static const IOleCacheControlVtbl DataCache_IOleCacheControl_VTable =
2754 DataCache_IOleCacheControl_QueryInterface,
2755 DataCache_IOleCacheControl_AddRef,
2756 DataCache_IOleCacheControl_Release,
2757 DataCache_OnRun,
2758 DataCache_OnStop
2761 static const IAdviseSinkVtbl DataCache_IAdviseSink_VTable =
2763 DataCache_IAdviseSink_QueryInterface,
2764 DataCache_IAdviseSink_AddRef,
2765 DataCache_IAdviseSink_Release,
2766 DataCache_OnDataChange,
2767 DataCache_OnViewChange,
2768 DataCache_OnRename,
2769 DataCache_OnSave,
2770 DataCache_OnClose
2773 /*********************************************************
2774 * Method implementation for DataCache class.
2776 static DataCache* DataCache_Construct(
2777 REFCLSID clsid,
2778 LPUNKNOWN pUnkOuter)
2780 DataCache* newObject = 0;
2783 * Allocate space for the object.
2785 newObject = HeapAlloc(GetProcessHeap(), 0, sizeof(DataCache));
2787 if (newObject==0)
2788 return newObject;
2791 * Initialize the virtual function table.
2793 newObject->IDataObject_iface.lpVtbl = &DataCache_IDataObject_VTable;
2794 newObject->IUnknown_inner.lpVtbl = &DataCache_NDIUnknown_VTable;
2795 newObject->IPersistStorage_iface.lpVtbl = &DataCache_IPersistStorage_VTable;
2796 newObject->IViewObject2_iface.lpVtbl = &DataCache_IViewObject2_VTable;
2797 newObject->IOleCache2_iface.lpVtbl = &DataCache_IOleCache2_VTable;
2798 newObject->IOleCacheControl_iface.lpVtbl = &DataCache_IOleCacheControl_VTable;
2799 newObject->IAdviseSink_iface.lpVtbl = &DataCache_IAdviseSink_VTable;
2800 newObject->outer_unk = pUnkOuter ? pUnkOuter : &newObject->IUnknown_inner;
2801 newObject->ref = 1;
2804 * Initialize the other members of the structure.
2806 newObject->sinkAspects = 0;
2807 newObject->sinkAdviseFlag = 0;
2808 newObject->sinkInterface = 0;
2809 newObject->clsid = CLSID_NULL;
2810 newObject->presentationStorage = NULL;
2811 list_init(&newObject->cache_list);
2812 newObject->last_cache_id = 2;
2813 newObject->dirty = FALSE;
2814 newObject->running_object = NULL;
2816 create_automatic_entry( newObject, clsid );
2817 newObject->clsid = *clsid;
2819 return newObject;
2822 /******************************************************************************
2823 * CreateDataCache [OLE32.@]
2825 * Creates a data cache to allow an object to render one or more of its views,
2826 * whether running or not.
2828 * PARAMS
2829 * pUnkOuter [I] Outer unknown for the object.
2830 * rclsid [I]
2831 * riid [I] IID of interface to return.
2832 * ppvObj [O] Address where the data cache object will be stored on return.
2834 * RETURNS
2835 * Success: S_OK.
2836 * Failure: HRESULT code.
2838 * NOTES
2839 * The following interfaces are supported by the returned data cache object:
2840 * IOleCache, IOleCache2, IOleCacheControl, IPersistStorage, IDataObject,
2841 * IViewObject and IViewObject2.
2843 HRESULT WINAPI CreateDataCache(
2844 LPUNKNOWN pUnkOuter,
2845 REFCLSID rclsid,
2846 REFIID riid,
2847 LPVOID* ppvObj)
2849 DataCache* newCache = NULL;
2850 HRESULT hr = S_OK;
2852 TRACE("(%s, %p, %s, %p)\n", debugstr_guid(rclsid), pUnkOuter, debugstr_guid(riid), ppvObj);
2855 * Sanity check
2857 if (ppvObj==0)
2858 return E_POINTER;
2860 *ppvObj = 0;
2863 * If this cache is constructed for aggregation, make sure
2864 * the caller is requesting the IUnknown interface.
2865 * This is necessary because it's the only time the non-delegating
2866 * IUnknown pointer can be returned to the outside.
2868 if ( pUnkOuter && !IsEqualIID(&IID_IUnknown, riid) )
2869 return E_INVALIDARG;
2872 * Try to construct a new instance of the class.
2874 newCache = DataCache_Construct(rclsid,
2875 pUnkOuter);
2877 if (newCache == 0)
2878 return E_OUTOFMEMORY;
2880 hr = IUnknown_QueryInterface(&newCache->IUnknown_inner, riid, ppvObj);
2881 IUnknown_Release(&newCache->IUnknown_inner);
2883 return hr;