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