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