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