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