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
24 #define WIN32_NO_STATUS
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
41 //#include "winuser.h"
42 //#include "objbase.h"
44 //#include "winerror.h"
45 //#include "winternl.h"
47 #include <wine/debug.h>
49 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
51 /****************************************************************************
52 * HGLOBALStreamImpl definition.
54 * This class implements the IStream interface and represents a stream
55 * supported by an HGLOBAL pointer.
59 IStream IStream_iface
;
62 /* support for the stream */
63 HGLOBAL supportHandle
;
65 /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
68 /* size of the stream */
69 ULARGE_INTEGER streamSize
;
71 /* current position of the cursor */
72 ULARGE_INTEGER currentPosition
;
75 static inline HGLOBALStreamImpl
*impl_from_IStream(IStream
*iface
)
77 return CONTAINING_RECORD(iface
, HGLOBALStreamImpl
, IStream_iface
);
80 static HRESULT WINAPI
HGLOBALStreamImpl_QueryInterface(
82 REFIID riid
, /* [in] */
83 void** ppvObject
) /* [iid_is][out] */
85 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
92 if (IsEqualIID(&IID_IUnknown
, riid
) ||
93 IsEqualIID(&IID_ISequentialStream
, riid
) ||
94 IsEqualIID(&IID_IStream
, riid
))
100 return E_NOINTERFACE
;
102 IStream_AddRef(iface
);
107 static ULONG WINAPI
HGLOBALStreamImpl_AddRef(IStream
* iface
)
109 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
110 return InterlockedIncrement(&This
->ref
);
113 static ULONG WINAPI
HGLOBALStreamImpl_Release(
116 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
117 ULONG ref
= InterlockedDecrement(&This
->ref
);
121 if (This
->deleteOnRelease
)
123 GlobalFree(This
->supportHandle
);
124 This
->supportHandle
= NULL
;
127 HeapFree(GetProcessHeap(), 0, This
);
134 * This method is part of the ISequentialStream interface.
136 * If reads a block of information from the stream at the current
137 * position. It then moves the current position at the end of the
140 * See the documentation of ISequentialStream for more info.
142 static HRESULT WINAPI
HGLOBALStreamImpl_Read(
144 void* pv
, /* [length_is][size_is][out] */
146 ULONG
* pcbRead
) /* [out] */
148 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
151 ULONG bytesReadBuffer
;
152 ULONG bytesToReadFromBuffer
;
154 TRACE("(%p, %p, %d, %p)\n", iface
,
158 * If the caller is not interested in the number of bytes read,
159 * we use another buffer to avoid "if" statements in the code.
162 pcbRead
= &bytesReadBuffer
;
165 * Using the known size of the stream, calculate the number of bytes
166 * to read from the block chain
168 bytesToReadFromBuffer
= min( This
->streamSize
.u
.LowPart
- This
->currentPosition
.u
.LowPart
, cb
);
171 * Lock the buffer in position and copy the data.
173 supportBuffer
= GlobalLock(This
->supportHandle
);
176 WARN("read from invalid hglobal %p\n", This
->supportHandle
);
181 memcpy(pv
, (char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, bytesToReadFromBuffer
);
184 * Move the current position to the new position
186 This
->currentPosition
.u
.LowPart
+=bytesToReadFromBuffer
;
189 * Return the number of bytes read.
191 *pcbRead
= bytesToReadFromBuffer
;
196 GlobalUnlock(This
->supportHandle
);
199 * Always returns S_OK even if the end of the stream is reached before the
207 * This method is part of the ISequentialStream interface.
209 * It writes a block of information to the stream at the current
210 * position. It then moves the current position at the end of the
211 * written block. If the stream is too small to fit the block,
212 * the stream is grown to fit.
214 * See the documentation of ISequentialStream for more info.
216 static HRESULT WINAPI
HGLOBALStreamImpl_Write(
218 const void* pv
, /* [size_is][in] */
220 ULONG
* pcbWritten
) /* [out] */
222 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
225 ULARGE_INTEGER newSize
;
226 ULONG bytesWritten
= 0;
228 TRACE("(%p, %p, %d, %p)\n", iface
, pv
, cb
, pcbWritten
);
231 * If the caller is not interested in the number of bytes written,
232 * we use another buffer to avoid "if" statements in the code.
235 pcbWritten
= &bytesWritten
;
242 newSize
.u
.HighPart
= 0;
243 newSize
.u
.LowPart
= This
->currentPosition
.u
.LowPart
+ cb
;
246 * Verify if we need to grow the stream
248 if (newSize
.u
.LowPart
> This
->streamSize
.u
.LowPart
)
251 HRESULT hr
= IStream_SetSize(iface
, newSize
);
254 ERR("IStream_SetSize failed with error 0x%08x\n", hr
);
260 * Lock the buffer in position and copy the data.
262 supportBuffer
= GlobalLock(This
->supportHandle
);
265 WARN("write to invalid hglobal %p\n", This
->supportHandle
);
269 memcpy((char *) supportBuffer
+This
->currentPosition
.u
.LowPart
, pv
, cb
);
272 * Move the current position to the new position
274 This
->currentPosition
.u
.LowPart
+=cb
;
279 GlobalUnlock(This
->supportHandle
);
283 * Return the number of bytes read.
291 * This method is part of the IStream interface.
293 * It will move the current stream pointer according to the parameters
296 * See the documentation of IStream for more info.
298 static HRESULT WINAPI
HGLOBALStreamImpl_Seek(
300 LARGE_INTEGER dlibMove
, /* [in] */
301 DWORD dwOrigin
, /* [in] */
302 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
304 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
306 ULARGE_INTEGER newPosition
= This
->currentPosition
;
309 TRACE("(%p, %x%08x, %d, %p)\n", iface
, dlibMove
.u
.HighPart
,
310 dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
313 * The file pointer is moved depending on the given "function"
318 case STREAM_SEEK_SET
:
319 newPosition
.u
.HighPart
= 0;
320 newPosition
.u
.LowPart
= 0;
322 case STREAM_SEEK_CUR
:
324 case STREAM_SEEK_END
:
325 newPosition
= This
->streamSize
;
328 hr
= STG_E_SEEKERROR
;
333 * Move the actual file pointer
334 * If the file pointer ends-up after the end of the stream, the next Write operation will
335 * make the file larger. This is how it is documented.
337 newPosition
.u
.HighPart
= 0;
338 newPosition
.u
.LowPart
+= dlibMove
.QuadPart
;
340 if (dlibMove
.u
.LowPart
>= 0x80000000 &&
341 newPosition
.u
.LowPart
>= dlibMove
.u
.LowPart
)
343 /* We tried to seek backwards and went past the start. */
344 hr
= STG_E_SEEKERROR
;
348 This
->currentPosition
= newPosition
;
351 if (plibNewPosition
) *plibNewPosition
= This
->currentPosition
;
357 * This method is part of the IStream interface.
359 * It will change the size of a stream.
361 * TODO: Switch from small blocks to big blocks and vice versa.
363 * See the documentation of IStream for more info.
365 static HRESULT WINAPI
HGLOBALStreamImpl_SetSize(
367 ULARGE_INTEGER libNewSize
) /* [in] */
369 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
370 HGLOBAL supportHandle
;
372 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
375 * HighPart is ignored as shown in tests
378 if (This
->streamSize
.u
.LowPart
== libNewSize
.u
.LowPart
)
382 * Re allocate the HGlobal to fit the new size of the stream.
384 supportHandle
= GlobalReAlloc(This
->supportHandle
, libNewSize
.u
.LowPart
, 0);
386 if (supportHandle
== 0)
387 return E_OUTOFMEMORY
;
389 This
->supportHandle
= supportHandle
;
390 This
->streamSize
.u
.LowPart
= libNewSize
.u
.LowPart
;
396 * This method is part of the IStream interface.
398 * It will copy the 'cb' Bytes to 'pstm' IStream.
400 * See the documentation of IStream for more info.
402 static HRESULT WINAPI
HGLOBALStreamImpl_CopyTo(
404 IStream
* pstm
, /* [unique][in] */
405 ULARGE_INTEGER cb
, /* [in] */
406 ULARGE_INTEGER
* pcbRead
, /* [out] */
407 ULARGE_INTEGER
* pcbWritten
) /* [out] */
411 ULONG bytesRead
, bytesWritten
, copySize
;
412 ULARGE_INTEGER totalBytesRead
;
413 ULARGE_INTEGER totalBytesWritten
;
415 TRACE("(%p, %p, %d, %p, %p)\n", iface
, pstm
,
416 cb
.u
.LowPart
, pcbRead
, pcbWritten
);
419 return STG_E_INVALIDPOINTER
;
421 totalBytesRead
.QuadPart
= 0;
422 totalBytesWritten
.QuadPart
= 0;
424 while ( cb
.QuadPart
> 0 )
426 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
427 copySize
= sizeof(tmpBuffer
);
429 copySize
= cb
.u
.LowPart
;
431 hr
= IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
435 totalBytesRead
.QuadPart
+= bytesRead
;
439 hr
= IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
443 totalBytesWritten
.QuadPart
+= bytesWritten
;
446 if (bytesRead
!=copySize
)
449 cb
.QuadPart
-= bytesRead
;
452 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
453 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
459 * This method is part of the IStream interface.
461 * For streams supported by HGLOBALS, this function does nothing.
462 * This is what the documentation tells us.
464 * See the documentation of IStream for more info.
466 static HRESULT WINAPI
HGLOBALStreamImpl_Commit(
468 DWORD grfCommitFlags
) /* [in] */
474 * This method is part of the IStream interface.
476 * For streams supported by HGLOBALS, this function does nothing.
477 * This is what the documentation tells us.
479 * See the documentation of IStream for more info.
481 static HRESULT WINAPI
HGLOBALStreamImpl_Revert(
488 * This method is part of the IStream interface.
490 * For streams supported by HGLOBALS, this function does nothing.
491 * This is what the documentation tells us.
493 * See the documentation of IStream for more info.
495 static HRESULT WINAPI
HGLOBALStreamImpl_LockRegion(
497 ULARGE_INTEGER libOffset
, /* [in] */
498 ULARGE_INTEGER cb
, /* [in] */
499 DWORD dwLockType
) /* [in] */
501 return STG_E_INVALIDFUNCTION
;
505 * This method is part of the IStream interface.
507 * For streams supported by HGLOBALS, this function does nothing.
508 * This is what the documentation tells us.
510 * See the documentation of IStream for more info.
512 static HRESULT WINAPI
HGLOBALStreamImpl_UnlockRegion(
514 ULARGE_INTEGER libOffset
, /* [in] */
515 ULARGE_INTEGER cb
, /* [in] */
516 DWORD dwLockType
) /* [in] */
522 * This method is part of the IStream interface.
524 * This method returns information about the current
527 * See the documentation of IStream for more info.
529 static HRESULT WINAPI
HGLOBALStreamImpl_Stat(
531 STATSTG
* pstatstg
, /* [out] */
532 DWORD grfStatFlag
) /* [in] */
534 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
536 memset(pstatstg
, 0, sizeof(STATSTG
));
538 pstatstg
->pwcsName
= NULL
;
539 pstatstg
->type
= STGTY_STREAM
;
540 pstatstg
->cbSize
= This
->streamSize
;
545 static HRESULT WINAPI
HGLOBALStreamImpl_Clone(
547 IStream
** ppstm
) /* [out] */
549 HGLOBALStreamImpl
* This
= impl_from_IStream(iface
);
550 ULARGE_INTEGER dummy
;
551 LARGE_INTEGER offset
;
554 TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface
,This
->deleteOnRelease
,(long)This
->currentPosition
.QuadPart
);
555 hr
= CreateStreamOnHGlobal(This
->supportHandle
, FALSE
, ppstm
);
558 offset
.QuadPart
= (LONGLONG
)This
->currentPosition
.QuadPart
;
559 IStream_Seek(*ppstm
, offset
, STREAM_SEEK_SET
, &dummy
);
563 static const IStreamVtbl HGLOBALStreamImplVtbl
=
565 HGLOBALStreamImpl_QueryInterface
,
566 HGLOBALStreamImpl_AddRef
,
567 HGLOBALStreamImpl_Release
,
568 HGLOBALStreamImpl_Read
,
569 HGLOBALStreamImpl_Write
,
570 HGLOBALStreamImpl_Seek
,
571 HGLOBALStreamImpl_SetSize
,
572 HGLOBALStreamImpl_CopyTo
,
573 HGLOBALStreamImpl_Commit
,
574 HGLOBALStreamImpl_Revert
,
575 HGLOBALStreamImpl_LockRegion
,
576 HGLOBALStreamImpl_UnlockRegion
,
577 HGLOBALStreamImpl_Stat
,
578 HGLOBALStreamImpl_Clone
581 /***********************************************************************
582 * CreateStreamOnHGlobal [OLE32.@]
584 HRESULT WINAPI
CreateStreamOnHGlobal(
586 BOOL fDeleteOnRelease
,
589 HGLOBALStreamImpl
* This
;
594 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl
));
595 if (!This
) return E_OUTOFMEMORY
;
597 This
->IStream_iface
.lpVtbl
= &HGLOBALStreamImplVtbl
;
600 /* initialize the support */
601 This
->supportHandle
= hGlobal
;
602 This
->deleteOnRelease
= fDeleteOnRelease
;
604 /* allocate a handle if one is not supplied */
605 if (!This
->supportHandle
)
606 This
->supportHandle
= GlobalAlloc(GMEM_MOVEABLE
|GMEM_NODISCARD
|GMEM_SHARE
, 0);
608 /* start at the beginning */
609 This
->currentPosition
.u
.HighPart
= 0;
610 This
->currentPosition
.u
.LowPart
= 0;
612 /* initialize the size of the stream to the size of the handle */
613 This
->streamSize
.u
.HighPart
= 0;
614 This
->streamSize
.u
.LowPart
= GlobalSize(This
->supportHandle
);
616 *ppstm
= &This
->IStream_iface
;
621 /***********************************************************************
622 * GetHGlobalFromStream [OLE32.@]
624 HRESULT WINAPI
GetHGlobalFromStream(IStream
* pstm
, HGLOBAL
* phglobal
)
626 HGLOBALStreamImpl
* pStream
;
631 pStream
= (HGLOBALStreamImpl
*) pstm
;
634 * Verify that the stream object was created with CreateStreamOnHGlobal.
636 if (pStream
->IStream_iface
.lpVtbl
== &HGLOBALStreamImplVtbl
)
637 *phglobal
= pStream
->supportHandle
;