1 /* 2 * HGLOBAL Stream implementation 3 * 4 * This file contains the implementation of the stream interface 5 * for streams contained supported by an HGLOBAL pointer. 6 * 7 * Copyright 1999 Francis Beaudet 8 * Copyright 2016 Dmitry Timoshkov 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23 */ 24 25 #include "precomp.h" 26 27 WINE_DEFAULT_DEBUG_CHANNEL(hglobalstream); 28 29 struct handle_wrapper 30 { 31 LONG ref; 32 HGLOBAL hglobal; 33 ULONG size; 34 BOOL delete_on_release; 35 CRITICAL_SECTION lock; 36 }; 37 38 static void handle_addref(struct handle_wrapper *handle) 39 { 40 InterlockedIncrement(&handle->ref); 41 } 42 43 static void handle_release(struct handle_wrapper *handle) 44 { 45 ULONG ref = InterlockedDecrement(&handle->ref); 46 47 if (!ref) 48 { 49 if (handle->delete_on_release) 50 { 51 GlobalFree(handle->hglobal); 52 handle->hglobal = NULL; 53 } 54 55 handle->lock.DebugInfo->Spare[0] = 0; 56 DeleteCriticalSection(&handle->lock); 57 HeapFree(GetProcessHeap(), 0, handle); 58 } 59 } 60 61 static ULONG handle_read(struct handle_wrapper *handle, ULONG *pos, void *dest, ULONG len) 62 { 63 void *source; 64 65 EnterCriticalSection(&handle->lock); 66 67 if (*pos < handle->size) 68 len = min(handle->size - *pos, len); 69 else 70 len = 0; 71 72 source = GlobalLock(handle->hglobal); 73 if (source) 74 { 75 memcpy(dest, (char *)source + *pos, len); 76 *pos += len; 77 GlobalUnlock(handle->hglobal); 78 } 79 else 80 { 81 WARN("read from invalid hglobal %p\n", handle->hglobal); 82 len = 0; 83 } 84 85 LeaveCriticalSection(&handle->lock); 86 return len; 87 } 88 89 static ULONG handle_write(struct handle_wrapper *handle, ULONG *pos, const void *source, ULONG len) 90 { 91 void *dest; 92 93 if (!len) 94 return 0; 95 96 EnterCriticalSection(&handle->lock); 97 98 if (*pos + len > handle->size) 99 { 100 HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, *pos + len, GMEM_MOVEABLE); 101 if (hglobal) 102 { 103 handle->hglobal = hglobal; 104 handle->size = *pos + len; 105 } 106 else 107 { 108 len = 0; 109 goto done; 110 } 111 } 112 113 dest = GlobalLock(handle->hglobal); 114 if (dest) 115 { 116 memcpy((char *)dest + *pos, source, len); 117 *pos += len; 118 GlobalUnlock(handle->hglobal); 119 } 120 else 121 { 122 WARN("write to invalid hglobal %p\n", handle->hglobal); 123 /* len = 0; */ 124 } 125 126 done: 127 LeaveCriticalSection(&handle->lock); 128 return len; 129 } 130 131 static HGLOBAL handle_gethglobal(struct handle_wrapper *handle) 132 { 133 return handle->hglobal; 134 } 135 136 static HRESULT handle_setsize(struct handle_wrapper *handle, ULONG size) 137 { 138 HRESULT hr = S_OK; 139 140 EnterCriticalSection(&handle->lock); 141 142 if (handle->size != size) 143 { 144 HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, size, GMEM_MOVEABLE); 145 if (hglobal) 146 { 147 handle->hglobal = hglobal; 148 handle->size = size; 149 } 150 else 151 hr = E_OUTOFMEMORY; 152 } 153 154 LeaveCriticalSection(&handle->lock); 155 return hr; 156 } 157 158 static ULONG handle_getsize(struct handle_wrapper *handle) 159 { 160 return handle->size; 161 } 162 163 static struct handle_wrapper *handle_create(HGLOBAL hglobal, BOOL delete_on_release) 164 { 165 struct handle_wrapper *handle; 166 167 handle = HeapAlloc(GetProcessHeap(), 0, sizeof(*handle)); 168 if (handle) 169 { 170 handle->ref = 1; 171 handle->hglobal = hglobal; 172 handle->size = GlobalSize(hglobal); 173 handle->delete_on_release = delete_on_release; 174 InitializeCriticalSection(&handle->lock); 175 handle->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": handle_wrapper.lock"); 176 } 177 return handle; 178 } 179 180 /**************************************************************************** 181 * HGLOBALStreamImpl definition. 182 * 183 * This class implements the IStream interface and represents a stream 184 * supported by an HGLOBAL pointer. 185 */ 186 typedef struct 187 { 188 IStream IStream_iface; 189 LONG ref; 190 191 struct handle_wrapper *handle; 192 193 /* current position of the cursor */ 194 ULARGE_INTEGER currentPosition; 195 } HGLOBALStreamImpl; 196 197 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface) 198 { 199 return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface); 200 } 201 202 static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface( 203 IStream* iface, 204 REFIID riid, /* [in] */ 205 void** ppvObject) /* [iid_is][out] */ 206 { 207 HGLOBALStreamImpl* This = impl_from_IStream(iface); 208 209 if (ppvObject==0) 210 return E_INVALIDARG; 211 212 *ppvObject = 0; 213 214 if (IsEqualIID(&IID_IUnknown, riid) || 215 IsEqualIID(&IID_ISequentialStream, riid) || 216 IsEqualIID(&IID_IStream, riid)) 217 { 218 *ppvObject = &This->IStream_iface; 219 } 220 221 if ((*ppvObject)==0) 222 return E_NOINTERFACE; 223 224 IStream_AddRef(iface); 225 226 return S_OK; 227 } 228 229 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface) 230 { 231 HGLOBALStreamImpl* This = impl_from_IStream(iface); 232 return InterlockedIncrement(&This->ref); 233 } 234 235 static ULONG WINAPI HGLOBALStreamImpl_Release( 236 IStream* iface) 237 { 238 HGLOBALStreamImpl* This= impl_from_IStream(iface); 239 ULONG ref = InterlockedDecrement(&This->ref); 240 241 if (!ref) 242 { 243 handle_release(This->handle); 244 HeapFree(GetProcessHeap(), 0, This); 245 } 246 247 return ref; 248 } 249 250 /*** 251 * This method is part of the ISequentialStream interface. 252 * 253 * If reads a block of information from the stream at the current 254 * position. It then moves the current position at the end of the 255 * read block 256 * 257 * See the documentation of ISequentialStream for more info. 258 */ 259 static HRESULT WINAPI HGLOBALStreamImpl_Read( 260 IStream* iface, 261 void* pv, /* [length_is][size_is][out] */ 262 ULONG cb, /* [in] */ 263 ULONG* pcbRead) /* [out] */ 264 { 265 HGLOBALStreamImpl* This = impl_from_IStream(iface); 266 ULONG num_bytes; 267 268 TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbRead); 269 270 num_bytes = handle_read(This->handle, &This->currentPosition.u.LowPart, pv, cb); 271 if (pcbRead) *pcbRead = num_bytes; 272 273 return S_OK; 274 } 275 276 /*** 277 * This method is part of the ISequentialStream interface. 278 * 279 * It writes a block of information to the stream at the current 280 * position. It then moves the current position at the end of the 281 * written block. If the stream is too small to fit the block, 282 * the stream is grown to fit. 283 * 284 * See the documentation of ISequentialStream for more info. 285 */ 286 static HRESULT WINAPI HGLOBALStreamImpl_Write( 287 IStream* iface, 288 const void* pv, /* [size_is][in] */ 289 ULONG cb, /* [in] */ 290 ULONG* pcbWritten) /* [out] */ 291 { 292 HGLOBALStreamImpl* This = impl_from_IStream(iface); 293 ULONG num_bytes; 294 295 TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten); 296 297 num_bytes = handle_write(This->handle, &This->currentPosition.u.LowPart, pv, cb); 298 if (pcbWritten) *pcbWritten = num_bytes; 299 300 return (num_bytes < cb) ? E_OUTOFMEMORY : S_OK; 301 } 302 303 /*** 304 * This method is part of the IStream interface. 305 * 306 * It will move the current stream pointer according to the parameters 307 * given. 308 * 309 * See the documentation of IStream for more info. 310 */ 311 static HRESULT WINAPI HGLOBALStreamImpl_Seek( 312 IStream* iface, 313 LARGE_INTEGER dlibMove, /* [in] */ 314 DWORD dwOrigin, /* [in] */ 315 ULARGE_INTEGER* plibNewPosition) /* [out] */ 316 { 317 HGLOBALStreamImpl* This = impl_from_IStream(iface); 318 319 ULARGE_INTEGER newPosition = This->currentPosition; 320 HRESULT hr = S_OK; 321 322 TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart, 323 dlibMove.u.LowPart, dwOrigin, plibNewPosition); 324 325 /* 326 * The file pointer is moved depending on the given "function" 327 * parameter. 328 */ 329 switch (dwOrigin) 330 { 331 case STREAM_SEEK_SET: 332 newPosition.u.HighPart = 0; 333 newPosition.u.LowPart = 0; 334 break; 335 case STREAM_SEEK_CUR: 336 break; 337 case STREAM_SEEK_END: 338 newPosition.QuadPart = handle_getsize(This->handle); 339 break; 340 default: 341 hr = STG_E_SEEKERROR; 342 goto end; 343 } 344 345 /* 346 * Move the actual file pointer 347 * If the file pointer ends-up after the end of the stream, the next Write operation will 348 * make the file larger. This is how it is documented. 349 */ 350 newPosition.u.HighPart = 0; 351 newPosition.u.LowPart += dlibMove.QuadPart; 352 353 if (dlibMove.u.LowPart >= 0x80000000 && 354 newPosition.u.LowPart >= dlibMove.u.LowPart) 355 { 356 /* We tried to seek backwards and went past the start. */ 357 hr = STG_E_SEEKERROR; 358 goto end; 359 } 360 361 This->currentPosition = newPosition; 362 363 end: 364 if (plibNewPosition) *plibNewPosition = This->currentPosition; 365 366 return hr; 367 } 368 369 /*** 370 * This method is part of the IStream interface. 371 * 372 * It will change the size of a stream. 373 * 374 * TODO: Switch from small blocks to big blocks and vice versa. 375 * 376 * See the documentation of IStream for more info. 377 */ 378 static HRESULT WINAPI HGLOBALStreamImpl_SetSize( 379 IStream* iface, 380 ULARGE_INTEGER libNewSize) /* [in] */ 381 { 382 HGLOBALStreamImpl* This = impl_from_IStream(iface); 383 384 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart); 385 386 /* 387 * HighPart is ignored as shown in tests 388 */ 389 return handle_setsize(This->handle, libNewSize.u.LowPart); 390 } 391 392 /*** 393 * This method is part of the IStream interface. 394 * 395 * It will copy the 'cb' Bytes to 'pstm' IStream. 396 * 397 * See the documentation of IStream for more info. 398 */ 399 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo( 400 IStream* iface, 401 IStream* pstm, /* [unique][in] */ 402 ULARGE_INTEGER cb, /* [in] */ 403 ULARGE_INTEGER* pcbRead, /* [out] */ 404 ULARGE_INTEGER* pcbWritten) /* [out] */ 405 { 406 HRESULT hr = S_OK; 407 BYTE tmpBuffer[128]; 408 ULONG bytesRead, bytesWritten, copySize; 409 ULARGE_INTEGER totalBytesRead; 410 ULARGE_INTEGER totalBytesWritten; 411 412 TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm, 413 cb.u.LowPart, pcbRead, pcbWritten); 414 415 if ( pstm == 0 ) 416 return STG_E_INVALIDPOINTER; 417 418 totalBytesRead.QuadPart = 0; 419 totalBytesWritten.QuadPart = 0; 420 421 while ( cb.QuadPart > 0 ) 422 { 423 if ( cb.QuadPart >= sizeof(tmpBuffer) ) 424 copySize = sizeof(tmpBuffer); 425 else 426 copySize = cb.u.LowPart; 427 428 hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead); 429 if (FAILED(hr)) 430 break; 431 432 totalBytesRead.QuadPart += bytesRead; 433 434 if (bytesRead) 435 { 436 hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); 437 if (FAILED(hr)) 438 break; 439 440 totalBytesWritten.QuadPart += bytesWritten; 441 } 442 443 if (bytesRead!=copySize) 444 cb.QuadPart = 0; 445 else 446 cb.QuadPart -= bytesRead; 447 } 448 449 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; 450 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; 451 452 return hr; 453 } 454 455 /*** 456 * This method is part of the IStream interface. 457 * 458 * For streams supported by HGLOBALS, this function does nothing. 459 * This is what the documentation tells us. 460 * 461 * See the documentation of IStream for more info. 462 */ 463 static HRESULT WINAPI HGLOBALStreamImpl_Commit( 464 IStream* iface, 465 DWORD grfCommitFlags) /* [in] */ 466 { 467 return S_OK; 468 } 469 470 /*** 471 * This method is part of the IStream interface. 472 * 473 * For streams supported by HGLOBALS, this function does nothing. 474 * This is what the documentation tells us. 475 * 476 * See the documentation of IStream for more info. 477 */ 478 static HRESULT WINAPI HGLOBALStreamImpl_Revert( 479 IStream* iface) 480 { 481 return S_OK; 482 } 483 484 /*** 485 * This method is part of the IStream interface. 486 * 487 * For streams supported by HGLOBALS, this function does nothing. 488 * This is what the documentation tells us. 489 * 490 * See the documentation of IStream for more info. 491 */ 492 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion( 493 IStream* iface, 494 ULARGE_INTEGER libOffset, /* [in] */ 495 ULARGE_INTEGER cb, /* [in] */ 496 DWORD dwLockType) /* [in] */ 497 { 498 return STG_E_INVALIDFUNCTION; 499 } 500 501 /* 502 * This method is part of the IStream interface. 503 * 504 * For streams supported by HGLOBALS, this function does nothing. 505 * This is what the documentation tells us. 506 * 507 * See the documentation of IStream for more info. 508 */ 509 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion( 510 IStream* iface, 511 ULARGE_INTEGER libOffset, /* [in] */ 512 ULARGE_INTEGER cb, /* [in] */ 513 DWORD dwLockType) /* [in] */ 514 { 515 return S_OK; 516 } 517 518 /*** 519 * This method is part of the IStream interface. 520 * 521 * This method returns information about the current 522 * stream. 523 * 524 * See the documentation of IStream for more info. 525 */ 526 static HRESULT WINAPI HGLOBALStreamImpl_Stat( 527 IStream* iface, 528 STATSTG* pstatstg, /* [out] */ 529 DWORD grfStatFlag) /* [in] */ 530 { 531 HGLOBALStreamImpl* This = impl_from_IStream(iface); 532 533 memset(pstatstg, 0, sizeof(STATSTG)); 534 535 pstatstg->pwcsName = NULL; 536 pstatstg->type = STGTY_STREAM; 537 pstatstg->cbSize.QuadPart = handle_getsize(This->handle); 538 539 return S_OK; 540 } 541 542 static const IStreamVtbl HGLOBALStreamImplVtbl; 543 544 static HGLOBALStreamImpl *HGLOBALStreamImpl_Create(void) 545 { 546 HGLOBALStreamImpl *This; 547 548 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); 549 if (This) 550 { 551 This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl; 552 This->ref = 1; 553 } 554 return This; 555 } 556 557 static HRESULT WINAPI HGLOBALStreamImpl_Clone( 558 IStream* iface, 559 IStream** ppstm) /* [out] */ 560 { 561 HGLOBALStreamImpl* This = impl_from_IStream(iface); 562 HGLOBALStreamImpl* clone; 563 ULARGE_INTEGER dummy; 564 LARGE_INTEGER offset; 565 566 if (!ppstm) return E_INVALIDARG; 567 568 *ppstm = NULL; 569 570 TRACE(" Cloning %p (seek position=%d)\n", iface, This->currentPosition.u.LowPart); 571 572 clone = HGLOBALStreamImpl_Create(); 573 if (!clone) return E_OUTOFMEMORY; 574 575 *ppstm = &clone->IStream_iface; 576 577 handle_addref(This->handle); 578 clone->handle = This->handle; 579 580 offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart; 581 IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy); 582 return S_OK; 583 } 584 585 static const IStreamVtbl HGLOBALStreamImplVtbl = 586 { 587 HGLOBALStreamImpl_QueryInterface, 588 HGLOBALStreamImpl_AddRef, 589 HGLOBALStreamImpl_Release, 590 HGLOBALStreamImpl_Read, 591 HGLOBALStreamImpl_Write, 592 HGLOBALStreamImpl_Seek, 593 HGLOBALStreamImpl_SetSize, 594 HGLOBALStreamImpl_CopyTo, 595 HGLOBALStreamImpl_Commit, 596 HGLOBALStreamImpl_Revert, 597 HGLOBALStreamImpl_LockRegion, 598 HGLOBALStreamImpl_UnlockRegion, 599 HGLOBALStreamImpl_Stat, 600 HGLOBALStreamImpl_Clone 601 }; 602 603 /*********************************************************************** 604 * CreateStreamOnHGlobal [OLE32.@] 605 */ 606 HRESULT WINAPI CreateStreamOnHGlobal( 607 HGLOBAL hGlobal, 608 BOOL fDeleteOnRelease, 609 LPSTREAM* ppstm) 610 { 611 HGLOBALStreamImpl* This; 612 613 if (!ppstm) 614 return E_INVALIDARG; 615 616 This = HGLOBALStreamImpl_Create(); 617 if (!This) return E_OUTOFMEMORY; 618 619 /* allocate a handle if one is not supplied */ 620 if (!hGlobal) 621 hGlobal = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0); 622 623 This->handle = handle_create(hGlobal, fDeleteOnRelease); 624 625 /* start at the beginning */ 626 This->currentPosition.u.HighPart = 0; 627 This->currentPosition.u.LowPart = 0; 628 629 *ppstm = &This->IStream_iface; 630 631 return S_OK; 632 } 633 634 /*********************************************************************** 635 * GetHGlobalFromStream [OLE32.@] 636 */ 637 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal) 638 { 639 HGLOBALStreamImpl* pStream; 640 641 if (!pstm || !phglobal) 642 return E_INVALIDARG; 643 644 pStream = impl_from_IStream(pstm); 645 646 /* 647 * Verify that the stream object was created with CreateStreamOnHGlobal. 648 */ 649 if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl) 650 *phglobal = handle_gethglobal(pStream->handle); 651 else 652 { 653 *phglobal = 0; 654 return E_INVALIDARG; 655 } 656 657 return S_OK; 658 } 659