xref: /reactos/dll/win32/ole32/datacache.c (revision ebaf247c)
1 /*
2  *	OLE 2 Data cache
3  *
4  *      Copyright 1999  Francis Beaudet
5  *      Copyright 2000  Abey George
6  *
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.
11  *
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.
16  *
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
20  *
21  * NOTES:
22  *    The OLE2 data cache supports a whole whack of
23  *    interfaces including:
24  *       IDataObject, IPersistStorage, IViewObject2,
25  *       IOleCache2 and IOleCacheControl.
26  *
27  *    Most of the implementation details are taken from: Inside OLE
28  *    second edition by Kraig Brockschmidt,
29  *
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.
45  */
46 
47 #include <stdarg.h>
48 #include <string.h>
49 
50 #define COBJMACROS
51 #define NONAMELESSUNION
52 
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/list.h"
61 #include "wine/debug.h"
62 
63 WINE_DEFAULT_DEBUG_CHANNEL(ole);
64 
65 /****************************************************************************
66  * PresentationDataHeader
67  *
68  * This structure represents the header of the \002OlePresXXX stream in
69  * the OLE object storage.
70  */
71 typedef struct PresentationDataHeader
72 {
73   /* clipformat:
74    *  - standard clipformat:
75    *  DWORD length = 0xffffffff;
76    *  DWORD cfFormat;
77    *  - or custom clipformat:
78    *  DWORD length;
79    *  CHAR format_name[length]; (null-terminated)
80    */
81   DWORD tdSize; /* This is actually a truncated DVTARGETDEVICE, if tdSize > sizeof(DWORD)
82                    then there are tdSize - sizeof(DWORD) more bytes before dvAspect */
83   DVASPECT dvAspect;
84   DWORD lindex;
85   DWORD advf;
86   DWORD unknown7;	/* 0 */
87   DWORD dwObjectExtentX;
88   DWORD dwObjectExtentY;
89   DWORD dwSize;
90 } PresentationDataHeader;
91 
92 #define STREAM_NUMBER_NOT_SET -2
93 #define STREAM_NUMBER_CONTENTS -1 /* CONTENTS stream */
94 
95 typedef struct DataCacheEntry
96 {
97   struct list entry;
98   /* format of this entry */
99   FORMATETC fmtetc;
100   /* cached data */
101   STGMEDIUM stgmedium;
102   /* connection ID */
103   DWORD id;
104   /* dirty flag */
105   BOOL dirty;
106   /* stream number that the entry was loaded from.
107      This is used to defer loading until the data is actually needed. */
108   int load_stream_num;
109   /* stream number that the entry will be saved to.
110      This may differ from above if cache entries have been Uncache()d for example. */
111   int save_stream_num;
112   /* sink id set when object is running */
113   DWORD sink_id;
114   /* Advise sink flags */
115   DWORD advise_flags;
116 } DataCacheEntry;
117 
118 /****************************************************************************
119  * DataCache
120  */
121 struct DataCache
122 {
123   /*
124    * List all interface here
125    */
126   IUnknown          IUnknown_inner;
127   IDataObject       IDataObject_iface;
128   IPersistStorage   IPersistStorage_iface;
129   IViewObject2      IViewObject2_iface;
130   IOleCache2        IOleCache2_iface;
131   IOleCacheControl  IOleCacheControl_iface;
132 
133   /* The sink that is connected to a remote object.
134      The other interfaces are not available by QI'ing the sink and vice-versa */
135   IAdviseSink       IAdviseSink_iface;
136 
137   /*
138    * Reference count of this object
139    */
140   LONG ref;
141 
142   /*
143    * IUnknown implementation of the outer object.
144    */
145   IUnknown *outer_unk;
146 
147   /*
148    * The user of this object can setup ONE advise sink
149    * connection with the object. These parameters describe
150    * that connection.
151    */
152   DWORD        sinkAspects;
153   DWORD        sinkAdviseFlag;
154   IAdviseSink *sinkInterface;
155 
156   CLSID clsid;
157   /* Is the clsid one of the CLSID_Picture classes */
158   BOOL clsid_static;
159 
160   IStorage *presentationStorage;
161 
162   /* list of cache entries */
163   struct list cache_list;
164   /* last id assigned to an entry */
165   DWORD last_cache_id;
166   /* dirty flag */
167   BOOL dirty;
168   /* running object set by OnRun */
169   IDataObject *running_object;
170 };
171 
172 typedef struct DataCache DataCache;
173 
174 /*
175  * Here, I define utility macros to help with the casting of the
176  * "this" parameter.
177  * There is a version to accommodate all of the VTables implemented
178  * by this object.
179  */
180 
181 static inline DataCache *impl_from_IDataObject( IDataObject *iface )
182 {
183     return CONTAINING_RECORD(iface, DataCache, IDataObject_iface);
184 }
185 
186 static inline DataCache *impl_from_IUnknown( IUnknown *iface )
187 {
188     return CONTAINING_RECORD(iface, DataCache, IUnknown_inner);
189 }
190 
191 static inline DataCache *impl_from_IPersistStorage( IPersistStorage *iface )
192 {
193     return CONTAINING_RECORD(iface, DataCache, IPersistStorage_iface);
194 }
195 
196 static inline DataCache *impl_from_IViewObject2( IViewObject2 *iface )
197 {
198     return CONTAINING_RECORD(iface, DataCache, IViewObject2_iface);
199 }
200 
201 static inline DataCache *impl_from_IOleCache2( IOleCache2 *iface )
202 {
203     return CONTAINING_RECORD(iface, DataCache, IOleCache2_iface);
204 }
205 
206 static inline DataCache *impl_from_IOleCacheControl( IOleCacheControl *iface )
207 {
208     return CONTAINING_RECORD(iface, DataCache, IOleCacheControl_iface);
209 }
210 
211 static inline DataCache *impl_from_IAdviseSink( IAdviseSink *iface )
212 {
213     return CONTAINING_RECORD(iface, DataCache, IAdviseSink_iface);
214 }
215 
216 const char *debugstr_formatetc(const FORMATETC *formatetc)
217 {
218     return wine_dbg_sprintf("{ cfFormat = 0x%x, ptd = %p, dwAspect = %d, lindex = %d, tymed = %d }",
219         formatetc->cfFormat, formatetc->ptd, formatetc->dwAspect,
220         formatetc->lindex, formatetc->tymed);
221 }
222 
223 /***********************************************************************
224  *           bitmap_info_size
225  *
226  * Return the size of the bitmap info structure including color table.
227  */
228 static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
229 {
230     unsigned int colors, size, masks = 0;
231 
232     if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
233     {
234         const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
235         colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
236         return sizeof(BITMAPCOREHEADER) + colors *
237             ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
238     }
239     else  /* assume BITMAPINFOHEADER */
240     {
241         colors = info->bmiHeader.biClrUsed;
242         if (colors > 256) /* buffer overflow otherwise */
243             colors = 256;
244         if (!colors && (info->bmiHeader.biBitCount <= 8))
245             colors = 1 << info->bmiHeader.biBitCount;
246         if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
247         size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
248         return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
249     }
250 }
251 
252 static void DataCacheEntry_Destroy(DataCache *cache, DataCacheEntry *cache_entry)
253 {
254     list_remove(&cache_entry->entry);
255     CoTaskMemFree(cache_entry->fmtetc.ptd);
256     ReleaseStgMedium(&cache_entry->stgmedium);
257     if(cache_entry->sink_id)
258         IDataObject_DUnadvise(cache->running_object, cache_entry->sink_id);
259 
260     HeapFree(GetProcessHeap(), 0, cache_entry);
261 }
262 
263 static void DataCache_Destroy(
264   DataCache* ptrToDestroy)
265 {
266   DataCacheEntry *cache_entry, *next_cache_entry;
267 
268   TRACE("()\n");
269 
270   if (ptrToDestroy->sinkInterface != NULL)
271   {
272     IAdviseSink_Release(ptrToDestroy->sinkInterface);
273     ptrToDestroy->sinkInterface = NULL;
274   }
275 
276   LIST_FOR_EACH_ENTRY_SAFE(cache_entry, next_cache_entry, &ptrToDestroy->cache_list, DataCacheEntry, entry)
277     DataCacheEntry_Destroy(ptrToDestroy, cache_entry);
278 
279   if (ptrToDestroy->presentationStorage != NULL)
280   {
281     IStorage_Release(ptrToDestroy->presentationStorage);
282     ptrToDestroy->presentationStorage = NULL;
283   }
284 
285   /*
286    * Free the datacache pointer.
287    */
288   HeapFree(GetProcessHeap(), 0, ptrToDestroy);
289 }
290 
291 static DataCacheEntry *DataCache_GetEntryForFormatEtc(DataCache *This, const FORMATETC *formatetc)
292 {
293     DataCacheEntry *cache_entry;
294     FORMATETC fmt = *formatetc;
295 
296     if (fmt.cfFormat == CF_BITMAP)
297     {
298         fmt.cfFormat = CF_DIB;
299         fmt.tymed = TYMED_HGLOBAL;
300     }
301 
302     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
303     {
304         /* FIXME: also compare DVTARGETDEVICEs */
305         if ((fmt.cfFormat == cache_entry->fmtetc.cfFormat) &&
306             (fmt.dwAspect == cache_entry->fmtetc.dwAspect) &&
307             (fmt.lindex == cache_entry->fmtetc.lindex) &&
308             ((fmt.tymed == cache_entry->fmtetc.tymed) || !cache_entry->fmtetc.cfFormat)) /* tymed is ignored for view caching */
309             return cache_entry;
310     }
311     return NULL;
312 }
313 
314 /* Returns the cache entry associated with a static CLSID.
315    This will be first in the list with connection id == 1 */
316 static HRESULT get_static_entry( DataCache *cache, DataCacheEntry **cache_entry )
317 {
318     DataCacheEntry *entry;
319     struct list *head = list_head( &cache->cache_list );
320     HRESULT hr = E_FAIL;
321 
322     *cache_entry = NULL;
323 
324     if (head)
325     {
326         entry = LIST_ENTRY( head, DataCacheEntry, entry );
327         if (entry->id == 1)
328         {
329             *cache_entry = entry;
330             hr = S_OK;
331         }
332     }
333 
334     return hr;
335 }
336 
337 /* checks that the clipformat and tymed are valid and returns an error if they
338 * aren't and CACHE_S_NOTSUPPORTED if they are valid, but can't be rendered by
339 * DataCache_Draw */
340 static HRESULT check_valid_formatetc( const FORMATETC *fmt )
341 {
342     /* DVASPECT_ICON must be CF_METAFILEPICT */
343     if (fmt->dwAspect == DVASPECT_ICON && fmt->cfFormat != CF_METAFILEPICT)
344         return DV_E_FORMATETC;
345 
346     if (!fmt->cfFormat ||
347         (fmt->cfFormat == CF_METAFILEPICT && fmt->tymed == TYMED_MFPICT) ||
348         (fmt->cfFormat == CF_BITMAP && fmt->tymed == TYMED_GDI) ||
349         (fmt->cfFormat == CF_DIB && fmt->tymed == TYMED_HGLOBAL) ||
350         (fmt->cfFormat == CF_ENHMETAFILE && fmt->tymed == TYMED_ENHMF))
351         return S_OK;
352     else if (fmt->tymed == TYMED_HGLOBAL)
353         return CACHE_S_FORMATETC_NOTSUPPORTED;
354     else
355     {
356         WARN("invalid clipformat/tymed combination: %d/%d\n", fmt->cfFormat, fmt->tymed);
357         return DV_E_TYMED;
358     }
359 }
360 
361 static BOOL init_cache_entry(DataCacheEntry *entry, const FORMATETC *fmt, DWORD advf,
362                              DWORD id)
363 {
364     HRESULT hr;
365 
366     hr = copy_formatetc(&entry->fmtetc, fmt);
367     if (FAILED(hr)) return FALSE;
368 
369     entry->stgmedium.tymed = TYMED_NULL;
370     entry->stgmedium.pUnkForRelease = NULL;
371     entry->id = id;
372     entry->dirty = TRUE;
373     entry->load_stream_num = STREAM_NUMBER_NOT_SET;
374     entry->save_stream_num = STREAM_NUMBER_NOT_SET;
375     entry->sink_id = 0;
376     entry->advise_flags = advf;
377 
378     return TRUE;
379 }
380 
381 static HRESULT DataCache_CreateEntry(DataCache *This, const FORMATETC *formatetc, DWORD advf,
382                                      BOOL automatic, DataCacheEntry **cache_entry)
383 {
384     HRESULT hr;
385     DWORD id = automatic ? 1 : This->last_cache_id;
386     DataCacheEntry *entry;
387 
388     hr = check_valid_formatetc( formatetc );
389     if (FAILED(hr))
390         return hr;
391     if (hr == CACHE_S_FORMATETC_NOTSUPPORTED)
392         TRACE("creating unsupported format %d\n", formatetc->cfFormat);
393 
394     entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
395     if (!entry)
396         return E_OUTOFMEMORY;
397 
398     if (!init_cache_entry(entry, formatetc, advf, id))
399         goto fail;
400 
401     if (automatic)
402         list_add_head(&This->cache_list, &entry->entry);
403     else
404     {
405         list_add_tail(&This->cache_list, &entry->entry);
406         This->last_cache_id++;
407     }
408 
409     if (cache_entry) *cache_entry = entry;
410     return hr;
411 
412 fail:
413     HeapFree(GetProcessHeap(), 0, entry);
414     return E_OUTOFMEMORY;
415 }
416 
417 /************************************************************************
418  * DataCache_FireOnViewChange
419  *
420  * This method will fire an OnViewChange notification to the advise
421  * sink registered with the datacache.
422  *
423  * See IAdviseSink::OnViewChange for more details.
424  */
425 static void DataCache_FireOnViewChange(
426   DataCache* this,
427   DWORD      aspect,
428   LONG       lindex)
429 {
430   TRACE("(%p, %x, %d)\n", this, aspect, lindex);
431 
432   /*
433    * The sink supplies a filter when it registers
434    * we make sure we only send the notifications when that
435    * filter matches.
436    */
437   if ((this->sinkAspects & aspect) != 0)
438   {
439     if (this->sinkInterface != NULL)
440     {
441       IAdviseSink_OnViewChange(this->sinkInterface,
442 			       aspect,
443 			       lindex);
444 
445       /*
446        * Some sinks want to be unregistered automatically when
447        * the first notification goes out.
448        */
449       if ( (this->sinkAdviseFlag & ADVF_ONLYONCE) != 0)
450       {
451 	IAdviseSink_Release(this->sinkInterface);
452 
453 	this->sinkInterface  = NULL;
454 	this->sinkAspects    = 0;
455 	this->sinkAdviseFlag = 0;
456       }
457     }
458   }
459 }
460 
461 static HRESULT read_clipformat(IStream *stream, CLIPFORMAT *clipformat)
462 {
463     DWORD length;
464     HRESULT hr;
465     ULONG read;
466 
467     *clipformat = 0;
468 
469     hr = IStream_Read(stream, &length, sizeof(length), &read);
470     if (hr != S_OK || read != sizeof(length))
471         return DV_E_CLIPFORMAT;
472     if (!length) {
473         /* No clipboard format present */
474         return S_OK;
475     }
476     if (length == -1)
477     {
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;
483     }
484     else
485     {
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')
491         {
492             HeapFree(GetProcessHeap(), 0, format_name);
493             return DV_E_CLIPFORMAT;
494         }
495         *clipformat = RegisterClipboardFormatA(format_name);
496         HeapFree(GetProcessHeap(), 0, format_name);
497     }
498     return S_OK;
499 }
500 
501 static HRESULT write_clipformat(IStream *stream, CLIPFORMAT clipformat)
502 {
503     DWORD length;
504     HRESULT hr;
505     char format_name[256];
506 
507     if (clipformat == 0)
508         length = 0;
509     else if (clipformat < 0xc000)
510         length = -1;
511     else
512     {
513         length = GetClipboardFormatNameA(clipformat, format_name, sizeof(format_name));
514         /* If there is a clipboard format name, we need to include its terminating \0 */
515         if (length) length++;
516     }
517     hr = IStream_Write(stream, &length, sizeof(length), NULL);
518     if (FAILED(hr) || clipformat == 0)
519         return hr;
520 
521     if (clipformat < 0xc000)
522     {
523         DWORD cf = clipformat;
524         hr = IStream_Write(stream, &cf, sizeof(cf), NULL);
525     }
526     else
527     {
528         hr = IStream_Write(stream, format_name, length, NULL);
529     }
530     return hr;
531 }
532 
533 static const WCHAR CONTENTS[] = {'C','O','N','T','E','N','T','S',0};
534 
535 static HRESULT open_pres_stream( IStorage *stg, int stream_number, IStream **stm )
536 {
537     WCHAR pres[] = {2,'O','l','e','P','r','e','s',
538                     '0' + (stream_number / 100) % 10,
539                     '0' + (stream_number / 10) % 10,
540                     '0' + stream_number % 10, 0};
541     const WCHAR *name = pres;
542 
543     if (stream_number == STREAM_NUMBER_NOT_SET) return E_FAIL;
544     if (stream_number == STREAM_NUMBER_CONTENTS) name = CONTENTS;
545 
546     return IStorage_OpenStream( stg, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
547 }
548 
549 static HRESULT synthesize_emf( HMETAFILEPICT data, STGMEDIUM *med )
550 {
551     METAFILEPICT *pict;
552     HRESULT hr = E_FAIL;
553     UINT size;
554     void *bits;
555 
556     if (!(pict = GlobalLock( data ))) return hr;
557 
558     size = GetMetaFileBitsEx( pict->hMF, 0, NULL );
559     if ((bits = HeapAlloc( GetProcessHeap(), 0, size )))
560     {
561         GetMetaFileBitsEx( pict->hMF, size, bits );
562         med->u.hEnhMetaFile = SetWinMetaFileBits( size, bits, NULL, pict );
563         HeapFree( GetProcessHeap(), 0, bits );
564         med->tymed = TYMED_ENHMF;
565         med->pUnkForRelease = NULL;
566         hr = S_OK;
567     }
568 
569     GlobalUnlock( data );
570     return hr;
571 }
572 #include <pshpack2.h>
573 struct meta_placeable
574 {
575     DWORD key;
576     WORD hwmf;
577     WORD bounding_box[4];
578     WORD inch;
579     DWORD reserved;
580     WORD checksum;
581 };
582 #include <poppack.h>
583 
584 static HRESULT load_mf_pict( DataCacheEntry *cache_entry, IStream *stm )
585 {
586     HRESULT hr;
587     STATSTG stat;
588     ULARGE_INTEGER current_pos;
589     void *bits;
590     METAFILEPICT *mfpict;
591     HGLOBAL hmfpict;
592     PresentationDataHeader header;
593     CLIPFORMAT clipformat;
594     static const LARGE_INTEGER offset_zero;
595     ULONG read;
596     struct meta_placeable mf_place;
597 
598     hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
599     if (FAILED( hr )) return hr;
600 
601     if (cache_entry->load_stream_num != STREAM_NUMBER_CONTENTS)
602     {
603         hr = read_clipformat( stm, &clipformat );
604         if (hr != S_OK) return hr;
605         hr = IStream_Read( stm, &header, sizeof(header), &read );
606         if (hr != S_OK) return hr;
607     }
608     else
609     {
610         hr = IStream_Read( stm, &mf_place, sizeof(mf_place), &read );
611         if (hr != S_OK) return hr;
612     }
613 
614     hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, &current_pos );
615     if (FAILED( hr )) return hr;
616     stat.cbSize.QuadPart -= current_pos.QuadPart;
617 
618     hmfpict = GlobalAlloc( GMEM_MOVEABLE, sizeof(METAFILEPICT) );
619     if (!hmfpict) return E_OUTOFMEMORY;
620     mfpict = GlobalLock( hmfpict );
621 
622     bits = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart);
623     if (!bits)
624     {
625         GlobalFree( hmfpict );
626         return E_OUTOFMEMORY;
627     }
628 
629     hr = IStream_Read( stm, bits, stat.cbSize.u.LowPart, &read );
630 
631     if (SUCCEEDED( hr ))
632     {
633         mfpict->mm = MM_ANISOTROPIC;
634         /* FIXME: get this from the stream */
635         if (cache_entry->load_stream_num != STREAM_NUMBER_CONTENTS)
636         {
637             mfpict->xExt = header.dwObjectExtentX;
638             mfpict->yExt = header.dwObjectExtentY;
639         }
640         else
641         {
642             mfpict->xExt = ((mf_place.bounding_box[2] - mf_place.bounding_box[0])
643                             * 2540) / mf_place.inch;
644             mfpict->yExt = ((mf_place.bounding_box[3] - mf_place.bounding_box[1])
645                             * 2540) / mf_place.inch;
646         }
647         mfpict->hMF = SetMetaFileBitsEx( stat.cbSize.u.LowPart, bits );
648         if (!mfpict->hMF)
649             hr = E_FAIL;
650     }
651 
652     GlobalUnlock( hmfpict );
653     if (SUCCEEDED( hr ))
654     {
655         cache_entry->stgmedium.tymed = TYMED_MFPICT;
656         cache_entry->stgmedium.u.hMetaFilePict = hmfpict;
657     }
658     else
659         GlobalFree( hmfpict );
660 
661     HeapFree( GetProcessHeap(), 0, bits );
662 
663     return hr;
664 }
665 
666 static HRESULT load_dib( DataCacheEntry *cache_entry, IStream *stm )
667 {
668     HRESULT hr;
669     STATSTG stat;
670     BYTE *dib;
671     HGLOBAL hglobal;
672     ULONG read, info_size, bi_size;
673     BITMAPFILEHEADER file;
674     BITMAPINFOHEADER *info;
675     CLIPFORMAT cf;
676     PresentationDataHeader pres;
677     ULARGE_INTEGER current_pos;
678     static const LARGE_INTEGER offset_zero;
679 
680     hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
681     if (FAILED( hr )) return hr;
682 
683     if (cache_entry->load_stream_num != STREAM_NUMBER_CONTENTS)
684     {
685         hr = read_clipformat( stm, &cf );
686         if (hr != S_OK) return hr;
687         hr = IStream_Read( stm, &pres, sizeof(pres), &read );
688         if (hr != S_OK) return hr;
689     }
690     else
691     {
692         hr = IStream_Read( stm, &file, sizeof(BITMAPFILEHEADER), &read );
693         if (hr != S_OK) return hr;
694     }
695 
696     hr = IStream_Seek( stm, offset_zero, STREAM_SEEK_CUR, &current_pos );
697     if (FAILED( hr )) return hr;
698     stat.cbSize.QuadPart -= current_pos.QuadPart;
699 
700     hglobal = GlobalAlloc( GMEM_MOVEABLE, stat.cbSize.u.LowPart );
701     if (!hglobal) return E_OUTOFMEMORY;
702     dib = GlobalLock( hglobal );
703 
704     /* read first DWORD of BITMAPINFOHEADER */
705     hr = IStream_Read( stm, dib, sizeof(DWORD), &read );
706     if (hr != S_OK) goto fail;
707     bi_size = *(DWORD *)dib;
708     if (stat.cbSize.QuadPart < bi_size) goto fail;
709 
710     /* read rest of BITMAPINFOHEADER */
711     hr = IStream_Read( stm, dib + sizeof(DWORD), bi_size - sizeof(DWORD), &read );
712     if (hr != S_OK) goto fail;
713 
714     info_size = bitmap_info_size( (BITMAPINFO *)dib, DIB_RGB_COLORS );
715     if (stat.cbSize.QuadPart < info_size) goto fail;
716     if (info_size > bi_size)
717     {
718         hr = IStream_Read( stm, dib + bi_size, info_size - bi_size, &read );
719         if (hr != S_OK) goto fail;
720     }
721     stat.cbSize.QuadPart -= info_size;
722 
723     /* set Stream pointer to beginning of bitmap bits */
724     if (cache_entry->load_stream_num == STREAM_NUMBER_CONTENTS && file.bfOffBits)
725     {
726         LARGE_INTEGER skip;
727 
728         skip.QuadPart = file.bfOffBits - sizeof(file) - info_size;
729         if (stat.cbSize.QuadPart < skip.QuadPart) goto fail;
730         hr = IStream_Seek( stm, skip, STREAM_SEEK_CUR, NULL );
731         if (hr != S_OK) goto fail;
732         stat.cbSize.QuadPart -= skip.QuadPart;
733     }
734 
735     hr = IStream_Read( stm, dib + info_size, stat.cbSize.u.LowPart, &read );
736     if (hr != S_OK) goto fail;
737 
738     if (bi_size >= sizeof(*info))
739     {
740         info = (BITMAPINFOHEADER *)dib;
741         if (info->biXPelsPerMeter == 0 || info->biYPelsPerMeter == 0)
742         {
743             HDC hdc = GetDC( 0 );
744             info->biXPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSX ), 10000, 254 );
745             info->biYPelsPerMeter = MulDiv( GetDeviceCaps( hdc, LOGPIXELSY ), 10000, 254 );
746             ReleaseDC( 0, hdc );
747         }
748     }
749 
750     GlobalUnlock( hglobal );
751 
752     cache_entry->stgmedium.tymed = TYMED_HGLOBAL;
753     cache_entry->stgmedium.u.hGlobal = hglobal;
754 
755     return hr;
756 
757 fail:
758     GlobalUnlock( hglobal );
759     GlobalFree( hglobal );
760     return hr;
761 
762 }
763 
764 static HRESULT load_emf( DataCacheEntry *cache_entry, IStream *stm )
765 {
766     HRESULT hr;
767 
768     if (cache_entry->load_stream_num != STREAM_NUMBER_CONTENTS)
769     {
770         STGMEDIUM stgmed;
771 
772         hr = load_mf_pict( cache_entry, stm );
773         if (SUCCEEDED( hr ))
774         {
775             hr = synthesize_emf( cache_entry->stgmedium.u.hMetaFilePict, &stgmed );
776             ReleaseStgMedium( &cache_entry->stgmedium );
777         }
778         if (SUCCEEDED( hr ))
779             cache_entry->stgmedium = stgmed;
780     }
781     else
782     {
783         STATSTG stat;
784         BYTE *data;
785         ULONG read, size_bits;
786 
787         hr = IStream_Stat( stm, &stat, STATFLAG_NONAME );
788 
789         if (SUCCEEDED( hr ))
790         {
791             data = HeapAlloc( GetProcessHeap(), 0, stat.cbSize.u.LowPart );
792             if (!data) return E_OUTOFMEMORY;
793 
794             hr = IStream_Read( stm, data, stat.cbSize.u.LowPart, &read );
795             if (hr != S_OK)
796             {
797                 HeapFree( GetProcessHeap(), 0, data );
798                 return hr;
799             }
800 
801             if (read <= sizeof(DWORD) + sizeof(ENHMETAHEADER))
802             {
803                 HeapFree( GetProcessHeap(), 0, data );
804                 return E_FAIL;
805             }
806             size_bits = read - sizeof(DWORD) - sizeof(ENHMETAHEADER);
807             cache_entry->stgmedium.u.hEnhMetaFile = SetEnhMetaFileBits( size_bits, data + (read - size_bits) );
808             cache_entry->stgmedium.tymed = TYMED_ENHMF;
809             cache_entry->stgmedium.pUnkForRelease = NULL;
810 
811             HeapFree( GetProcessHeap(), 0, data );
812         }
813     }
814 
815     return hr;
816 }
817 
818 /************************************************************************
819  * DataCacheEntry_LoadData
820  *
821  * This method will read information for the requested presentation
822  * into the given structure.
823  *
824  * Param:
825  *   This - The entry to load the data from.
826  *
827  * Returns:
828  *   This method returns a metafile handle if it is successful.
829  *   it will return 0 if not.
830  */
831 static HRESULT DataCacheEntry_LoadData(DataCacheEntry *cache_entry, IStorage *stg)
832 {
833     HRESULT hr;
834     IStream *stm;
835 
836     if (!stg) return OLE_E_BLANK;
837     hr = open_pres_stream( stg, cache_entry->load_stream_num, &stm );
838     if (FAILED(hr)) return hr;
839 
840     switch (cache_entry->fmtetc.cfFormat)
841     {
842     case CF_METAFILEPICT:
843         hr = load_mf_pict( cache_entry, stm );
844         break;
845 
846     case CF_DIB:
847         hr = load_dib( cache_entry, stm );
848         break;
849 
850     case CF_ENHMETAFILE:
851         hr = load_emf( cache_entry, stm );
852         break;
853 
854     default:
855         FIXME( "Unimplemented clip format %x\n", cache_entry->fmtetc.cfFormat );
856         hr = E_NOTIMPL;
857     }
858 
859     IStream_Release( stm );
860     return hr;
861 }
862 
863 static void init_stream_header(DataCacheEntry *entry, PresentationDataHeader *header)
864 {
865     if (entry->fmtetc.ptd)
866         FIXME("ptd not serialized\n");
867     header->tdSize = sizeof(header->tdSize);
868     header->dvAspect = entry->fmtetc.dwAspect;
869     header->lindex = entry->fmtetc.lindex;
870     header->advf = entry->advise_flags;
871     header->unknown7 = 0;
872     header->dwObjectExtentX = 0;
873     header->dwObjectExtentY = 0;
874     header->dwSize = 0;
875 }
876 
877 static HRESULT save_dib(DataCacheEntry *entry, BOOL contents, IStream *stream)
878 {
879     HRESULT hr = S_OK;
880     int data_size = 0;
881     BITMAPINFO *bmi = NULL;
882 
883     if (entry->stgmedium.tymed != TYMED_NULL)
884     {
885         data_size = GlobalSize(entry->stgmedium.u.hGlobal);
886         bmi = GlobalLock(entry->stgmedium.u.hGlobal);
887     }
888 
889     if (!contents)
890     {
891         PresentationDataHeader header;
892 
893         init_stream_header(entry, &header);
894         hr = write_clipformat(stream, entry->fmtetc.cfFormat);
895         if (FAILED(hr)) goto end;
896         if (data_size)
897         {
898             header.dwSize = data_size;
899             /* Size in units of 0.01mm (ie. MM_HIMETRIC) */
900             if (bmi->bmiHeader.biXPelsPerMeter != 0 && bmi->bmiHeader.biYPelsPerMeter != 0)
901             {
902                 header.dwObjectExtentX = MulDiv(bmi->bmiHeader.biWidth, 100000, bmi->bmiHeader.biXPelsPerMeter);
903                 header.dwObjectExtentY = MulDiv(bmi->bmiHeader.biHeight, 100000, bmi->bmiHeader.biYPelsPerMeter);
904             }
905             else
906             {
907                 HDC hdc = GetDC(0);
908                 header.dwObjectExtentX = MulDiv(bmi->bmiHeader.biWidth, 2540, GetDeviceCaps(hdc, LOGPIXELSX));
909                 header.dwObjectExtentY = MulDiv(bmi->bmiHeader.biHeight, 2540, GetDeviceCaps(hdc, LOGPIXELSY));
910                 ReleaseDC(0, hdc);
911             }
912         }
913         hr = IStream_Write(stream, &header, sizeof(PresentationDataHeader), NULL);
914         if (hr == S_OK && data_size)
915             hr = IStream_Write(stream, bmi, data_size, NULL);
916     }
917     else if(data_size)
918     {
919         BITMAPFILEHEADER bmp_fhdr;
920 
921         bmp_fhdr.bfType = 0x4d42;
922         bmp_fhdr.bfSize = data_size + sizeof(BITMAPFILEHEADER);
923         bmp_fhdr.bfReserved1 = bmp_fhdr.bfReserved2 = 0;
924         bmp_fhdr.bfOffBits = bitmap_info_size(bmi, DIB_RGB_COLORS) + sizeof(BITMAPFILEHEADER);
925         hr = IStream_Write(stream, &bmp_fhdr, sizeof(BITMAPFILEHEADER), NULL);
926         if (hr == S_OK)
927             hr = IStream_Write(stream, bmi, data_size, NULL);
928     }
929 
930 end:
931     if (bmi) GlobalUnlock(entry->stgmedium.u.hGlobal);
932     return hr;
933 }
934 
935 static HRESULT save_mfpict(DataCacheEntry *entry, BOOL contents, IStream *stream)
936 {
937     HRESULT hr = S_OK;
938     int data_size = 0;
939     void *data = NULL;
940     METAFILEPICT *mfpict = NULL;
941 
942     if (!contents)
943     {
944         PresentationDataHeader header;
945 
946         init_stream_header(entry, &header);
947         hr = write_clipformat(stream, entry->fmtetc.cfFormat);
948         if (FAILED(hr)) return hr;
949         if (entry->stgmedium.tymed != TYMED_NULL)
950         {
951             mfpict = GlobalLock(entry->stgmedium.u.hMetaFilePict);
952             if (!mfpict)
953                 return DV_E_STGMEDIUM;
954             data_size = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
955             header.dwObjectExtentX = mfpict->xExt;
956             header.dwObjectExtentY = mfpict->yExt;
957             header.dwSize = data_size;
958             data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
959             if (!data)
960             {
961                 GlobalUnlock(entry->stgmedium.u.hMetaFilePict);
962                 return E_OUTOFMEMORY;
963             }
964             GetMetaFileBitsEx(mfpict->hMF, header.dwSize, data);
965             GlobalUnlock(entry->stgmedium.u.hMetaFilePict);
966         }
967         hr = IStream_Write(stream, &header, sizeof(PresentationDataHeader), NULL);
968         if (hr == S_OK && data_size)
969             hr = IStream_Write(stream, data, data_size, NULL);
970         HeapFree(GetProcessHeap(), 0, data);
971     }
972     else if (entry->stgmedium.tymed != TYMED_NULL)
973     {
974         struct meta_placeable meta_place_rec;
975         WORD *check;
976 
977         mfpict = GlobalLock(entry->stgmedium.u.hMetaFilePict);
978         if (!mfpict)
979             return DV_E_STGMEDIUM;
980         data_size = GetMetaFileBitsEx(mfpict->hMF, 0, NULL);
981         data = HeapAlloc(GetProcessHeap(), 0, data_size);
982         if (!data)
983         {
984             GlobalUnlock(entry->stgmedium.u.hMetaFilePict);
985             return E_OUTOFMEMORY;
986         }
987         GetMetaFileBitsEx(mfpict->hMF, data_size, data);
988 
989         /* units are in 1/8th of a point (1 point is 1/72th of an inch) */
990         meta_place_rec.key = 0x9ac6cdd7;
991         meta_place_rec.hwmf = 0;
992         meta_place_rec.inch = 576;
993         meta_place_rec.bounding_box[0] = 0;
994         meta_place_rec.bounding_box[1] = 0;
995         meta_place_rec.bounding_box[2] = 0;
996         meta_place_rec.bounding_box[3] = 0;
997         meta_place_rec.checksum = 0;
998         meta_place_rec.reserved = 0;
999 
1000         /* These values are rounded down so MulDiv won't do the right thing */
1001         meta_place_rec.bounding_box[2] = (LONGLONG)mfpict->xExt * meta_place_rec.inch / 2540;
1002         meta_place_rec.bounding_box[3] = (LONGLONG)mfpict->yExt * meta_place_rec.inch / 2540;
1003         GlobalUnlock(entry->stgmedium.u.hMetaFilePict);
1004 
1005         for (check = (WORD *)&meta_place_rec; check != &meta_place_rec.checksum; check++)
1006             meta_place_rec.checksum ^= *check;
1007         hr = IStream_Write(stream, &meta_place_rec, sizeof(struct meta_placeable), NULL);
1008         if (hr == S_OK && data_size)
1009             hr = IStream_Write(stream, data, data_size, NULL);
1010         HeapFree(GetProcessHeap(), 0, data);
1011     }
1012 
1013     return hr;
1014 }
1015 
1016 static HRESULT save_emf(DataCacheEntry *entry, BOOL contents, IStream *stream)
1017 {
1018     HRESULT hr = S_OK;
1019     int data_size = 0;
1020     BYTE *data;
1021 
1022     if (!contents)
1023     {
1024         PresentationDataHeader header;
1025         METAFILEPICT *mfpict;
1026         HDC hdc = GetDC(0);
1027 
1028         init_stream_header(entry, &header);
1029         hr = write_clipformat(stream, entry->fmtetc.cfFormat);
1030         if (FAILED(hr))
1031         {
1032             ReleaseDC(0, hdc);
1033             return hr;
1034         }
1035         data_size = GetWinMetaFileBits(entry->stgmedium.u.hEnhMetaFile, 0, NULL, MM_ANISOTROPIC, hdc);
1036         header.dwSize = data_size;
1037         data = HeapAlloc(GetProcessHeap(), 0, header.dwSize);
1038         if (!data)
1039         {
1040             ReleaseDC(0, hdc);
1041             return E_OUTOFMEMORY;
1042         }
1043         GetWinMetaFileBits(entry->stgmedium.u.hEnhMetaFile, header.dwSize, data, MM_ANISOTROPIC, hdc);
1044         ReleaseDC(0, hdc);
1045         mfpict = (METAFILEPICT *)data;
1046         header.dwObjectExtentX = mfpict->xExt;
1047         header.dwObjectExtentY = mfpict->yExt;
1048         hr = IStream_Write(stream, &header, sizeof(PresentationDataHeader), NULL);
1049         if (hr == S_OK && data_size)
1050             hr = IStream_Write(stream, data, data_size, NULL);
1051         HeapFree(GetProcessHeap(), 0, data);
1052     }
1053     else if (entry->stgmedium.tymed != TYMED_NULL)
1054     {
1055         data_size = GetEnhMetaFileBits(entry->stgmedium.u.hEnhMetaFile, 0, NULL);
1056         data = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) + sizeof(ENHMETAHEADER) + data_size);
1057         if (!data) return E_OUTOFMEMORY;
1058         *((DWORD *)data) = sizeof(ENHMETAHEADER);
1059         GetEnhMetaFileBits(entry->stgmedium.u.hEnhMetaFile, data_size, data + sizeof(DWORD) + sizeof(ENHMETAHEADER));
1060         memcpy(data + sizeof(DWORD), data + sizeof(DWORD) + sizeof(ENHMETAHEADER), sizeof(ENHMETAHEADER));
1061         data_size += sizeof(DWORD) + sizeof(ENHMETAHEADER);
1062         hr = IStream_Write(stream, data, data_size, NULL);
1063         HeapFree(GetProcessHeap(), 0, data);
1064     }
1065 
1066     return hr;
1067 }
1068 
1069 static HRESULT save_view_cache(DataCacheEntry *entry, IStream *stream)
1070 {
1071     HRESULT hr;
1072     PresentationDataHeader header;
1073 
1074     init_stream_header(entry, &header);
1075     hr = write_clipformat(stream, entry->fmtetc.cfFormat);
1076     if (SUCCEEDED(hr))
1077         hr = IStream_Write(stream, &header, FIELD_OFFSET(PresentationDataHeader, unknown7), NULL);
1078 
1079     return hr;
1080 }
1081 
1082 static HRESULT create_stream(DataCacheEntry *cache_entry, IStorage *storage,
1083                              BOOL contents, IStream **stream)
1084 {
1085     WCHAR pres[] = {2,'O','l','e','P','r','e','s',
1086         '0' + (cache_entry->save_stream_num / 100) % 10,
1087         '0' + (cache_entry->save_stream_num / 10) % 10,
1088         '0' + cache_entry->save_stream_num % 10, 0};
1089     const WCHAR *name;
1090 
1091     if (contents)
1092         name = CONTENTS;
1093     else
1094         name = pres;
1095 
1096     return IStorage_CreateStream(storage, name,
1097                                  STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
1098                                  0, 0, stream);
1099 }
1100 
1101 static HRESULT DataCacheEntry_Save(DataCacheEntry *cache_entry, IStorage *storage,
1102                                    BOOL same_as_load)
1103 {
1104     HRESULT hr;
1105     IStream *stream;
1106     BOOL contents = (cache_entry->id == 1);
1107 
1108     TRACE("stream_number = %d, fmtetc = %s\n", cache_entry->save_stream_num, debugstr_formatetc(&cache_entry->fmtetc));
1109 
1110     hr = create_stream(cache_entry, storage, contents, &stream);
1111     if (FAILED(hr))
1112         return hr;
1113 
1114     switch (cache_entry->fmtetc.cfFormat)
1115     {
1116     case CF_DIB:
1117         hr = save_dib(cache_entry, contents, stream);
1118         break;
1119     case CF_METAFILEPICT:
1120         hr = save_mfpict(cache_entry, contents, stream);
1121         break;
1122     case CF_ENHMETAFILE:
1123         hr = save_emf(cache_entry, contents, stream);
1124         break;
1125     case 0:
1126         hr = save_view_cache(cache_entry, stream);
1127         break;
1128     default:
1129         FIXME("got unsupported clipboard format %x\n", cache_entry->fmtetc.cfFormat);
1130     }
1131 
1132     IStream_Release(stream);
1133     return hr;
1134 }
1135 
1136 /* helper for copying STGMEDIUM of type bitmap, MF, EMF or HGLOBAL.
1137 * does no checking of whether src_stgm has a supported tymed, so this should be
1138 * done in the caller */
1139 static HRESULT copy_stg_medium(CLIPFORMAT cf, STGMEDIUM *dest_stgm,
1140                                const STGMEDIUM *src_stgm)
1141 {
1142     if (src_stgm->tymed == TYMED_MFPICT)
1143     {
1144         const METAFILEPICT *src_mfpict = GlobalLock(src_stgm->u.hMetaFilePict);
1145         METAFILEPICT *dest_mfpict;
1146 
1147         if (!src_mfpict)
1148             return DV_E_STGMEDIUM;
1149         dest_stgm->u.hMetaFilePict = GlobalAlloc(GMEM_MOVEABLE, sizeof(METAFILEPICT));
1150         dest_mfpict = GlobalLock(dest_stgm->u.hMetaFilePict);
1151         if (!dest_mfpict)
1152         {
1153             GlobalUnlock(src_stgm->u.hMetaFilePict);
1154             return E_OUTOFMEMORY;
1155         }
1156         *dest_mfpict = *src_mfpict;
1157         dest_mfpict->hMF = CopyMetaFileW(src_mfpict->hMF, NULL);
1158         GlobalUnlock(src_stgm->u.hMetaFilePict);
1159         GlobalUnlock(dest_stgm->u.hMetaFilePict);
1160     }
1161     else if (src_stgm->tymed != TYMED_NULL)
1162     {
1163         dest_stgm->u.hGlobal = OleDuplicateData(src_stgm->u.hGlobal, cf,
1164                                                 GMEM_MOVEABLE);
1165         if (!dest_stgm->u.hGlobal)
1166             return E_OUTOFMEMORY;
1167     }
1168     dest_stgm->tymed = src_stgm->tymed;
1169     dest_stgm->pUnkForRelease = src_stgm->pUnkForRelease;
1170     if (dest_stgm->pUnkForRelease)
1171         IUnknown_AddRef(dest_stgm->pUnkForRelease);
1172     return S_OK;
1173 }
1174 
1175 static HRESULT synthesize_dib( HBITMAP bm, STGMEDIUM *med )
1176 {
1177     HDC hdc = GetDC( 0 );
1178     BITMAPINFOHEADER header;
1179     BITMAPINFO *bmi;
1180     HRESULT hr = E_FAIL;
1181     DWORD header_size;
1182 
1183     memset( &header, 0, sizeof(header) );
1184     header.biSize = sizeof(header);
1185     if (!GetDIBits( hdc, bm, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done;
1186 
1187     header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS );
1188     if (!(med->u.hGlobal = GlobalAlloc( GMEM_MOVEABLE, header_size + header.biSizeImage ))) goto done;
1189     bmi = GlobalLock( med->u.hGlobal );
1190     memset( bmi, 0, header_size );
1191     memcpy( bmi, &header, header.biSize );
1192     GetDIBits( hdc, bm, 0, abs(header.biHeight), (char *)bmi + header_size, bmi, DIB_RGB_COLORS );
1193     GlobalUnlock( med->u.hGlobal );
1194     med->tymed = TYMED_HGLOBAL;
1195     med->pUnkForRelease = NULL;
1196     hr = S_OK;
1197 
1198 done:
1199     ReleaseDC( 0, hdc );
1200     return hr;
1201 }
1202 
1203 static HRESULT synthesize_bitmap( HGLOBAL dib, STGMEDIUM *med )
1204 {
1205     HRESULT hr = E_FAIL;
1206     BITMAPINFO *bmi;
1207     HDC hdc = GetDC( 0 );
1208 
1209     if ((bmi = GlobalLock( dib )))
1210     {
1211         /* FIXME: validate data size */
1212         med->u.hBitmap = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT,
1213                                          (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ),
1214                                          bmi, DIB_RGB_COLORS );
1215         GlobalUnlock( dib );
1216         med->tymed = TYMED_GDI;
1217         med->pUnkForRelease = NULL;
1218         hr = S_OK;
1219     }
1220     ReleaseDC( 0, hdc );
1221     return hr;
1222 }
1223 
1224 static HRESULT DataCacheEntry_SetData(DataCacheEntry *cache_entry,
1225                                       const FORMATETC *formatetc,
1226                                       STGMEDIUM *stgmedium,
1227                                       BOOL fRelease)
1228 {
1229     STGMEDIUM copy;
1230     HRESULT hr;
1231 
1232     if ((!cache_entry->fmtetc.cfFormat && !formatetc->cfFormat) ||
1233         (cache_entry->fmtetc.tymed == TYMED_NULL && formatetc->tymed == TYMED_NULL) ||
1234         stgmedium->tymed == TYMED_NULL)
1235     {
1236         WARN("invalid formatetc\n");
1237         return DV_E_FORMATETC;
1238     }
1239 
1240     cache_entry->dirty = TRUE;
1241     ReleaseStgMedium(&cache_entry->stgmedium);
1242 
1243     if (formatetc->cfFormat == CF_BITMAP)
1244     {
1245         hr = synthesize_dib( stgmedium->u.hBitmap, &copy );
1246         if (FAILED(hr)) return hr;
1247         if (fRelease) ReleaseStgMedium(stgmedium);
1248         stgmedium = &copy;
1249         fRelease = TRUE;
1250     }
1251     else if (formatetc->cfFormat == CF_METAFILEPICT && cache_entry->fmtetc.cfFormat == CF_ENHMETAFILE)
1252     {
1253         hr = synthesize_emf( stgmedium->u.hMetaFilePict, &copy );
1254         if (FAILED(hr)) return hr;
1255         if (fRelease) ReleaseStgMedium(stgmedium);
1256         stgmedium = &copy;
1257         fRelease = TRUE;
1258     }
1259 
1260     if (fRelease)
1261     {
1262         cache_entry->stgmedium = *stgmedium;
1263         return S_OK;
1264     }
1265     else
1266         return copy_stg_medium(cache_entry->fmtetc.cfFormat, &cache_entry->stgmedium, stgmedium);
1267 }
1268 
1269 static HRESULT DataCacheEntry_GetData(DataCacheEntry *cache_entry, IStorage *stg, FORMATETC *fmt, STGMEDIUM *stgmedium)
1270 {
1271     if (cache_entry->stgmedium.tymed == TYMED_NULL && cache_entry->load_stream_num != STREAM_NUMBER_NOT_SET)
1272     {
1273         HRESULT hr = DataCacheEntry_LoadData(cache_entry, stg);
1274         if (FAILED(hr))
1275             return hr;
1276     }
1277     if (cache_entry->stgmedium.tymed == TYMED_NULL)
1278         return OLE_E_BLANK;
1279 
1280     if (fmt->cfFormat == CF_BITMAP)
1281         return synthesize_bitmap( cache_entry->stgmedium.u.hGlobal, stgmedium );
1282 
1283     return copy_stg_medium(cache_entry->fmtetc.cfFormat, stgmedium, &cache_entry->stgmedium);
1284 }
1285 
1286 static inline HRESULT DataCacheEntry_DiscardData(DataCacheEntry *cache_entry)
1287 {
1288     ReleaseStgMedium(&cache_entry->stgmedium);
1289     return S_OK;
1290 }
1291 
1292 static inline DWORD tymed_from_cf( DWORD cf )
1293 {
1294     switch( cf )
1295     {
1296     case CF_BITMAP:       return TYMED_GDI;
1297     case CF_METAFILEPICT: return TYMED_MFPICT;
1298     case CF_ENHMETAFILE:  return TYMED_ENHMF;
1299     case CF_DIB:
1300     default:              return TYMED_HGLOBAL;
1301     }
1302 }
1303 
1304 /****************************************************************
1305  *  create_automatic_entry
1306  *
1307  * Creates an appropriate cache entry for one of the CLSID_Picture_
1308  * classes.  The connection id of the entry is one.  Any pre-existing
1309  * automatic entry is re-assigned a new connection id, and moved to
1310  * the end of the list.
1311  */
1312 static HRESULT create_automatic_entry(DataCache *cache, const CLSID *clsid)
1313 {
1314     static const struct data
1315     {
1316         const CLSID *clsid;
1317         FORMATETC fmt;
1318     } data[] =
1319     {
1320         { &CLSID_Picture_Dib,         { CF_DIB,          0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } },
1321         { &CLSID_Picture_Metafile,    { CF_METAFILEPICT, 0, DVASPECT_CONTENT, -1, TYMED_MFPICT } },
1322         { &CLSID_Picture_EnhMetafile, { CF_ENHMETAFILE,  0, DVASPECT_CONTENT, -1, TYMED_ENHMF } },
1323         { NULL }
1324     };
1325     const struct data *ptr = data;
1326     struct list *head;
1327     DataCacheEntry *entry;
1328 
1329     if (IsEqualCLSID( &cache->clsid, clsid )) return S_OK;
1330 
1331     /* move and reassign any pre-existing automatic entry */
1332     if ((head = list_head( &cache->cache_list )))
1333     {
1334         entry = LIST_ENTRY( head, DataCacheEntry, entry );
1335         if (entry->id == 1)
1336         {
1337             list_remove( &entry->entry );
1338             entry->id = cache->last_cache_id++;
1339             list_add_tail( &cache->cache_list, &entry->entry );
1340         }
1341     }
1342 
1343     while (ptr->clsid)
1344     {
1345         if (IsEqualCLSID( clsid, ptr->clsid ))
1346         {
1347             cache->clsid_static = TRUE;
1348             return DataCache_CreateEntry( cache, &ptr->fmt, 0, TRUE, NULL );
1349         }
1350         ptr++;
1351     }
1352     cache->clsid_static = FALSE;
1353     return S_OK;
1354 }
1355 
1356 /*********************************************************
1357  * Method implementation for the  non delegating IUnknown
1358  * part of the DataCache class.
1359  */
1360 
1361 /************************************************************************
1362  * DataCache_NDIUnknown_QueryInterface (IUnknown)
1363  *
1364  * This version of QueryInterface will not delegate its implementation
1365  * to the outer unknown.
1366  */
1367 static HRESULT WINAPI DataCache_NDIUnknown_QueryInterface(
1368             IUnknown*      iface,
1369             REFIID         riid,
1370             void**         ppvObject)
1371 {
1372   DataCache *this = impl_from_IUnknown(iface);
1373 
1374   if ( ppvObject==0 )
1375     return E_INVALIDARG;
1376 
1377   *ppvObject = 0;
1378 
1379   if (IsEqualIID(&IID_IUnknown, riid))
1380   {
1381     if (this->outer_unk == iface) /* non-aggregated, return IUnknown from IOleCache2 */
1382       *ppvObject = &this->IOleCache2_iface;
1383     else
1384       *ppvObject = iface;
1385   }
1386   else if (IsEqualIID(&IID_IDataObject, riid))
1387   {
1388     *ppvObject = &this->IDataObject_iface;
1389   }
1390   else if ( IsEqualIID(&IID_IPersistStorage, riid)  ||
1391             IsEqualIID(&IID_IPersist, riid) )
1392   {
1393     *ppvObject = &this->IPersistStorage_iface;
1394   }
1395   else if ( IsEqualIID(&IID_IViewObject, riid) ||
1396             IsEqualIID(&IID_IViewObject2, riid) )
1397   {
1398     *ppvObject = &this->IViewObject2_iface;
1399   }
1400   else if ( IsEqualIID(&IID_IOleCache, riid) ||
1401             IsEqualIID(&IID_IOleCache2, riid) )
1402   {
1403     *ppvObject = &this->IOleCache2_iface;
1404   }
1405   else if ( IsEqualIID(&IID_IOleCacheControl, riid) )
1406   {
1407     *ppvObject = &this->IOleCacheControl_iface;
1408   }
1409 
1410   if ((*ppvObject)==0)
1411   {
1412     WARN( "() : asking for unsupported interface %s\n", debugstr_guid(riid));
1413     return E_NOINTERFACE;
1414   }
1415 
1416   IUnknown_AddRef((IUnknown*)*ppvObject);
1417 
1418   return S_OK;
1419 }
1420 
1421 /************************************************************************
1422  * DataCache_NDIUnknown_AddRef (IUnknown)
1423  *
1424  * This version of QueryInterface will not delegate its implementation
1425  * to the outer unknown.
1426  */
1427 static ULONG WINAPI DataCache_NDIUnknown_AddRef(
1428             IUnknown*      iface)
1429 {
1430   DataCache *this = impl_from_IUnknown(iface);
1431   return InterlockedIncrement(&this->ref);
1432 }
1433 
1434 /************************************************************************
1435  * DataCache_NDIUnknown_Release (IUnknown)
1436  *
1437  * This version of QueryInterface will not delegate its implementation
1438  * to the outer unknown.
1439  */
1440 static ULONG WINAPI DataCache_NDIUnknown_Release(
1441             IUnknown*      iface)
1442 {
1443   DataCache *this = impl_from_IUnknown(iface);
1444   ULONG ref;
1445 
1446   ref = InterlockedDecrement(&this->ref);
1447 
1448   if (ref == 0) DataCache_Destroy(this);
1449 
1450   return ref;
1451 }
1452 
1453 /*********************************************************
1454  * Method implementation for the IDataObject
1455  * part of the DataCache class.
1456  */
1457 
1458 /************************************************************************
1459  * DataCache_IDataObject_QueryInterface (IUnknown)
1460  */
1461 static HRESULT WINAPI DataCache_IDataObject_QueryInterface(
1462             IDataObject*     iface,
1463             REFIID           riid,
1464             void**           ppvObject)
1465 {
1466   DataCache *this = impl_from_IDataObject(iface);
1467 
1468   return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1469 }
1470 
1471 /************************************************************************
1472  * DataCache_IDataObject_AddRef (IUnknown)
1473  */
1474 static ULONG WINAPI DataCache_IDataObject_AddRef(
1475             IDataObject*     iface)
1476 {
1477   DataCache *this = impl_from_IDataObject(iface);
1478 
1479   return IUnknown_AddRef(this->outer_unk);
1480 }
1481 
1482 /************************************************************************
1483  * DataCache_IDataObject_Release (IUnknown)
1484  */
1485 static ULONG WINAPI DataCache_IDataObject_Release(
1486             IDataObject*     iface)
1487 {
1488   DataCache *this = impl_from_IDataObject(iface);
1489 
1490   return IUnknown_Release(this->outer_unk);
1491 }
1492 
1493 /************************************************************************
1494  * DataCache_GetData
1495  *
1496  * Get Data from a source dataobject using format pformatetcIn->cfFormat
1497  */
1498 static HRESULT WINAPI DataCache_GetData(
1499 	    IDataObject*     iface,
1500 	    LPFORMATETC      pformatetcIn,
1501 	    STGMEDIUM*       pmedium)
1502 {
1503     DataCache *This = impl_from_IDataObject(iface);
1504     DataCacheEntry *cache_entry;
1505 
1506     TRACE("(%p, %s, %p)\n", iface, debugstr_formatetc(pformatetcIn), pmedium);
1507 
1508     memset(pmedium, 0, sizeof(*pmedium));
1509 
1510     cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetcIn);
1511     if (!cache_entry)
1512         return OLE_E_BLANK;
1513 
1514     return DataCacheEntry_GetData(cache_entry, This->presentationStorage, pformatetcIn, pmedium);
1515 }
1516 
1517 static HRESULT WINAPI DataCache_GetDataHere(
1518 	    IDataObject*     iface,
1519 	    LPFORMATETC      pformatetc,
1520 	    STGMEDIUM*       pmedium)
1521 {
1522   FIXME("stub\n");
1523   return E_NOTIMPL;
1524 }
1525 
1526 static HRESULT WINAPI DataCache_QueryGetData( IDataObject *iface, FORMATETC *fmt )
1527 {
1528     DataCache *This = impl_from_IDataObject( iface );
1529     DataCacheEntry *cache_entry;
1530 
1531     TRACE( "(%p)->(%s)\n", iface, debugstr_formatetc( fmt ) );
1532     cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1533 
1534     return cache_entry ? S_OK : S_FALSE;
1535 }
1536 
1537 /************************************************************************
1538  * DataCache_EnumFormatEtc (IDataObject)
1539  *
1540  * The data cache doesn't implement this method.
1541  */
1542 static HRESULT WINAPI DataCache_GetCanonicalFormatEtc(
1543 	    IDataObject*     iface,
1544 	    LPFORMATETC      pformatectIn,
1545 	    LPFORMATETC      pformatetcOut)
1546 {
1547   TRACE("()\n");
1548   return E_NOTIMPL;
1549 }
1550 
1551 /************************************************************************
1552  * DataCache_IDataObject_SetData (IDataObject)
1553  *
1554  * This method is delegated to the IOleCache2 implementation.
1555  */
1556 static HRESULT WINAPI DataCache_IDataObject_SetData(
1557 	    IDataObject*     iface,
1558 	    LPFORMATETC      pformatetc,
1559 	    STGMEDIUM*       pmedium,
1560 	    BOOL             fRelease)
1561 {
1562   IOleCache2* oleCache = NULL;
1563   HRESULT     hres;
1564 
1565   TRACE("(%p, %p, %p, %d)\n", iface, pformatetc, pmedium, fRelease);
1566 
1567   hres = IDataObject_QueryInterface(iface, &IID_IOleCache2, (void**)&oleCache);
1568 
1569   if (FAILED(hres))
1570     return E_UNEXPECTED;
1571 
1572   hres = IOleCache2_SetData(oleCache, pformatetc, pmedium, fRelease);
1573 
1574   IOleCache2_Release(oleCache);
1575 
1576   return hres;
1577 }
1578 
1579 /************************************************************************
1580  * DataCache_EnumFormatEtc (IDataObject)
1581  *
1582  * The data cache doesn't implement this method.
1583  */
1584 static HRESULT WINAPI DataCache_EnumFormatEtc(
1585 	    IDataObject*     iface,
1586 	    DWORD            dwDirection,
1587 	    IEnumFORMATETC** ppenumFormatEtc)
1588 {
1589   TRACE("()\n");
1590   return E_NOTIMPL;
1591 }
1592 
1593 /************************************************************************
1594  * DataCache_DAdvise (IDataObject)
1595  *
1596  * The data cache doesn't support connections.
1597  */
1598 static HRESULT WINAPI DataCache_DAdvise(
1599 	    IDataObject*     iface,
1600 	    FORMATETC*       pformatetc,
1601 	    DWORD            advf,
1602 	    IAdviseSink*     pAdvSink,
1603 	    DWORD*           pdwConnection)
1604 {
1605   TRACE("()\n");
1606   return OLE_E_ADVISENOTSUPPORTED;
1607 }
1608 
1609 /************************************************************************
1610  * DataCache_DUnadvise (IDataObject)
1611  *
1612  * The data cache doesn't support connections.
1613  */
1614 static HRESULT WINAPI DataCache_DUnadvise(
1615 	    IDataObject*     iface,
1616 	    DWORD            dwConnection)
1617 {
1618   TRACE("()\n");
1619   return OLE_E_NOCONNECTION;
1620 }
1621 
1622 /************************************************************************
1623  * DataCache_EnumDAdvise (IDataObject)
1624  *
1625  * The data cache doesn't support connections.
1626  */
1627 static HRESULT WINAPI DataCache_EnumDAdvise(
1628 	    IDataObject*     iface,
1629 	    IEnumSTATDATA**  ppenumAdvise)
1630 {
1631   TRACE("()\n");
1632   return OLE_E_ADVISENOTSUPPORTED;
1633 }
1634 
1635 /*********************************************************
1636  * Method implementation for the IDataObject
1637  * part of the DataCache class.
1638  */
1639 
1640 /************************************************************************
1641  * DataCache_IPersistStorage_QueryInterface (IUnknown)
1642  */
1643 static HRESULT WINAPI DataCache_IPersistStorage_QueryInterface(
1644             IPersistStorage* iface,
1645             REFIID           riid,
1646             void**           ppvObject)
1647 {
1648   DataCache *this = impl_from_IPersistStorage(iface);
1649 
1650   return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1651 }
1652 
1653 /************************************************************************
1654  * DataCache_IPersistStorage_AddRef (IUnknown)
1655  */
1656 static ULONG WINAPI DataCache_IPersistStorage_AddRef(
1657             IPersistStorage* iface)
1658 {
1659   DataCache *this = impl_from_IPersistStorage(iface);
1660 
1661   return IUnknown_AddRef(this->outer_unk);
1662 }
1663 
1664 /************************************************************************
1665  * DataCache_IPersistStorage_Release (IUnknown)
1666  */
1667 static ULONG WINAPI DataCache_IPersistStorage_Release(
1668             IPersistStorage* iface)
1669 {
1670   DataCache *this = impl_from_IPersistStorage(iface);
1671 
1672   return IUnknown_Release(this->outer_unk);
1673 }
1674 
1675 /************************************************************************
1676  * DataCache_GetClassID (IPersistStorage)
1677  *
1678  */
1679 static HRESULT WINAPI DataCache_GetClassID(IPersistStorage *iface, CLSID *clsid)
1680 {
1681     DataCache *This = impl_from_IPersistStorage( iface );
1682 
1683     TRACE( "(%p, %p) returning %s\n", iface, clsid, debugstr_guid(&This->clsid) );
1684     *clsid = This->clsid;
1685 
1686     return S_OK;
1687 }
1688 
1689 /************************************************************************
1690  * DataCache_IsDirty (IPersistStorage)
1691  */
1692 static HRESULT WINAPI DataCache_IsDirty(
1693             IPersistStorage* iface)
1694 {
1695     DataCache *This = impl_from_IPersistStorage(iface);
1696     DataCacheEntry *cache_entry;
1697 
1698     TRACE("(%p)\n", iface);
1699 
1700     if (This->dirty)
1701         return S_OK;
1702 
1703     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1704         if (cache_entry->dirty)
1705             return S_OK;
1706 
1707     return S_FALSE;
1708 }
1709 
1710 /************************************************************************
1711  * DataCache_InitNew (IPersistStorage)
1712  *
1713  * The data cache implementation of IPersistStorage_InitNew simply stores
1714  * the storage pointer.
1715  */
1716 static HRESULT WINAPI DataCache_InitNew(
1717             IPersistStorage* iface,
1718 	    IStorage*        pStg)
1719 {
1720     DataCache *This = impl_from_IPersistStorage(iface);
1721     CLSID clsid;
1722     HRESULT hr;
1723 
1724     TRACE("(%p, %p)\n", iface, pStg);
1725 
1726     if (This->presentationStorage != NULL)
1727         return CO_E_ALREADYINITIALIZED;
1728 
1729     This->presentationStorage = pStg;
1730 
1731     IStorage_AddRef(This->presentationStorage);
1732     This->dirty = TRUE;
1733     ReadClassStg( pStg, &clsid );
1734     hr = create_automatic_entry( This, &clsid );
1735     if (FAILED(hr))
1736     {
1737         IStorage_Release( pStg );
1738         This->presentationStorage = NULL;
1739         return hr;
1740     }
1741     This->clsid = clsid;
1742 
1743     return S_OK;
1744 }
1745 
1746 
1747 static HRESULT add_cache_entry( DataCache *This, const FORMATETC *fmt, DWORD advf, int stream_number )
1748 {
1749     DataCacheEntry *cache_entry;
1750     HRESULT hr = S_OK;
1751 
1752     TRACE( "loading entry with formatetc: %s\n", debugstr_formatetc( fmt ) );
1753 
1754     cache_entry = DataCache_GetEntryForFormatEtc( This, fmt );
1755     if (!cache_entry)
1756         hr = DataCache_CreateEntry( This, fmt, advf, FALSE, &cache_entry );
1757     if (SUCCEEDED( hr ))
1758     {
1759         DataCacheEntry_DiscardData( cache_entry );
1760         cache_entry->load_stream_num = stream_number;
1761         cache_entry->save_stream_num = stream_number;
1762         cache_entry->dirty = FALSE;
1763     }
1764     return hr;
1765 }
1766 
1767 static HRESULT parse_pres_streams( DataCache *cache, IStorage *stg )
1768 {
1769     HRESULT hr;
1770     IStream *stm;
1771     PresentationDataHeader header;
1772     ULONG actual_read;
1773     CLIPFORMAT clipformat;
1774     FORMATETC fmtetc;
1775     int stream_number = 0;
1776 
1777     do
1778     {
1779         hr = open_pres_stream( stg, stream_number, &stm );
1780         if (FAILED(hr)) break;
1781 
1782         hr = read_clipformat( stm, &clipformat );
1783 
1784         if (hr == S_OK) hr = IStream_Read( stm, &header, sizeof(header), &actual_read );
1785 
1786         if (hr == S_OK && actual_read == sizeof(header))
1787         {
1788             fmtetc.cfFormat = clipformat;
1789             fmtetc.ptd = NULL; /* FIXME */
1790             fmtetc.dwAspect = header.dvAspect;
1791             fmtetc.lindex = header.lindex;
1792             fmtetc.tymed = tymed_from_cf( clipformat );
1793 
1794             add_cache_entry( cache, &fmtetc, header.advf, stream_number );
1795         }
1796         IStream_Release( stm );
1797         stream_number++;
1798     } while (hr == S_OK);
1799 
1800     return S_OK;
1801 }
1802 
1803 static HRESULT parse_contents_stream( DataCache *cache, IStorage *stg )
1804 {
1805     HRESULT hr;
1806     IStream *stm;
1807     DataCacheEntry *cache_entry;
1808 
1809     hr = open_pres_stream( stg, STREAM_NUMBER_CONTENTS, &stm );
1810     if (FAILED( hr )) return hr;
1811 
1812     hr = get_static_entry( cache, &cache_entry );
1813     if (hr == S_OK)
1814     {
1815         cache_entry->load_stream_num = STREAM_NUMBER_CONTENTS;
1816         cache_entry->save_stream_num = STREAM_NUMBER_CONTENTS;
1817         cache_entry->dirty = FALSE;
1818     }
1819 
1820     IStream_Release( stm );
1821     return hr;
1822 }
1823 
1824 /************************************************************************
1825  * DataCache_Load (IPersistStorage)
1826  *
1827  * The data cache implementation of IPersistStorage_Load doesn't
1828  * actually load anything. Instead, it holds on to the storage pointer
1829  * and it will load the presentation information when the
1830  * IDataObject_GetData or IViewObject2_Draw methods are called.
1831  */
1832 static HRESULT WINAPI DataCache_Load( IPersistStorage *iface, IStorage *stg )
1833 {
1834     DataCache *This = impl_from_IPersistStorage(iface);
1835     HRESULT hr;
1836     CLSID clsid;
1837     DataCacheEntry *entry, *cursor2;
1838 
1839     TRACE("(%p, %p)\n", iface, stg);
1840 
1841     IPersistStorage_HandsOffStorage( iface );
1842 
1843     LIST_FOR_EACH_ENTRY_SAFE( entry, cursor2, &This->cache_list, DataCacheEntry, entry )
1844         DataCacheEntry_Destroy( This, entry );
1845     This->clsid = CLSID_NULL;
1846 
1847     ReadClassStg( stg, &clsid );
1848     hr = create_automatic_entry( This, &clsid );
1849     if (FAILED( hr )) return hr;
1850 
1851     This->clsid = clsid;
1852 
1853     if (This->clsid_static)
1854     {
1855         hr = parse_contents_stream( This, stg );
1856         if (FAILED(hr)) hr = parse_pres_streams( This, stg );
1857     }
1858     else
1859         hr = parse_pres_streams( This, stg );
1860 
1861     if (SUCCEEDED( hr ))
1862     {
1863         This->dirty = FALSE;
1864         This->presentationStorage = stg;
1865         IStorage_AddRef( This->presentationStorage );
1866     }
1867 
1868     return hr;
1869 }
1870 
1871 /************************************************************************
1872  * DataCache_Save (IPersistStorage)
1873  *
1874  * Until we actually connect to a running object and retrieve new
1875  * information to it, we never have to save anything. However, it is
1876  * our responsibility to copy the information when saving to a new
1877  * storage.
1878  */
1879 static HRESULT WINAPI DataCache_Save(IPersistStorage* iface, IStorage *stg, BOOL same_as_load)
1880 {
1881     DataCache *This = impl_from_IPersistStorage(iface);
1882     DataCacheEntry *cache_entry;
1883     HRESULT hr = S_OK;
1884     int stream_number = 0;
1885 
1886     TRACE("(%p, %p, %d)\n", iface, stg, same_as_load);
1887 
1888     /* assign stream numbers to the cache entries */
1889     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1890     {
1891         if (cache_entry->save_stream_num != stream_number)
1892         {
1893             cache_entry->dirty = TRUE; /* needs to be written out again */
1894             cache_entry->save_stream_num = stream_number;
1895         }
1896         stream_number++;
1897     }
1898 
1899     /* write out the cache entries */
1900     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
1901     {
1902         if (!same_as_load || cache_entry->dirty)
1903         {
1904             hr = DataCacheEntry_Save(cache_entry, stg, same_as_load);
1905             if (FAILED(hr))
1906                 break;
1907 
1908             if (same_as_load) cache_entry->dirty = FALSE;
1909         }
1910     }
1911 
1912     if (same_as_load) This->dirty = FALSE;
1913     return hr;
1914 }
1915 
1916 /************************************************************************
1917  * DataCache_SaveCompleted (IPersistStorage)
1918  *
1919  * This method is called to tell the cache to release the storage
1920  * pointer it's currently holding.
1921  */
1922 static HRESULT WINAPI DataCache_SaveCompleted(
1923             IPersistStorage* iface,
1924 	    IStorage*        pStgNew)
1925 {
1926   TRACE("(%p, %p)\n", iface, pStgNew);
1927 
1928   if (pStgNew)
1929   {
1930     IPersistStorage_HandsOffStorage(iface);
1931 
1932     DataCache_Load(iface, pStgNew);
1933   }
1934 
1935   return S_OK;
1936 }
1937 
1938 /************************************************************************
1939  * DataCache_HandsOffStorage (IPersistStorage)
1940  *
1941  * This method is called to tell the cache to release the storage
1942  * pointer it's currently holding.
1943  */
1944 static HRESULT WINAPI DataCache_HandsOffStorage(
1945             IPersistStorage* iface)
1946 {
1947   DataCache *this = impl_from_IPersistStorage(iface);
1948 
1949   TRACE("(%p)\n", iface);
1950 
1951   if (this->presentationStorage != NULL)
1952   {
1953     IStorage_Release(this->presentationStorage);
1954     this->presentationStorage = NULL;
1955   }
1956 
1957   return S_OK;
1958 }
1959 
1960 /*********************************************************
1961  * Method implementation for the IViewObject2
1962  * part of the DataCache class.
1963  */
1964 
1965 /************************************************************************
1966  * DataCache_IViewObject2_QueryInterface (IUnknown)
1967  */
1968 static HRESULT WINAPI DataCache_IViewObject2_QueryInterface(
1969             IViewObject2* iface,
1970             REFIID           riid,
1971             void**           ppvObject)
1972 {
1973   DataCache *this = impl_from_IViewObject2(iface);
1974 
1975   return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
1976 }
1977 
1978 /************************************************************************
1979  * DataCache_IViewObject2_AddRef (IUnknown)
1980  */
1981 static ULONG WINAPI DataCache_IViewObject2_AddRef(
1982             IViewObject2* iface)
1983 {
1984   DataCache *this = impl_from_IViewObject2(iface);
1985 
1986   return IUnknown_AddRef(this->outer_unk);
1987 }
1988 
1989 /************************************************************************
1990  * DataCache_IViewObject2_Release (IUnknown)
1991  */
1992 static ULONG WINAPI DataCache_IViewObject2_Release(
1993             IViewObject2* iface)
1994 {
1995   DataCache *this = impl_from_IViewObject2(iface);
1996 
1997   return IUnknown_Release(this->outer_unk);
1998 }
1999 
2000 /************************************************************************
2001  * DataCache_Draw (IViewObject2)
2002  *
2003  * This method will draw the cached representation of the object
2004  * to the given device context.
2005  */
2006 static HRESULT WINAPI DataCache_Draw(
2007             IViewObject2*    iface,
2008 	    DWORD            dwDrawAspect,
2009 	    LONG             lindex,
2010 	    void*            pvAspect,
2011 	    DVTARGETDEVICE*  ptd,
2012 	    HDC              hdcTargetDev,
2013 	    HDC              hdcDraw,
2014 	    LPCRECTL         lprcBounds,
2015 	    LPCRECTL         lprcWBounds,
2016 	    BOOL  (CALLBACK *pfnContinue)(ULONG_PTR dwContinue),
2017 	    ULONG_PTR        dwContinue)
2018 {
2019   DataCache *This = impl_from_IViewObject2(iface);
2020   HRESULT                hres;
2021   DataCacheEntry        *cache_entry;
2022 
2023   TRACE("(%p, %x, %d, %p, %p, %p, %p, %p, %p, %lx)\n",
2024 	iface,
2025 	dwDrawAspect,
2026 	lindex,
2027 	pvAspect,
2028 	hdcTargetDev,
2029 	hdcDraw,
2030 	lprcBounds,
2031 	lprcWBounds,
2032 	pfnContinue,
2033 	dwContinue);
2034 
2035   if (lprcBounds==NULL)
2036     return E_INVALIDARG;
2037 
2038   LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2039   {
2040     /* FIXME: compare ptd too */
2041     if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
2042         (cache_entry->fmtetc.lindex != lindex))
2043       continue;
2044 
2045     /* if the data hasn't been loaded yet, do it now */
2046     if ((cache_entry->stgmedium.tymed == TYMED_NULL) && (cache_entry->load_stream_num != STREAM_NUMBER_NOT_SET))
2047     {
2048       hres = DataCacheEntry_LoadData(cache_entry, This->presentationStorage);
2049       if (FAILED(hres))
2050         continue;
2051     }
2052 
2053     /* no data */
2054     if (cache_entry->stgmedium.tymed == TYMED_NULL)
2055       continue;
2056 
2057     if (pfnContinue && !pfnContinue(dwContinue)) return E_ABORT;
2058 
2059     switch (cache_entry->fmtetc.cfFormat)
2060     {
2061       case CF_METAFILEPICT:
2062       {
2063         /*
2064          * We have to be careful not to modify the state of the
2065          * DC.
2066          */
2067         INT   prevMapMode;
2068         SIZE  oldWindowExt;
2069         SIZE  oldViewportExt;
2070         POINT oldViewportOrg;
2071         METAFILEPICT *mfpict;
2072 
2073         if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
2074             !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
2075           continue;
2076 
2077         prevMapMode = SetMapMode(hdcDraw, mfpict->mm);
2078 
2079         SetWindowExtEx(hdcDraw,
2080 		       mfpict->xExt,
2081 		       mfpict->yExt,
2082 		       &oldWindowExt);
2083 
2084         SetViewportExtEx(hdcDraw,
2085 		         lprcBounds->right - lprcBounds->left,
2086 		         lprcBounds->bottom - lprcBounds->top,
2087 		         &oldViewportExt);
2088 
2089         SetViewportOrgEx(hdcDraw,
2090 		         lprcBounds->left,
2091 		         lprcBounds->top,
2092 		         &oldViewportOrg);
2093 
2094         PlayMetaFile(hdcDraw, mfpict->hMF);
2095 
2096         SetWindowExtEx(hdcDraw,
2097 		       oldWindowExt.cx,
2098 		       oldWindowExt.cy,
2099 		       NULL);
2100 
2101         SetViewportExtEx(hdcDraw,
2102 		         oldViewportExt.cx,
2103 		         oldViewportExt.cy,
2104 		         NULL);
2105 
2106         SetViewportOrgEx(hdcDraw,
2107 		         oldViewportOrg.x,
2108 		         oldViewportOrg.y,
2109 		         NULL);
2110 
2111         SetMapMode(hdcDraw, prevMapMode);
2112 
2113         GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
2114 
2115         return S_OK;
2116       }
2117       case CF_DIB:
2118       {
2119           BITMAPINFO *info;
2120           BYTE *bits;
2121 
2122           if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
2123               !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
2124               continue;
2125 
2126           bits = (BYTE *) info + bitmap_info_size( info, DIB_RGB_COLORS );
2127           StretchDIBits( hdcDraw, lprcBounds->left, lprcBounds->top,
2128                          lprcBounds->right - lprcBounds->left, lprcBounds->bottom - lprcBounds->top,
2129                          0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
2130                          bits, info, DIB_RGB_COLORS, SRCCOPY );
2131 
2132           GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
2133           return S_OK;
2134       }
2135     }
2136   }
2137 
2138   WARN("no data could be found to be drawn\n");
2139 
2140   return OLE_E_BLANK;
2141 }
2142 
2143 static HRESULT WINAPI DataCache_GetColorSet(
2144             IViewObject2*   iface,
2145 	    DWORD           dwDrawAspect,
2146 	    LONG            lindex,
2147 	    void*           pvAspect,
2148 	    DVTARGETDEVICE* ptd,
2149 	    HDC             hicTargetDevice,
2150 	    LOGPALETTE**    ppColorSet)
2151 {
2152   FIXME("stub\n");
2153   return E_NOTIMPL;
2154 }
2155 
2156 static HRESULT WINAPI DataCache_Freeze(
2157             IViewObject2*   iface,
2158 	    DWORD           dwDrawAspect,
2159 	    LONG            lindex,
2160 	    void*           pvAspect,
2161 	    DWORD*          pdwFreeze)
2162 {
2163   FIXME("stub\n");
2164   return E_NOTIMPL;
2165 }
2166 
2167 static HRESULT WINAPI DataCache_Unfreeze(
2168             IViewObject2*   iface,
2169 	    DWORD           dwFreeze)
2170 {
2171   FIXME("stub\n");
2172   return E_NOTIMPL;
2173 }
2174 
2175 /************************************************************************
2176  * DataCache_SetAdvise (IViewObject2)
2177  *
2178  * This sets-up an advisory sink with the data cache. When the object's
2179  * view changes, this sink is called.
2180  */
2181 static HRESULT WINAPI DataCache_SetAdvise(
2182             IViewObject2*   iface,
2183 	    DWORD           aspects,
2184 	    DWORD           advf,
2185 	    IAdviseSink*    pAdvSink)
2186 {
2187   DataCache *this = impl_from_IViewObject2(iface);
2188 
2189   TRACE("(%p, %x, %x, %p)\n", iface, aspects, advf, pAdvSink);
2190 
2191   /*
2192    * A call to this function removes the previous sink
2193    */
2194   if (this->sinkInterface != NULL)
2195   {
2196     IAdviseSink_Release(this->sinkInterface);
2197     this->sinkInterface  = NULL;
2198     this->sinkAspects    = 0;
2199     this->sinkAdviseFlag = 0;
2200   }
2201 
2202   /*
2203    * Now, setup the new one.
2204    */
2205   if (pAdvSink!=NULL)
2206   {
2207     this->sinkInterface  = pAdvSink;
2208     this->sinkAspects    = aspects;
2209     this->sinkAdviseFlag = advf;
2210 
2211     IAdviseSink_AddRef(this->sinkInterface);
2212   }
2213 
2214   /*
2215    * When the ADVF_PRIMEFIRST flag is set, we have to advise the
2216    * sink immediately.
2217    */
2218   if (advf & ADVF_PRIMEFIRST)
2219   {
2220     DataCache_FireOnViewChange(this, aspects, -1);
2221   }
2222 
2223   return S_OK;
2224 }
2225 
2226 /************************************************************************
2227  * DataCache_GetAdvise (IViewObject2)
2228  *
2229  * This method queries the current state of the advise sink
2230  * installed on the data cache.
2231  */
2232 static HRESULT WINAPI DataCache_GetAdvise(
2233             IViewObject2*   iface,
2234 	    DWORD*          pAspects,
2235 	    DWORD*          pAdvf,
2236 	    IAdviseSink**   ppAdvSink)
2237 {
2238   DataCache *this = impl_from_IViewObject2(iface);
2239 
2240   TRACE("(%p, %p, %p, %p)\n", iface, pAspects, pAdvf, ppAdvSink);
2241 
2242   /*
2243    * Just copy all the requested values.
2244    */
2245   if (pAspects!=NULL)
2246     *pAspects = this->sinkAspects;
2247 
2248   if (pAdvf!=NULL)
2249     *pAdvf = this->sinkAdviseFlag;
2250 
2251   if (ppAdvSink!=NULL)
2252   {
2253     if (this->sinkInterface != NULL)
2254         IAdviseSink_QueryInterface(this->sinkInterface,
2255 			       &IID_IAdviseSink,
2256 			       (void**)ppAdvSink);
2257     else *ppAdvSink = NULL;
2258   }
2259 
2260   return S_OK;
2261 }
2262 
2263 /************************************************************************
2264  * DataCache_GetExtent (IViewObject2)
2265  *
2266  * This method retrieves the "natural" size of this cached object.
2267  */
2268 static HRESULT WINAPI DataCache_GetExtent(
2269             IViewObject2*   iface,
2270 	    DWORD           dwDrawAspect,
2271 	    LONG            lindex,
2272 	    DVTARGETDEVICE* ptd,
2273 	    LPSIZEL         lpsizel)
2274 {
2275   DataCache *This = impl_from_IViewObject2(iface);
2276   HRESULT                hres = E_FAIL;
2277   DataCacheEntry        *cache_entry;
2278 
2279   TRACE("(%p, %x, %d, %p, %p)\n",
2280 	iface, dwDrawAspect, lindex, ptd, lpsizel);
2281 
2282   if (lpsizel==NULL)
2283     return E_POINTER;
2284 
2285   lpsizel->cx = 0;
2286   lpsizel->cy = 0;
2287 
2288   if (lindex!=-1)
2289     FIXME("Unimplemented flag lindex = %d\n", lindex);
2290 
2291   /*
2292    * Right now, we support only the callback from
2293    * the default handler.
2294    */
2295   if (ptd!=NULL)
2296     FIXME("Unimplemented ptd = %p\n", ptd);
2297 
2298   LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2299   {
2300     /* FIXME: compare ptd too */
2301     if ((cache_entry->fmtetc.dwAspect != dwDrawAspect) ||
2302         (cache_entry->fmtetc.lindex != lindex))
2303       continue;
2304 
2305     /* if the data hasn't been loaded yet, do it now */
2306     if ((cache_entry->stgmedium.tymed == TYMED_NULL) && (cache_entry->load_stream_num != STREAM_NUMBER_NOT_SET))
2307     {
2308       hres = DataCacheEntry_LoadData(cache_entry, This->presentationStorage);
2309       if (FAILED(hres))
2310         continue;
2311     }
2312 
2313     /* no data */
2314     if (cache_entry->stgmedium.tymed == TYMED_NULL)
2315       continue;
2316 
2317 
2318     switch (cache_entry->fmtetc.cfFormat)
2319     {
2320       case CF_METAFILEPICT:
2321       {
2322           METAFILEPICT *mfpict;
2323 
2324           if ((cache_entry->stgmedium.tymed != TYMED_MFPICT) ||
2325               !((mfpict = GlobalLock(cache_entry->stgmedium.u.hMetaFilePict))))
2326             continue;
2327 
2328         lpsizel->cx = mfpict->xExt;
2329         lpsizel->cy = mfpict->yExt;
2330 
2331         GlobalUnlock(cache_entry->stgmedium.u.hMetaFilePict);
2332 
2333         return S_OK;
2334       }
2335       case CF_DIB:
2336       {
2337           BITMAPINFOHEADER *info;
2338           LONG x_pels_m, y_pels_m;
2339 
2340 
2341           if ((cache_entry->stgmedium.tymed != TYMED_HGLOBAL) ||
2342               !((info = GlobalLock( cache_entry->stgmedium.u.hGlobal ))))
2343               continue;
2344 
2345           x_pels_m = info->biXPelsPerMeter;
2346           y_pels_m = info->biYPelsPerMeter;
2347 
2348           /* Size in units of 0.01mm (ie. MM_HIMETRIC) */
2349           if (x_pels_m != 0 && y_pels_m != 0)
2350           {
2351               lpsizel->cx = info->biWidth  * 100000 / x_pels_m;
2352               lpsizel->cy = info->biHeight * 100000 / y_pels_m;
2353           }
2354           else
2355           {
2356               HDC hdc = GetDC( 0 );
2357               lpsizel->cx = info->biWidth  * 2540 / GetDeviceCaps( hdc, LOGPIXELSX );
2358               lpsizel->cy = info->biHeight * 2540 / GetDeviceCaps( hdc, LOGPIXELSY );
2359 
2360               ReleaseDC( 0, hdc );
2361           }
2362 
2363           GlobalUnlock( cache_entry->stgmedium.u.hGlobal );
2364 
2365           return S_OK;
2366       }
2367     }
2368   }
2369 
2370   WARN("no data could be found to get the extents from\n");
2371 
2372   /*
2373    * This method returns OLE_E_BLANK when it fails.
2374    */
2375   return OLE_E_BLANK;
2376 }
2377 
2378 
2379 /*********************************************************
2380  * Method implementation for the IOleCache2
2381  * part of the DataCache class.
2382  */
2383 
2384 /************************************************************************
2385  * DataCache_IOleCache2_QueryInterface (IUnknown)
2386  */
2387 static HRESULT WINAPI DataCache_IOleCache2_QueryInterface(
2388             IOleCache2*     iface,
2389             REFIID          riid,
2390             void**          ppvObject)
2391 {
2392   DataCache *this = impl_from_IOleCache2(iface);
2393 
2394   return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2395 }
2396 
2397 /************************************************************************
2398  * DataCache_IOleCache2_AddRef (IUnknown)
2399  */
2400 static ULONG WINAPI DataCache_IOleCache2_AddRef(
2401             IOleCache2*     iface)
2402 {
2403   DataCache *this = impl_from_IOleCache2(iface);
2404 
2405   return IUnknown_AddRef(this->outer_unk);
2406 }
2407 
2408 /************************************************************************
2409  * DataCache_IOleCache2_Release (IUnknown)
2410  */
2411 static ULONG WINAPI DataCache_IOleCache2_Release(
2412             IOleCache2*     iface)
2413 {
2414   DataCache *this = impl_from_IOleCache2(iface);
2415 
2416   return IUnknown_Release(this->outer_unk);
2417 }
2418 
2419 /*****************************************************************************
2420  * setup_sink
2421  *
2422  * Set up the sink connection to the running object.
2423  */
2424 static HRESULT setup_sink(DataCache *This, DataCacheEntry *cache_entry)
2425 {
2426     HRESULT hr = S_FALSE;
2427     DWORD flags;
2428 
2429     /* Clear the ADVFCACHE_* bits.  Native also sets the two highest bits for some reason. */
2430     flags = cache_entry->advise_flags & ~(ADVFCACHE_NOHANDLER | ADVFCACHE_FORCEBUILTIN | ADVFCACHE_ONSAVE);
2431 
2432     if(This->running_object)
2433         if(!(flags & ADVF_NODATA))
2434             hr = IDataObject_DAdvise(This->running_object, &cache_entry->fmtetc, flags,
2435                                      &This->IAdviseSink_iface, &cache_entry->sink_id);
2436     return hr;
2437 }
2438 
2439 static HRESULT WINAPI DataCache_Cache(
2440             IOleCache2*     iface,
2441 	    FORMATETC*      pformatetc,
2442 	    DWORD           advf,
2443 	    DWORD*          pdwConnection)
2444 {
2445     DataCache *This = impl_from_IOleCache2(iface);
2446     DataCacheEntry *cache_entry;
2447     HRESULT hr;
2448     FORMATETC fmt_cpy;
2449 
2450     TRACE("(%p, 0x%x, %p)\n", pformatetc, advf, pdwConnection);
2451 
2452     if (!pformatetc || !pdwConnection)
2453         return E_INVALIDARG;
2454 
2455     TRACE("pformatetc = %s\n", debugstr_formatetc(pformatetc));
2456 
2457     fmt_cpy = *pformatetc; /* No need for a deep copy */
2458     if (fmt_cpy.cfFormat == CF_BITMAP && fmt_cpy.tymed == TYMED_GDI)
2459     {
2460         fmt_cpy.cfFormat = CF_DIB;
2461         fmt_cpy.tymed = TYMED_HGLOBAL;
2462     }
2463 
2464     /* View caching DVASPECT_ICON gets converted to CF_METAFILEPICT */
2465     if (fmt_cpy.dwAspect == DVASPECT_ICON && fmt_cpy.cfFormat == 0)
2466     {
2467         fmt_cpy.cfFormat = CF_METAFILEPICT;
2468         fmt_cpy.tymed = TYMED_MFPICT;
2469     }
2470 
2471     *pdwConnection = 0;
2472 
2473     cache_entry = DataCache_GetEntryForFormatEtc(This, &fmt_cpy);
2474     if (cache_entry)
2475     {
2476         TRACE("found an existing cache entry\n");
2477         *pdwConnection = cache_entry->id;
2478         return CACHE_S_SAMECACHE;
2479     }
2480 
2481     if (This->clsid_static && fmt_cpy.dwAspect != DVASPECT_ICON) return DV_E_FORMATETC;
2482 
2483     hr = DataCache_CreateEntry(This, &fmt_cpy, advf, FALSE, &cache_entry);
2484 
2485     if (SUCCEEDED(hr))
2486     {
2487         *pdwConnection = cache_entry->id;
2488         setup_sink(This, cache_entry);
2489     }
2490 
2491     return hr;
2492 }
2493 
2494 static HRESULT WINAPI DataCache_Uncache(
2495 	    IOleCache2*     iface,
2496 	    DWORD           dwConnection)
2497 {
2498     DataCache *This = impl_from_IOleCache2(iface);
2499     DataCacheEntry *cache_entry;
2500 
2501     TRACE("(%d)\n", dwConnection);
2502 
2503     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2504         if (cache_entry->id == dwConnection)
2505         {
2506             DataCacheEntry_Destroy(This, cache_entry);
2507             return S_OK;
2508         }
2509 
2510     WARN("no connection found for %d\n", dwConnection);
2511 
2512     return OLE_E_NOCONNECTION;
2513 }
2514 
2515 static HRESULT WINAPI DataCache_EnumCache(IOleCache2 *iface,
2516                                           IEnumSTATDATA **enum_stat)
2517 {
2518     DataCache *This = impl_from_IOleCache2( iface );
2519     DataCacheEntry *cache_entry;
2520     int i = 0, count = 0;
2521     STATDATA *data;
2522     HRESULT hr;
2523 
2524     TRACE( "(%p, %p)\n", This, enum_stat );
2525 
2526     LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2527     {
2528         count++;
2529         if (cache_entry->fmtetc.cfFormat == CF_DIB)
2530             count++;
2531     }
2532 
2533     data = CoTaskMemAlloc( count * sizeof(*data) );
2534     if (!data) return E_OUTOFMEMORY;
2535 
2536     LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2537     {
2538         if (i == count) goto fail;
2539         hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2540         if (FAILED(hr)) goto fail;
2541         data[i].advf = cache_entry->advise_flags;
2542         data[i].pAdvSink = NULL;
2543         data[i].dwConnection = cache_entry->id;
2544         i++;
2545 
2546         if (cache_entry->fmtetc.cfFormat == CF_DIB)
2547         {
2548             if (i == count) goto fail;
2549             hr = copy_formatetc( &data[i].formatetc, &cache_entry->fmtetc );
2550             if (FAILED(hr)) goto fail;
2551             data[i].formatetc.cfFormat = CF_BITMAP;
2552             data[i].formatetc.tymed = TYMED_GDI;
2553             data[i].advf = cache_entry->advise_flags;
2554             data[i].pAdvSink = NULL;
2555             data[i].dwConnection = cache_entry->id;
2556             i++;
2557         }
2558     }
2559 
2560     hr = EnumSTATDATA_Construct( NULL, 0, i, data, FALSE, enum_stat );
2561     if (SUCCEEDED(hr)) return hr;
2562 
2563 fail:
2564     while (i--) CoTaskMemFree( data[i].formatetc.ptd );
2565     CoTaskMemFree( data );
2566     return hr;
2567 }
2568 
2569 static HRESULT WINAPI DataCache_InitCache( IOleCache2 *iface, IDataObject *data )
2570 {
2571     TRACE( "(%p %p)\n", iface, data );
2572     return IOleCache2_UpdateCache( iface, data, UPDFCACHE_ALLBUTNODATACACHE, NULL );
2573 }
2574 
2575 static HRESULT WINAPI DataCache_IOleCache2_SetData(
2576             IOleCache2*     iface,
2577 	    FORMATETC*      pformatetc,
2578 	    STGMEDIUM*      pmedium,
2579 	    BOOL            fRelease)
2580 {
2581     DataCache *This = impl_from_IOleCache2(iface);
2582     DataCacheEntry *cache_entry;
2583     HRESULT hr;
2584 
2585     TRACE("(%p, %p, %s)\n", pformatetc, pmedium, fRelease ? "TRUE" : "FALSE");
2586     TRACE("formatetc = %s\n", debugstr_formatetc(pformatetc));
2587 
2588     cache_entry = DataCache_GetEntryForFormatEtc(This, pformatetc);
2589     if (cache_entry)
2590     {
2591         hr = DataCacheEntry_SetData(cache_entry, pformatetc, pmedium, fRelease);
2592 
2593         if (SUCCEEDED(hr))
2594             DataCache_FireOnViewChange(This, cache_entry->fmtetc.dwAspect,
2595                                        cache_entry->fmtetc.lindex);
2596 
2597         return hr;
2598     }
2599     WARN("cache entry not found\n");
2600 
2601     return OLE_E_BLANK;
2602 }
2603 
2604 static BOOL entry_updatable( DataCacheEntry *entry, DWORD mode )
2605 {
2606     BOOL is_blank = entry->stgmedium.tymed == TYMED_NULL;
2607 
2608     if ((mode & UPDFCACHE_ONLYIFBLANK) && !is_blank) return FALSE;
2609 
2610     if ((mode & UPDFCACHE_NODATACACHE) && (entry->advise_flags & ADVF_NODATA)) return TRUE;
2611     if ((mode & UPDFCACHE_ONSAVECACHE) && (entry->advise_flags & ADVFCACHE_ONSAVE)) return TRUE;
2612     if ((mode & UPDFCACHE_ONSTOPCACHE) && (entry->advise_flags & ADVF_DATAONSTOP)) return TRUE;
2613     if ((mode & UPDFCACHE_NORMALCACHE) && (entry->advise_flags == 0)) return TRUE;
2614     if ((mode & UPDFCACHE_IFBLANK) && (is_blank && !(entry->advise_flags & ADVF_NODATA))) return TRUE;
2615 
2616     return FALSE;
2617 }
2618 
2619 static HRESULT WINAPI DataCache_UpdateCache( IOleCache2 *iface, IDataObject *data,
2620                                              DWORD mode, void *reserved )
2621 {
2622     DataCache *This = impl_from_IOleCache2(iface);
2623     DataCacheEntry *cache_entry;
2624     STGMEDIUM med;
2625     HRESULT hr = S_OK;
2626     CLIPFORMAT view_list[] = { CF_METAFILEPICT, CF_ENHMETAFILE, CF_DIB, CF_BITMAP };
2627     FORMATETC fmt;
2628     int i, slots = 0;
2629     BOOL done_one = FALSE;
2630 
2631     TRACE( "(%p %p %08x %p)\n", iface, data, mode, reserved );
2632 
2633     LIST_FOR_EACH_ENTRY( cache_entry, &This->cache_list, DataCacheEntry, entry )
2634     {
2635         slots++;
2636 
2637         if (!entry_updatable( cache_entry, mode ))
2638         {
2639             done_one = TRUE;
2640             continue;
2641         }
2642 
2643         fmt = cache_entry->fmtetc;
2644 
2645         if (fmt.cfFormat)
2646         {
2647             hr = IDataObject_GetData( data, &fmt, &med );
2648             if (hr != S_OK && fmt.cfFormat == CF_DIB)
2649             {
2650                 fmt.cfFormat = CF_BITMAP;
2651                 fmt.tymed = TYMED_GDI;
2652                 hr = IDataObject_GetData( data, &fmt, &med );
2653             }
2654             if (hr != S_OK && fmt.cfFormat == CF_ENHMETAFILE)
2655             {
2656                 fmt.cfFormat = CF_METAFILEPICT;
2657                 fmt.tymed = TYMED_MFPICT;
2658                 hr = IDataObject_GetData( data, &fmt, &med );
2659             }
2660             if (hr == S_OK)
2661             {
2662                 hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2663                 if (hr != S_OK) ReleaseStgMedium( &med );
2664                 else done_one = TRUE;
2665             }
2666         }
2667         else
2668         {
2669             for (i = 0; i < ARRAY_SIZE(view_list); i++)
2670             {
2671                 fmt.cfFormat = view_list[i];
2672                 fmt.tymed = tymed_from_cf( fmt.cfFormat );
2673                 hr = IDataObject_QueryGetData( data, &fmt );
2674                 if (hr == S_OK)
2675                 {
2676                     hr = IDataObject_GetData( data, &fmt, &med );
2677                     if (hr == S_OK)
2678                     {
2679                         if (fmt.cfFormat == CF_BITMAP)
2680                         {
2681                             cache_entry->fmtetc.cfFormat = CF_DIB;
2682                             cache_entry->fmtetc.tymed = TYMED_HGLOBAL;
2683                         }
2684                         else
2685                         {
2686                             cache_entry->fmtetc.cfFormat = fmt.cfFormat;
2687                             cache_entry->fmtetc.tymed = fmt.tymed;
2688                         }
2689                         hr = DataCacheEntry_SetData( cache_entry, &fmt, &med, TRUE );
2690                         if (hr != S_OK) ReleaseStgMedium( &med );
2691                         else done_one = TRUE;
2692                         break;
2693                     }
2694                 }
2695             }
2696         }
2697     }
2698 
2699     return (!slots || done_one) ? S_OK : CACHE_E_NOCACHE_UPDATED;
2700 }
2701 
2702 static HRESULT WINAPI DataCache_DiscardCache(
2703             IOleCache2*     iface,
2704 	    DWORD           dwDiscardOptions)
2705 {
2706     DataCache *This = impl_from_IOleCache2(iface);
2707     DataCacheEntry *cache_entry;
2708     HRESULT hr = S_OK;
2709 
2710     TRACE("(%d)\n", dwDiscardOptions);
2711 
2712     if (dwDiscardOptions == DISCARDCACHE_SAVEIFDIRTY)
2713         hr = DataCache_Save(&This->IPersistStorage_iface, This->presentationStorage, TRUE);
2714 
2715     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2716     {
2717         hr = DataCacheEntry_DiscardData(cache_entry);
2718         if (FAILED(hr))
2719             break;
2720     }
2721 
2722     return hr;
2723 }
2724 
2725 
2726 /*********************************************************
2727  * Method implementation for the IOleCacheControl
2728  * part of the DataCache class.
2729  */
2730 
2731 /************************************************************************
2732  * DataCache_IOleCacheControl_QueryInterface (IUnknown)
2733  */
2734 static HRESULT WINAPI DataCache_IOleCacheControl_QueryInterface(
2735             IOleCacheControl* iface,
2736             REFIID            riid,
2737             void**            ppvObject)
2738 {
2739   DataCache *this = impl_from_IOleCacheControl(iface);
2740 
2741   return IUnknown_QueryInterface(this->outer_unk, riid, ppvObject);
2742 }
2743 
2744 /************************************************************************
2745  * DataCache_IOleCacheControl_AddRef (IUnknown)
2746  */
2747 static ULONG WINAPI DataCache_IOleCacheControl_AddRef(
2748             IOleCacheControl* iface)
2749 {
2750   DataCache *this = impl_from_IOleCacheControl(iface);
2751 
2752   return IUnknown_AddRef(this->outer_unk);
2753 }
2754 
2755 /************************************************************************
2756  * DataCache_IOleCacheControl_Release (IUnknown)
2757  */
2758 static ULONG WINAPI DataCache_IOleCacheControl_Release(
2759             IOleCacheControl* iface)
2760 {
2761   DataCache *this = impl_from_IOleCacheControl(iface);
2762 
2763   return IUnknown_Release(this->outer_unk);
2764 }
2765 
2766 /************************************************************************
2767  * DataCache_OnRun (IOleCacheControl)
2768  */
2769 static HRESULT WINAPI DataCache_OnRun(IOleCacheControl* iface, IDataObject *data_obj)
2770 {
2771     DataCache *This = impl_from_IOleCacheControl(iface);
2772     DataCacheEntry *cache_entry;
2773 
2774     TRACE("(%p)->(%p)\n", iface, data_obj);
2775 
2776     if(This->running_object) return S_OK;
2777 
2778     /* No reference is taken on the data object */
2779     This->running_object = data_obj;
2780 
2781     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2782     {
2783         setup_sink(This, cache_entry);
2784     }
2785 
2786     return S_OK;
2787 }
2788 
2789 /************************************************************************
2790  * DataCache_OnStop (IOleCacheControl)
2791  */
2792 static HRESULT WINAPI DataCache_OnStop(IOleCacheControl* iface)
2793 {
2794     DataCache *This = impl_from_IOleCacheControl(iface);
2795     DataCacheEntry *cache_entry;
2796 
2797     TRACE("(%p)\n", iface);
2798 
2799     if(!This->running_object) return S_OK;
2800 
2801     LIST_FOR_EACH_ENTRY(cache_entry, &This->cache_list, DataCacheEntry, entry)
2802     {
2803         if(cache_entry->sink_id)
2804         {
2805             IDataObject_DUnadvise(This->running_object, cache_entry->sink_id);
2806             cache_entry->sink_id = 0;
2807         }
2808     }
2809 
2810     /* No ref taken in OnRun, so no Release call here */
2811     This->running_object = NULL;
2812     return S_OK;
2813 }
2814 
2815 /************************************************************************
2816  *              IAdviseSink methods.
2817  * This behaves as an internal object to the data cache.  QI'ing its ptr doesn't
2818  * give access to the cache's other interfaces.  We don't maintain a ref count,
2819  * the object exists as long as the cache is around.
2820  */
2821 static HRESULT WINAPI DataCache_IAdviseSink_QueryInterface(IAdviseSink *iface, REFIID iid, void **obj)
2822 {
2823     *obj = NULL;
2824     if (IsEqualIID(&IID_IUnknown, iid) ||
2825         IsEqualIID(&IID_IAdviseSink, iid))
2826     {
2827         *obj = iface;
2828     }
2829 
2830     if(*obj)
2831     {
2832         IAdviseSink_AddRef(iface);
2833         return S_OK;
2834     }
2835     return E_NOINTERFACE;
2836 }
2837 
2838 static ULONG WINAPI DataCache_IAdviseSink_AddRef(IAdviseSink *iface)
2839 {
2840     return 2;
2841 }
2842 
2843 static ULONG WINAPI DataCache_IAdviseSink_Release(IAdviseSink *iface)
2844 {
2845     return 1;
2846 }
2847 
2848 static void WINAPI DataCache_OnDataChange(IAdviseSink *iface, FORMATETC *fmt, STGMEDIUM *med)
2849 {
2850     DataCache *This = impl_from_IAdviseSink(iface);
2851     TRACE("(%p)->(%s, %p)\n", This, debugstr_formatetc(fmt), med);
2852     IOleCache2_SetData(&This->IOleCache2_iface, fmt, med, FALSE);
2853 }
2854 
2855 static void WINAPI DataCache_OnViewChange(IAdviseSink *iface, DWORD aspect, LONG index)
2856 {
2857     FIXME("stub\n");
2858 }
2859 
2860 static void WINAPI DataCache_OnRename(IAdviseSink *iface, IMoniker *mk)
2861 {
2862     FIXME("stub\n");
2863 }
2864 
2865 static void WINAPI DataCache_OnSave(IAdviseSink *iface)
2866 {
2867     FIXME("stub\n");
2868 }
2869 
2870 static void WINAPI DataCache_OnClose(IAdviseSink *iface)
2871 {
2872     FIXME("stub\n");
2873 }
2874 
2875 /*
2876  * Virtual function tables for the DataCache class.
2877  */
2878 static const IUnknownVtbl DataCache_NDIUnknown_VTable =
2879 {
2880   DataCache_NDIUnknown_QueryInterface,
2881   DataCache_NDIUnknown_AddRef,
2882   DataCache_NDIUnknown_Release
2883 };
2884 
2885 static const IDataObjectVtbl DataCache_IDataObject_VTable =
2886 {
2887   DataCache_IDataObject_QueryInterface,
2888   DataCache_IDataObject_AddRef,
2889   DataCache_IDataObject_Release,
2890   DataCache_GetData,
2891   DataCache_GetDataHere,
2892   DataCache_QueryGetData,
2893   DataCache_GetCanonicalFormatEtc,
2894   DataCache_IDataObject_SetData,
2895   DataCache_EnumFormatEtc,
2896   DataCache_DAdvise,
2897   DataCache_DUnadvise,
2898   DataCache_EnumDAdvise
2899 };
2900 
2901 static const IPersistStorageVtbl DataCache_IPersistStorage_VTable =
2902 {
2903   DataCache_IPersistStorage_QueryInterface,
2904   DataCache_IPersistStorage_AddRef,
2905   DataCache_IPersistStorage_Release,
2906   DataCache_GetClassID,
2907   DataCache_IsDirty,
2908   DataCache_InitNew,
2909   DataCache_Load,
2910   DataCache_Save,
2911   DataCache_SaveCompleted,
2912   DataCache_HandsOffStorage
2913 };
2914 
2915 static const IViewObject2Vtbl DataCache_IViewObject2_VTable =
2916 {
2917   DataCache_IViewObject2_QueryInterface,
2918   DataCache_IViewObject2_AddRef,
2919   DataCache_IViewObject2_Release,
2920   DataCache_Draw,
2921   DataCache_GetColorSet,
2922   DataCache_Freeze,
2923   DataCache_Unfreeze,
2924   DataCache_SetAdvise,
2925   DataCache_GetAdvise,
2926   DataCache_GetExtent
2927 };
2928 
2929 static const IOleCache2Vtbl DataCache_IOleCache2_VTable =
2930 {
2931   DataCache_IOleCache2_QueryInterface,
2932   DataCache_IOleCache2_AddRef,
2933   DataCache_IOleCache2_Release,
2934   DataCache_Cache,
2935   DataCache_Uncache,
2936   DataCache_EnumCache,
2937   DataCache_InitCache,
2938   DataCache_IOleCache2_SetData,
2939   DataCache_UpdateCache,
2940   DataCache_DiscardCache
2941 };
2942 
2943 static const IOleCacheControlVtbl DataCache_IOleCacheControl_VTable =
2944 {
2945   DataCache_IOleCacheControl_QueryInterface,
2946   DataCache_IOleCacheControl_AddRef,
2947   DataCache_IOleCacheControl_Release,
2948   DataCache_OnRun,
2949   DataCache_OnStop
2950 };
2951 
2952 static const IAdviseSinkVtbl DataCache_IAdviseSink_VTable =
2953 {
2954     DataCache_IAdviseSink_QueryInterface,
2955     DataCache_IAdviseSink_AddRef,
2956     DataCache_IAdviseSink_Release,
2957     DataCache_OnDataChange,
2958     DataCache_OnViewChange,
2959     DataCache_OnRename,
2960     DataCache_OnSave,
2961     DataCache_OnClose
2962 };
2963 
2964 /*********************************************************
2965  * Method implementation for DataCache class.
2966  */
2967 static DataCache* DataCache_Construct(
2968   REFCLSID  clsid,
2969   LPUNKNOWN pUnkOuter)
2970 {
2971   DataCache* newObject = 0;
2972 
2973   /*
2974    * Allocate space for the object.
2975    */
2976   newObject = HeapAlloc(GetProcessHeap(), 0, sizeof(DataCache));
2977 
2978   if (newObject==0)
2979     return newObject;
2980 
2981   /*
2982    * Initialize the virtual function table.
2983    */
2984   newObject->IDataObject_iface.lpVtbl = &DataCache_IDataObject_VTable;
2985   newObject->IUnknown_inner.lpVtbl = &DataCache_NDIUnknown_VTable;
2986   newObject->IPersistStorage_iface.lpVtbl = &DataCache_IPersistStorage_VTable;
2987   newObject->IViewObject2_iface.lpVtbl = &DataCache_IViewObject2_VTable;
2988   newObject->IOleCache2_iface.lpVtbl = &DataCache_IOleCache2_VTable;
2989   newObject->IOleCacheControl_iface.lpVtbl = &DataCache_IOleCacheControl_VTable;
2990   newObject->IAdviseSink_iface.lpVtbl = &DataCache_IAdviseSink_VTable;
2991   newObject->outer_unk = pUnkOuter ? pUnkOuter : &newObject->IUnknown_inner;
2992   newObject->ref = 1;
2993 
2994   /*
2995    * Initialize the other members of the structure.
2996    */
2997   newObject->sinkAspects = 0;
2998   newObject->sinkAdviseFlag = 0;
2999   newObject->sinkInterface = 0;
3000   newObject->clsid = CLSID_NULL;
3001   newObject->clsid_static = FALSE;
3002   newObject->presentationStorage = NULL;
3003   list_init(&newObject->cache_list);
3004   newObject->last_cache_id = 2;
3005   newObject->dirty = FALSE;
3006   newObject->running_object = NULL;
3007 
3008   create_automatic_entry( newObject, clsid );
3009   newObject->clsid = *clsid;
3010 
3011   return newObject;
3012 }
3013 
3014 /******************************************************************************
3015  *              CreateDataCache        [OLE32.@]
3016  *
3017  * Creates a data cache to allow an object to render one or more of its views,
3018  * whether running or not.
3019  *
3020  * PARAMS
3021  *  pUnkOuter [I] Outer unknown for the object.
3022  *  rclsid    [I]
3023  *  riid      [I] IID of interface to return.
3024  *  ppvObj    [O] Address where the data cache object will be stored on return.
3025  *
3026  * RETURNS
3027  *  Success: S_OK.
3028  *  Failure: HRESULT code.
3029  *
3030  * NOTES
3031  *  The following interfaces are supported by the returned data cache object:
3032  *  IOleCache, IOleCache2, IOleCacheControl, IPersistStorage, IDataObject,
3033  *  IViewObject and IViewObject2.
3034  */
3035 HRESULT WINAPI CreateDataCache(
3036   LPUNKNOWN pUnkOuter,
3037   REFCLSID  rclsid,
3038   REFIID    riid,
3039   LPVOID*   ppvObj)
3040 {
3041   DataCache* newCache = NULL;
3042   HRESULT    hr       = S_OK;
3043 
3044   TRACE("(%s, %p, %s, %p)\n", debugstr_guid(rclsid), pUnkOuter, debugstr_guid(riid), ppvObj);
3045 
3046   /*
3047    * Sanity check
3048    */
3049   if (ppvObj==0)
3050     return E_POINTER;
3051 
3052   *ppvObj = 0;
3053 
3054   /*
3055    * If this cache is constructed for aggregation, make sure
3056    * the caller is requesting the IUnknown interface.
3057    * This is necessary because it's the only time the non-delegating
3058    * IUnknown pointer can be returned to the outside.
3059    */
3060   if ( pUnkOuter && !IsEqualIID(&IID_IUnknown, riid) )
3061     return E_INVALIDARG;
3062 
3063   /*
3064    * Try to construct a new instance of the class.
3065    */
3066   newCache = DataCache_Construct(rclsid,
3067 				 pUnkOuter);
3068 
3069   if (newCache == 0)
3070     return E_OUTOFMEMORY;
3071 
3072   hr = IUnknown_QueryInterface(&newCache->IUnknown_inner, riid, ppvObj);
3073   IUnknown_Release(&newCache->IUnknown_inner);
3074 
3075   return hr;
3076 }
3077