2 * HGLOBAL Stream implementation
4 * This file contains the implementation of the stream interface
5 * for streams contained supported by an HGLOBAL pointer.
7 * Copyright 1999 Francis Beaudet
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #define NONAMELESSUNION
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
45 /****************************************************************************
46 * HGLOBALStreamImpl definition.
48 * This class implements the IStream interface and represents a stream
49 * supported by an HGLOBAL pointer.
53 IStream IStream_iface
;
56 /* support for the stream */
57 HGLOBAL supportHandle
;
59 /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
62 /* size of the stream */
63 ULARGE_INTEGER streamSize
;
65 /* current position of the cursor */
66 ULARGE_INTEGER currentPosition
;
69 static inline HGLOBALStreamImpl
*impl_from_IStream(IStream
*iface
)
71 return CONTAINING_RECORD(iface
, HGLOBALStreamImpl
, IStream_iface
);
74 static HRESULT WINAPI
HGLOBALStreamImpl_QueryInterface(
76 REFIID riid
, /* [in] */
77 void** ppvObject
) /* [iid_is][out] */
79 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
86 if (IsEqualIID(&IID_IUnknown
, riid
) ||
87 IsEqualIID(&IID_ISequentialStream
, riid
) ||
88 IsEqualIID(&IID_IStream
, riid
))
90 *ppvObject
= &This
->IStream_iface
;
96 IStream_AddRef(iface
);
101 static ULONG WINAPI
HGLOBALStreamImpl_AddRef(IStream
* iface
)
103 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
104 return InterlockedIncrement(&This
->ref
);
107 static ULONG WINAPI
HGLOBALStreamImpl_Release(
110 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
111 ULONG ref
= InterlockedDecrement(&This
->ref
);
115 if (This
->deleteOnRelease
)
117 GlobalFree(This
->supportHandle
);
118 This
->supportHandle
= NULL
;
121 HeapFree(GetProcessHeap(), 0, This
);
128 * This method is part of the ISequentialStream interface.
130 * If reads a block of information from the stream at the current
131 * position. It then moves the current position at the end of the
134 * See the documentation of ISequentialStream for more info.
136 static HRESULT WINAPI
HGLOBALStreamImpl_Read(
138 void* pv
, /* [length_is][size_is][out] */
140 ULONG
* pcbRead
) /* [out] */
142 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
145 ULONG bytesReadBuffer
;
146 ULONG bytesToReadFromBuffer
;
148 TRACE("(%p, %p, %d, %p)\n", iface
,
152 * If the caller is not interested in the number of bytes read,
153 * we use another buffer to avoid "if" statements in the code.
156 pcbRead
= &bytesReadBuffer
;
159 * Using the known size of the stream, calculate the number of bytes
160 * to read from the block chain
162 bytesToReadFromBuffer
= min( This
->streamSize
.u
.LowPart
- This
->currentPosition
.u
.LowPart
, cb
);
165 * Lock the buffer in position and copy the data.
167 supportBuffer
= GlobalLock(This
->supportHandle
);
170 WARN("read from invalid hglobal %p\n", This
->supportHandle
);
175 memcpy(pv
, (char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, bytesToReadFromBuffer
);
178 * Move the current position to the new position
180 This
->currentPosition
.u
.LowPart
+=bytesToReadFromBuffer
;
183 * Return the number of bytes read.
185 *pcbRead
= bytesToReadFromBuffer
;
190 GlobalUnlock(This
->supportHandle
);
193 * Always returns S_OK even if the end of the stream is reached before the
201 * This method is part of the ISequentialStream interface.
203 * It writes a block of information to the stream at the current
204 * position. It then moves the current position at the end of the
205 * written block. If the stream is too small to fit the block,
206 * the stream is grown to fit.
208 * See the documentation of ISequentialStream for more info.
210 static HRESULT WINAPI
HGLOBALStreamImpl_Write(
212 const void* pv
, /* [size_is][in] */
214 ULONG
* pcbWritten
) /* [out] */
216 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
219 ULARGE_INTEGER newSize
;
220 ULONG bytesWritten
= 0;
222 TRACE("(%p, %p, %d, %p)\n", iface
, pv
, cb
, pcbWritten
);
225 * If the caller is not interested in the number of bytes written,
226 * we use another buffer to avoid "if" statements in the code.
229 pcbWritten
= &bytesWritten
;
236 newSize
.u
.HighPart
= 0;
237 newSize
.u
.LowPart
= This
->currentPosition
.u
.LowPart
+ cb
;
240 * Verify if we need to grow the stream
242 if (newSize
.u
.LowPart
> This
->streamSize
.u
.LowPart
)
245 HRESULT hr
= IStream_SetSize(iface
, newSize
);
248 ERR("IStream_SetSize failed with error 0x%08x\n", hr
);
254 * Lock the buffer in position and copy the data.
256 supportBuffer
= GlobalLock(This
->supportHandle
);
259 WARN("write to invalid hglobal %p\n", This
->supportHandle
);
263 memcpy((char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, pv
, cb
);
266 * Move the current position to the new position
268 This
->currentPosition
.u
.LowPart
+=cb
;
273 GlobalUnlock(This
->supportHandle
);
277 * Return the number of bytes read.
285 * This method is part of the IStream interface.
287 * It will move the current stream pointer according to the parameters
290 * See the documentation of IStream for more info.
292 static HRESULT WINAPI
HGLOBALStreamImpl_Seek(
294 LARGE_INTEGER dlibMove
, /* [in] */
295 DWORD dwOrigin
, /* [in] */
296 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
298 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
300 ULARGE_INTEGER newPosition
= This
->currentPosition
;
303 TRACE("(%p, %x%08x, %d, %p)\n", iface
, dlibMove
.u
.HighPart
,
304 dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
307 * The file pointer is moved depending on the given "function"
312 case STREAM_SEEK_SET
:
313 newPosition
.u
.HighPart
= 0;
314 newPosition
.u
.LowPart
= 0;
316 case STREAM_SEEK_CUR
:
318 case STREAM_SEEK_END
:
319 newPosition
= This
->streamSize
;
322 hr
= STG_E_SEEKERROR
;
327 * Move the actual file pointer
328 * If the file pointer ends-up after the end of the stream, the next Write operation will
329 * make the file larger. This is how it is documented.
331 newPosition
.u
.HighPart
= 0;
332 newPosition
.u
.LowPart
+= dlibMove
.QuadPart
;
334 if (dlibMove
.u
.LowPart
>= 0x80000000 &&
335 newPosition
.u
.LowPart
>= dlibMove
.u
.LowPart
)
337 /* We tried to seek backwards and went past the start. */
338 hr
= STG_E_SEEKERROR
;
342 This
->currentPosition
= newPosition
;
345 if (plibNewPosition
) *plibNewPosition
= This
->currentPosition
;
351 * This method is part of the IStream interface.
353 * It will change the size of a stream.
355 * TODO: Switch from small blocks to big blocks and vice versa.
357 * See the documentation of IStream for more info.
359 static HRESULT WINAPI
HGLOBALStreamImpl_SetSize(
361 ULARGE_INTEGER libNewSize
) /* [in] */
363 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
364 HGLOBAL supportHandle
;
366 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
369 * HighPart is ignored as shown in tests
372 if (This
->streamSize
.u
.LowPart
== libNewSize
.u
.LowPart
)
376 * Re allocate the HGlobal to fit the new size of the stream.
378 supportHandle
= GlobalReAlloc(This
->supportHandle
, libNewSize
.u
.LowPart
, 0);
380 if (supportHandle
== 0)
381 return E_OUTOFMEMORY
;
383 This
->supportHandle
= supportHandle
;
384 This
->streamSize
.u
.LowPart
= libNewSize
.u
.LowPart
;
390 * This method is part of the IStream interface.
392 * It will copy the 'cb' Bytes to 'pstm' IStream.
394 * See the documentation of IStream for more info.
396 static HRESULT WINAPI
HGLOBALStreamImpl_CopyTo(
398 IStream
* pstm
, /* [unique][in] */
399 ULARGE_INTEGER cb
, /* [in] */
400 ULARGE_INTEGER
* pcbRead
, /* [out] */
401 ULARGE_INTEGER
* pcbWritten
) /* [out] */
405 ULONG bytesRead
, bytesWritten
, copySize
;
406 ULARGE_INTEGER totalBytesRead
;
407 ULARGE_INTEGER totalBytesWritten
;
409 TRACE("(%p, %p, %d, %p, %p)\n", iface
, pstm
,
410 cb
.u
.LowPart
, pcbRead
, pcbWritten
);
413 return STG_E_INVALIDPOINTER
;
415 totalBytesRead
.QuadPart
= 0;
416 totalBytesWritten
.QuadPart
= 0;
418 while ( cb
.QuadPart
> 0 )
420 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
421 copySize
= sizeof(tmpBuffer
);
423 copySize
= cb
.u
.LowPart
;
425 hr
= IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
429 totalBytesRead
.QuadPart
+= bytesRead
;
433 hr
= IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
437 totalBytesWritten
.QuadPart
+= bytesWritten
;
440 if (bytesRead
!=copySize
)
443 cb
.QuadPart
-= bytesRead
;
446 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
447 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
453 * This method is part of the IStream interface.
455 * For streams supported by HGLOBALS, this function does nothing.
456 * This is what the documentation tells us.
458 * See the documentation of IStream for more info.
460 static HRESULT WINAPI
HGLOBALStreamImpl_Commit(
462 DWORD grfCommitFlags
) /* [in] */
468 * This method is part of the IStream interface.
470 * For streams supported by HGLOBALS, this function does nothing.
471 * This is what the documentation tells us.
473 * See the documentation of IStream for more info.
475 static HRESULT WINAPI
HGLOBALStreamImpl_Revert(
482 * This method is part of the IStream interface.
484 * For streams supported by HGLOBALS, this function does nothing.
485 * This is what the documentation tells us.
487 * See the documentation of IStream for more info.
489 static HRESULT WINAPI
HGLOBALStreamImpl_LockRegion(
491 ULARGE_INTEGER libOffset
, /* [in] */
492 ULARGE_INTEGER cb
, /* [in] */
493 DWORD dwLockType
) /* [in] */
495 return STG_E_INVALIDFUNCTION
;
499 * This method is part of the IStream interface.
501 * For streams supported by HGLOBALS, this function does nothing.
502 * This is what the documentation tells us.
504 * See the documentation of IStream for more info.
506 static HRESULT WINAPI
HGLOBALStreamImpl_UnlockRegion(
508 ULARGE_INTEGER libOffset
, /* [in] */
509 ULARGE_INTEGER cb
, /* [in] */
510 DWORD dwLockType
) /* [in] */
516 * This method is part of the IStream interface.
518 * This method returns information about the current
521 * See the documentation of IStream for more info.
523 static HRESULT WINAPI
HGLOBALStreamImpl_Stat(
525 STATSTG
* pstatstg
, /* [out] */
526 DWORD grfStatFlag
) /* [in] */
528 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
530 memset(pstatstg
, 0, sizeof(STATSTG
));
532 pstatstg
->pwcsName
= NULL
;
533 pstatstg
->type
= STGTY_STREAM
;
534 pstatstg
->cbSize
= This
->streamSize
;
539 static HRESULT WINAPI
HGLOBALStreamImpl_Clone(
541 IStream
** ppstm
) /* [out] */
543 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
544 ULARGE_INTEGER dummy
;
545 LARGE_INTEGER offset
;
548 TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface
,This
->deleteOnRelease
,(long)This
->currentPosition
.QuadPart
);
549 hr
= CreateStreamOnHGlobal(This
->supportHandle
, FALSE
, ppstm
);
552 offset
.QuadPart
= (LONGLONG
)This
->currentPosition
.QuadPart
;
553 IStream_Seek(*ppstm
, offset
, STREAM_SEEK_SET
, &dummy
);
557 static const IStreamVtbl HGLOBALStreamImplVtbl
=
559 HGLOBALStreamImpl_QueryInterface
,
560 HGLOBALStreamImpl_AddRef
,
561 HGLOBALStreamImpl_Release
,
562 HGLOBALStreamImpl_Read
,
563 HGLOBALStreamImpl_Write
,
564 HGLOBALStreamImpl_Seek
,
565 HGLOBALStreamImpl_SetSize
,
566 HGLOBALStreamImpl_CopyTo
,
567 HGLOBALStreamImpl_Commit
,
568 HGLOBALStreamImpl_Revert
,
569 HGLOBALStreamImpl_LockRegion
,
570 HGLOBALStreamImpl_UnlockRegion
,
571 HGLOBALStreamImpl_Stat
,
572 HGLOBALStreamImpl_Clone
575 /***********************************************************************
576 * CreateStreamOnHGlobal [OLE32.@]
578 HRESULT WINAPI
CreateStreamOnHGlobal(
580 BOOL fDeleteOnRelease
,
583 HGLOBALStreamImpl
* This
;
588 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl
));
589 if (!This
) return E_OUTOFMEMORY
;
591 This
->IStream_iface
.lpVtbl
= &HGLOBALStreamImplVtbl
;
594 /* initialize the support */
595 This
->supportHandle
= hGlobal
;
596 This
->deleteOnRelease
= fDeleteOnRelease
;
598 /* allocate a handle if one is not supplied */
599 if (!This
->supportHandle
)
600 This
->supportHandle
= GlobalAlloc(GMEM_MOVEABLE
|GMEM_NODISCARD
|GMEM_SHARE
, 0);
602 /* start at the beginning */
603 This
->currentPosition
.u
.HighPart
= 0;
604 This
->currentPosition
.u
.LowPart
= 0;
606 /* initialize the size of the stream to the size of the handle */
607 This
->streamSize
.u
.HighPart
= 0;
608 This
->streamSize
.u
.LowPart
= GlobalSize(This
->supportHandle
);
610 *ppstm
= &This
->IStream_iface
;
615 /***********************************************************************
616 * GetHGlobalFromStream [OLE32.@]
618 HRESULT WINAPI
GetHGlobalFromStream(IStream
* pstm
, HGLOBAL
* phglobal
)
620 HGLOBALStreamImpl
* pStream
;
625 pStream
= (HGLOBALStreamImpl
*) pstm
;
628 * Verify that the stream object was created with CreateStreamOnHGlobal.
630 if (pStream
->IStream_iface
.lpVtbl
== &HGLOBALStreamImplVtbl
)
631 *phglobal
= pStream
->supportHandle
;