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