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