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