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
33 #define NONAMELESSUNION
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
47 /****************************************************************************
48 * HGLOBALStreamImpl definition.
50 * This class implements the IStream interface and represents a stream
51 * supported by an HGLOBAL pointer.
55 IStream IStream_iface
;
58 /* support for the stream */
59 HGLOBAL supportHandle
;
61 /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
64 /* size of the stream */
65 ULARGE_INTEGER streamSize
;
67 /* current position of the cursor */
68 ULARGE_INTEGER currentPosition
;
71 static inline HGLOBALStreamImpl
*impl_from_IStream(IStream
*iface
)
73 return CONTAINING_RECORD(iface
, HGLOBALStreamImpl
, IStream_iface
);
76 static HRESULT WINAPI
HGLOBALStreamImpl_QueryInterface(
78 REFIID riid
, /* [in] */
79 void** ppvObject
) /* [iid_is][out] */
81 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
88 if (IsEqualIID(&IID_IUnknown
, riid
) ||
89 IsEqualIID(&IID_ISequentialStream
, riid
) ||
90 IsEqualIID(&IID_IStream
, riid
))
92 *ppvObject
= &This
->IStream_iface
;
98 IStream_AddRef(iface
);
103 static ULONG WINAPI
HGLOBALStreamImpl_AddRef(IStream
* iface
)
105 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
106 return InterlockedIncrement(&This
->ref
);
109 static ULONG WINAPI
HGLOBALStreamImpl_Release(
112 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
113 ULONG ref
= InterlockedDecrement(&This
->ref
);
117 if (This
->deleteOnRelease
)
119 GlobalFree(This
->supportHandle
);
120 This
->supportHandle
= NULL
;
123 HeapFree(GetProcessHeap(), 0, This
);
130 * This method is part of the ISequentialStream interface.
132 * If reads a block of information from the stream at the current
133 * position. It then moves the current position at the end of the
136 * See the documentation of ISequentialStream for more info.
138 static HRESULT WINAPI
HGLOBALStreamImpl_Read(
140 void* pv
, /* [length_is][size_is][out] */
142 ULONG
* pcbRead
) /* [out] */
144 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
147 ULONG bytesReadBuffer
;
148 ULONG bytesToReadFromBuffer
;
150 TRACE("(%p, %p, %d, %p)\n", iface
,
154 * If the caller is not interested in the number of bytes read,
155 * we use another buffer to avoid "if" statements in the code.
158 pcbRead
= &bytesReadBuffer
;
161 * Using the known size of the stream, calculate the number of bytes
162 * to read from the block chain
164 bytesToReadFromBuffer
= min( This
->streamSize
.u
.LowPart
- This
->currentPosition
.u
.LowPart
, cb
);
167 * Lock the buffer in position and copy the data.
169 supportBuffer
= GlobalLock(This
->supportHandle
);
172 WARN("read from invalid hglobal %p\n", This
->supportHandle
);
177 memcpy(pv
, (char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, bytesToReadFromBuffer
);
180 * Move the current position to the new position
182 This
->currentPosition
.u
.LowPart
+=bytesToReadFromBuffer
;
185 * Return the number of bytes read.
187 *pcbRead
= bytesToReadFromBuffer
;
192 GlobalUnlock(This
->supportHandle
);
195 * Always returns S_OK even if the end of the stream is reached before the
203 * This method is part of the ISequentialStream interface.
205 * It writes a block of information to the stream at the current
206 * position. It then moves the current position at the end of the
207 * written block. If the stream is too small to fit the block,
208 * the stream is grown to fit.
210 * See the documentation of ISequentialStream for more info.
212 static HRESULT WINAPI
HGLOBALStreamImpl_Write(
214 const void* pv
, /* [size_is][in] */
216 ULONG
* pcbWritten
) /* [out] */
218 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
221 ULARGE_INTEGER newSize
;
222 ULONG bytesWritten
= 0;
224 TRACE("(%p, %p, %d, %p)\n", iface
, pv
, cb
, pcbWritten
);
227 * If the caller is not interested in the number of bytes written,
228 * we use another buffer to avoid "if" statements in the code.
231 pcbWritten
= &bytesWritten
;
238 newSize
.u
.HighPart
= 0;
239 newSize
.u
.LowPart
= This
->currentPosition
.u
.LowPart
+ cb
;
242 * Verify if we need to grow the stream
244 if (newSize
.u
.LowPart
> This
->streamSize
.u
.LowPart
)
247 HRESULT hr
= IStream_SetSize(iface
, newSize
);
250 ERR("IStream_SetSize failed with error 0x%08x\n", hr
);
256 * Lock the buffer in position and copy the data.
258 supportBuffer
= GlobalLock(This
->supportHandle
);
261 WARN("write to invalid hglobal %p\n", This
->supportHandle
);
265 memcpy((char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, pv
, cb
);
268 * Move the current position to the new position
270 This
->currentPosition
.u
.LowPart
+=cb
;
275 GlobalUnlock(This
->supportHandle
);
279 * Return the number of bytes read.
287 * This method is part of the IStream interface.
289 * It will move the current stream pointer according to the parameters
292 * See the documentation of IStream for more info.
294 static HRESULT WINAPI
HGLOBALStreamImpl_Seek(
296 LARGE_INTEGER dlibMove
, /* [in] */
297 DWORD dwOrigin
, /* [in] */
298 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
300 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
302 ULARGE_INTEGER newPosition
= This
->currentPosition
;
305 TRACE("(%p, %x%08x, %d, %p)\n", iface
, dlibMove
.u
.HighPart
,
306 dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
309 * The file pointer is moved depending on the given "function"
314 case STREAM_SEEK_SET
:
315 newPosition
.u
.HighPart
= 0;
316 newPosition
.u
.LowPart
= 0;
318 case STREAM_SEEK_CUR
:
320 case STREAM_SEEK_END
:
321 newPosition
= This
->streamSize
;
324 hr
= STG_E_SEEKERROR
;
329 * Move the actual file pointer
330 * If the file pointer ends-up after the end of the stream, the next Write operation will
331 * make the file larger. This is how it is documented.
333 newPosition
.u
.HighPart
= 0;
334 newPosition
.u
.LowPart
+= dlibMove
.QuadPart
;
336 if (dlibMove
.u
.LowPart
>= 0x80000000 &&
337 newPosition
.u
.LowPart
>= dlibMove
.u
.LowPart
)
339 /* We tried to seek backwards and went past the start. */
340 hr
= STG_E_SEEKERROR
;
344 This
->currentPosition
= newPosition
;
347 if (plibNewPosition
) *plibNewPosition
= This
->currentPosition
;
353 * This method is part of the IStream interface.
355 * It will change the size of a stream.
357 * TODO: Switch from small blocks to big blocks and vice versa.
359 * See the documentation of IStream for more info.
361 static HRESULT WINAPI
HGLOBALStreamImpl_SetSize(
363 ULARGE_INTEGER libNewSize
) /* [in] */
365 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
366 HGLOBAL supportHandle
;
368 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
371 * HighPart is ignored as shown in tests
374 if (This
->streamSize
.u
.LowPart
== libNewSize
.u
.LowPart
)
378 * Re allocate the HGlobal to fit the new size of the stream.
380 supportHandle
= GlobalReAlloc(This
->supportHandle
, libNewSize
.u
.LowPart
, 0);
382 if (supportHandle
== 0)
383 return E_OUTOFMEMORY
;
385 This
->supportHandle
= supportHandle
;
386 This
->streamSize
.u
.LowPart
= libNewSize
.u
.LowPart
;
392 * This method is part of the IStream interface.
394 * It will copy the 'cb' Bytes to 'pstm' IStream.
396 * See the documentation of IStream for more info.
398 static HRESULT WINAPI
HGLOBALStreamImpl_CopyTo(
400 IStream
* pstm
, /* [unique][in] */
401 ULARGE_INTEGER cb
, /* [in] */
402 ULARGE_INTEGER
* pcbRead
, /* [out] */
403 ULARGE_INTEGER
* pcbWritten
) /* [out] */
407 ULONG bytesRead
, bytesWritten
, copySize
;
408 ULARGE_INTEGER totalBytesRead
;
409 ULARGE_INTEGER totalBytesWritten
;
411 TRACE("(%p, %p, %d, %p, %p)\n", iface
, pstm
,
412 cb
.u
.LowPart
, pcbRead
, pcbWritten
);
415 return STG_E_INVALIDPOINTER
;
417 totalBytesRead
.QuadPart
= 0;
418 totalBytesWritten
.QuadPart
= 0;
420 while ( cb
.QuadPart
> 0 )
422 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
423 copySize
= sizeof(tmpBuffer
);
425 copySize
= cb
.u
.LowPart
;
427 hr
= IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
431 totalBytesRead
.QuadPart
+= bytesRead
;
435 hr
= IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
439 totalBytesWritten
.QuadPart
+= bytesWritten
;
442 if (bytesRead
!=copySize
)
445 cb
.QuadPart
-= bytesRead
;
448 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
449 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
455 * This method is part of the IStream interface.
457 * For streams supported by HGLOBALS, this function does nothing.
458 * This is what the documentation tells us.
460 * See the documentation of IStream for more info.
462 static HRESULT WINAPI
HGLOBALStreamImpl_Commit(
464 DWORD grfCommitFlags
) /* [in] */
470 * This method is part of the IStream interface.
472 * For streams supported by HGLOBALS, this function does nothing.
473 * This is what the documentation tells us.
475 * See the documentation of IStream for more info.
477 static HRESULT WINAPI
HGLOBALStreamImpl_Revert(
484 * This method is part of the IStream interface.
486 * For streams supported by HGLOBALS, this function does nothing.
487 * This is what the documentation tells us.
489 * See the documentation of IStream for more info.
491 static HRESULT WINAPI
HGLOBALStreamImpl_LockRegion(
493 ULARGE_INTEGER libOffset
, /* [in] */
494 ULARGE_INTEGER cb
, /* [in] */
495 DWORD dwLockType
) /* [in] */
497 return STG_E_INVALIDFUNCTION
;
501 * This method is part of the IStream interface.
503 * For streams supported by HGLOBALS, this function does nothing.
504 * This is what the documentation tells us.
506 * See the documentation of IStream for more info.
508 static HRESULT WINAPI
HGLOBALStreamImpl_UnlockRegion(
510 ULARGE_INTEGER libOffset
, /* [in] */
511 ULARGE_INTEGER cb
, /* [in] */
512 DWORD dwLockType
) /* [in] */
518 * This method is part of the IStream interface.
520 * This method returns information about the current
523 * See the documentation of IStream for more info.
525 static HRESULT WINAPI
HGLOBALStreamImpl_Stat(
527 STATSTG
* pstatstg
, /* [out] */
528 DWORD grfStatFlag
) /* [in] */
530 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
532 memset(pstatstg
, 0, sizeof(STATSTG
));
534 pstatstg
->pwcsName
= NULL
;
535 pstatstg
->type
= STGTY_STREAM
;
536 pstatstg
->cbSize
= This
->streamSize
;
541 static HRESULT WINAPI
HGLOBALStreamImpl_Clone(
543 IStream
** ppstm
) /* [out] */
545 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
546 ULARGE_INTEGER dummy
;
547 LARGE_INTEGER offset
;
550 TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface
,This
->deleteOnRelease
,(long)This
->currentPosition
.QuadPart
);
551 hr
= CreateStreamOnHGlobal(This
->supportHandle
, FALSE
, ppstm
);
554 offset
.QuadPart
= (LONGLONG
)This
->currentPosition
.QuadPart
;
555 IStream_Seek(*ppstm
, offset
, STREAM_SEEK_SET
, &dummy
);
559 static const IStreamVtbl HGLOBALStreamImplVtbl
=
561 HGLOBALStreamImpl_QueryInterface
,
562 HGLOBALStreamImpl_AddRef
,
563 HGLOBALStreamImpl_Release
,
564 HGLOBALStreamImpl_Read
,
565 HGLOBALStreamImpl_Write
,
566 HGLOBALStreamImpl_Seek
,
567 HGLOBALStreamImpl_SetSize
,
568 HGLOBALStreamImpl_CopyTo
,
569 HGLOBALStreamImpl_Commit
,
570 HGLOBALStreamImpl_Revert
,
571 HGLOBALStreamImpl_LockRegion
,
572 HGLOBALStreamImpl_UnlockRegion
,
573 HGLOBALStreamImpl_Stat
,
574 HGLOBALStreamImpl_Clone
577 /***********************************************************************
578 * CreateStreamOnHGlobal [OLE32.@]
580 HRESULT WINAPI
CreateStreamOnHGlobal(
582 BOOL fDeleteOnRelease
,
585 HGLOBALStreamImpl
* This
;
590 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl
));
591 if (!This
) return E_OUTOFMEMORY
;
593 This
->IStream_iface
.lpVtbl
= &HGLOBALStreamImplVtbl
;
596 /* initialize the support */
597 This
->supportHandle
= hGlobal
;
598 This
->deleteOnRelease
= fDeleteOnRelease
;
600 /* allocate a handle if one is not supplied */
601 if (!This
->supportHandle
)
602 This
->supportHandle
= GlobalAlloc(GMEM_MOVEABLE
|GMEM_NODISCARD
|GMEM_SHARE
, 0);
604 /* start at the beginning */
605 This
->currentPosition
.u
.HighPart
= 0;
606 This
->currentPosition
.u
.LowPart
= 0;
608 /* initialize the size of the stream to the size of the handle */
609 This
->streamSize
.u
.HighPart
= 0;
610 This
->streamSize
.u
.LowPart
= GlobalSize(This
->supportHandle
);
612 *ppstm
= &This
->IStream_iface
;
617 /***********************************************************************
618 * GetHGlobalFromStream [OLE32.@]
620 HRESULT WINAPI
GetHGlobalFromStream(IStream
* pstm
, HGLOBAL
* phglobal
)
622 HGLOBALStreamImpl
* pStream
;
627 pStream
= (HGLOBALStreamImpl
*) pstm
;
630 * Verify that the stream object was created with CreateStreamOnHGlobal.
632 if (pStream
->IStream_iface
.lpVtbl
== &HGLOBALStreamImplVtbl
)
633 *phglobal
= pStream
->supportHandle
;