[SHELL32] Fix Control_RunDLLW (#5400)
[reactos.git] / dll / win32 / ole32 / datacache.c
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 }