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