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
23 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(storage);
27 /****************************************************************************
28 * HGLOBALStreamImpl definition.
30 * This class imlements the IStream inteface and represents a stream
31 * supported by an HGLOBAL pointer.
33 struct HGLOBALStreamImpl
35 ICOM_VFIELD(IStream); /* Needs to be the first item in the stuct
36 * since we want to cast this in a IStream pointer */
44 * Support for the stream
46 HGLOBAL supportHandle;
49 * This flag is TRUE if the HGLOBAL is destroyed when the stream
50 * is finally released.
55 * Helper variable that contains the size of the stream
57 ULARGE_INTEGER streamSize;
60 * This is the current position of the cursor in the stream
62 ULARGE_INTEGER currentPosition;
65 typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;
68 * Method definition for the StgStreamImpl class.
70 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
72 BOOL fDeleteOnRelease);
74 void HGLOBALStreamImpl_Destroy(
75 HGLOBALStreamImpl* This);
77 void HGLOBALStreamImpl_OpenBlockChain(
78 HGLOBALStreamImpl* This);
80 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
82 REFIID riid, /* [in] */
83 void** ppvObject); /* [iid_is][out] */
85 ULONG WINAPI HGLOBALStreamImpl_AddRef(
88 ULONG WINAPI HGLOBALStreamImpl_Release(
91 HRESULT WINAPI HGLOBALStreamImpl_Read(
93 void* pv, /* [length_is][size_is][out] */
95 ULONG* pcbRead); /* [out] */
97 HRESULT WINAPI HGLOBALStreamImpl_Write(
99 const void* pv, /* [size_is][in] */
101 ULONG* pcbWritten); /* [out] */
103 HRESULT WINAPI HGLOBALStreamImpl_Seek(
105 LARGE_INTEGER dlibMove, /* [in] */
106 DWORD dwOrigin, /* [in] */
107 ULARGE_INTEGER* plibNewPosition); /* [out] */
109 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
111 ULARGE_INTEGER libNewSize); /* [in] */
113 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
115 IStream* pstm, /* [unique][in] */
116 ULARGE_INTEGER cb, /* [in] */
117 ULARGE_INTEGER* pcbRead, /* [out] */
118 ULARGE_INTEGER* pcbWritten); /* [out] */
120 HRESULT WINAPI HGLOBALStreamImpl_Commit(
122 DWORD grfCommitFlags); /* [in] */
124 HRESULT WINAPI HGLOBALStreamImpl_Revert(
127 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
129 ULARGE_INTEGER libOffset, /* [in] */
130 ULARGE_INTEGER cb, /* [in] */
131 DWORD dwLockType); /* [in] */
133 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
135 ULARGE_INTEGER libOffset, /* [in] */
136 ULARGE_INTEGER cb, /* [in] */
137 DWORD dwLockType); /* [in] */
139 HRESULT WINAPI HGLOBALStreamImpl_Stat(
141 STATSTG* pstatstg, /* [out] */
142 DWORD grfStatFlag); /* [in] */
144 HRESULT WINAPI HGLOBALStreamImpl_Clone(
146 IStream** ppstm); /* [out] */
150 * Virtual function table for the HGLOBALStreamImpl class.
152 static ICOM_VTABLE(IStream) HGLOBALStreamImpl_Vtbl =
154 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
155 HGLOBALStreamImpl_QueryInterface,
156 HGLOBALStreamImpl_AddRef,
157 HGLOBALStreamImpl_Release,
158 HGLOBALStreamImpl_Read,
159 HGLOBALStreamImpl_Write,
160 HGLOBALStreamImpl_Seek,
161 HGLOBALStreamImpl_SetSize,
162 HGLOBALStreamImpl_CopyTo,
163 HGLOBALStreamImpl_Commit,
164 HGLOBALStreamImpl_Revert,
165 HGLOBALStreamImpl_LockRegion,
166 HGLOBALStreamImpl_UnlockRegion,
167 HGLOBALStreamImpl_Stat,
168 HGLOBALStreamImpl_Clone
171 /***********************************************************************
172 * CreateStreamOnHGlobal [OLE32.61]
174 HRESULT WINAPI CreateStreamOnHGlobal(
176 BOOL fDeleteOnRelease,
179 HGLOBALStreamImpl* newStream;
181 newStream = HGLOBALStreamImpl_Construct(hGlobal,
186 return IUnknown_QueryInterface((IUnknown*)newStream,
191 return E_OUTOFMEMORY;
194 /***********************************************************************
195 * GetHGlobalFromStream [OLE32.71]
197 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
199 HGLOBALStreamImpl* pStream;
204 pStream = (HGLOBALStreamImpl*) pstm;
207 * Verify that the stream object was created with CreateStreamOnHGlobal.
209 if (ICOM_VTBL(pStream) == &HGLOBALStreamImpl_Vtbl)
210 *phglobal = pStream->supportHandle;
220 /******************************************************************************
221 ** HGLOBALStreamImpl implementation
225 * This is the constructor for the HGLOBALStreamImpl class.
228 * hGlobal - Handle that will support the stream. can be NULL.
229 * fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
230 * when the IStream object is destroyed.
232 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
234 BOOL fDeleteOnRelease)
236 HGLOBALStreamImpl* newStream;
238 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
243 * Set-up the virtual function table and reference count.
245 ICOM_VTBL(newStream) = &HGLOBALStreamImpl_Vtbl;
249 * Initialize the support.
251 newStream->supportHandle = hGlobal;
252 newStream->deleteOnRelease = fDeleteOnRelease;
255 * This method will allocate a handle if one is not supplied.
257 if (!newStream->supportHandle)
259 newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
264 * Start the stream at the begining.
266 newStream->currentPosition.s.HighPart = 0;
267 newStream->currentPosition.s.LowPart = 0;
270 * Initialize the size of the stream to the size of the handle.
272 newStream->streamSize.s.HighPart = 0;
273 newStream->streamSize.s.LowPart = GlobalSize(newStream->supportHandle);
280 * This is the destructor of the HGLOBALStreamImpl class.
282 * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl
283 * class. The pointer passed-in to this function will be freed and will not
286 void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
288 TRACE("(%p)\n", This);
291 * Release the HGlobal if the constructor asked for that.
293 if (This->deleteOnRelease)
295 GlobalFree(This->supportHandle);
296 This->supportHandle=0;
300 * Finally, free the memory used-up by the class.
302 HeapFree(GetProcessHeap(), 0, This);
306 * This implements the IUnknown method QueryInterface for this
309 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
311 REFIID riid, /* [in] */
312 void** ppvObject) /* [iid_is][out] */
314 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
317 * Perform a sanity check on the parameters.
323 * Initialize the return parameter.
328 * Compare the riid with the interface IDs implemented by this object.
330 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
332 *ppvObject = (IStream*)This;
334 else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
336 *ppvObject = (IStream*)This;
340 * Check that we obtained an interface.
343 return E_NOINTERFACE;
346 * Query Interface always increases the reference count by one when it is
349 HGLOBALStreamImpl_AddRef(iface);
355 * This implements the IUnknown method AddRef for this
358 ULONG WINAPI HGLOBALStreamImpl_AddRef(
361 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
369 * This implements the IUnknown method Release for this
372 ULONG WINAPI HGLOBALStreamImpl_Release(
375 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
384 * If the reference count goes down to 0, perform suicide.
388 HGLOBALStreamImpl_Destroy(This);
395 * This method is part of the ISequentialStream interface.
397 * If reads a block of information from the stream at the current
398 * position. It then moves the current position at the end of the
401 * See the documentation of ISequentialStream for more info.
403 HRESULT WINAPI HGLOBALStreamImpl_Read(
405 void* pv, /* [length_is][size_is][out] */
407 ULONG* pcbRead) /* [out] */
409 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
412 ULONG bytesReadBuffer;
413 ULONG bytesToReadFromBuffer;
415 TRACE("(%p, %p, %ld, %p)\n", iface,
419 * If the caller is not interested in the nubmer of bytes read,
420 * we use another buffer to avoid "if" statements in the code.
423 pcbRead = &bytesReadBuffer;
426 * Using the known size of the stream, calculate the number of bytes
427 * to read from the block chain
429 bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
432 * Lock the buffer in position and copy the data.
434 supportBuffer = GlobalLock(This->supportHandle);
436 memcpy(pv, (char *) supportBuffer+This->currentPosition.s.LowPart, bytesToReadFromBuffer);
439 * Move the current position to the new position
441 This->currentPosition.s.LowPart+=bytesToReadFromBuffer;
444 * Return the number of bytes read.
446 *pcbRead = bytesToReadFromBuffer;
451 GlobalUnlock(This->supportHandle);
454 * The function returns S_OK if the buffer was filled completely
455 * it returns S_FALSE if the end of the stream is reached before the
465 * This method is part of the ISequentialStream interface.
467 * It writes a block of information to the stream at the current
468 * position. It then moves the current position at the end of the
469 * written block. If the stream is too small to fit the block,
470 * the stream is grown to fit.
472 * See the documentation of ISequentialStream for more info.
474 HRESULT WINAPI HGLOBALStreamImpl_Write(
476 const void* pv, /* [size_is][in] */
478 ULONG* pcbWritten) /* [out] */
480 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
483 ULARGE_INTEGER newSize;
484 ULONG bytesWritten = 0;
486 TRACE("(%p, %p, %ld, %p)\n", iface,
490 * If the caller is not interested in the number of bytes written,
491 * we use another buffer to avoid "if" statements in the code.
494 pcbWritten = &bytesWritten;
502 newSize.s.HighPart = 0;
503 newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
507 * Verify if we need to grow the stream
509 if (newSize.s.LowPart > This->streamSize.s.LowPart)
512 IStream_SetSize(iface, newSize);
516 * Lock the buffer in position and copy the data.
518 supportBuffer = GlobalLock(This->supportHandle);
520 memcpy((char *) supportBuffer+This->currentPosition.s.LowPart, pv, cb);
523 * Move the current position to the new position
525 This->currentPosition.s.LowPart+=cb;
528 * Return the number of bytes read.
535 GlobalUnlock(This->supportHandle);
541 * This method is part of the IStream interface.
543 * It will move the current stream pointer according to the parameters
546 * See the documentation of IStream for more info.
548 HRESULT WINAPI HGLOBALStreamImpl_Seek(
550 LARGE_INTEGER dlibMove, /* [in] */
551 DWORD dwOrigin, /* [in] */
552 ULARGE_INTEGER* plibNewPosition) /* [out] */
554 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
556 ULARGE_INTEGER newPosition;
558 TRACE("(%p, %ld, %ld, %p)\n", iface,
559 dlibMove.s.LowPart, dwOrigin, plibNewPosition);
562 * The caller is allowed to pass in NULL as the new position return value.
563 * If it happens, we assign it to a dynamic variable to avoid special cases
566 if (plibNewPosition == 0)
568 plibNewPosition = &newPosition;
572 * The file pointer is moved depending on the given "function"
577 case STREAM_SEEK_SET:
578 plibNewPosition->s.HighPart = 0;
579 plibNewPosition->s.LowPart = 0;
581 case STREAM_SEEK_CUR:
582 *plibNewPosition = This->currentPosition;
584 case STREAM_SEEK_END:
585 *plibNewPosition = This->streamSize;
588 return STG_E_INVALIDFUNCTION;
592 * We don't support files with offsets of 64 bits.
594 assert(dlibMove.s.HighPart == 0);
597 * Check if we end-up before the beginning of the file. That should trigger an
600 if ( (dlibMove.s.LowPart<0) && (plibNewPosition->s.LowPart < (ULONG)(-dlibMove.s.LowPart)) )
603 * I don't know what error to send there.
609 * Move the actual file pointer
610 * If the file pointer ends-up after the end of the stream, the next Write operation will
611 * make the file larger. This is how it is documented.
613 plibNewPosition->s.LowPart += dlibMove.s.LowPart;
614 This->currentPosition = *plibNewPosition;
620 * This method is part of the IStream interface.
622 * It will change the size of a stream.
624 * TODO: Switch from small blocks to big blocks and vice versa.
626 * See the documentation of IStream for more info.
628 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
630 ULARGE_INTEGER libNewSize) /* [in] */
632 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
634 TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
639 if (libNewSize.s.HighPart != 0)
640 return STG_E_INVALIDFUNCTION;
642 if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
646 * Re allocate the HGlobal to fit the new size of the stream.
648 This->supportHandle = GlobalReAlloc(This->supportHandle,
649 libNewSize.s.LowPart,
652 This->streamSize.s.LowPart = libNewSize.s.LowPart;
658 * This method is part of the IStream interface.
660 * It will copy the 'cb' Bytes to 'pstm' IStream.
662 * See the documentation of IStream for more info.
664 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
666 IStream* pstm, /* [unique][in] */
667 ULARGE_INTEGER cb, /* [in] */
668 ULARGE_INTEGER* pcbRead, /* [out] */
669 ULARGE_INTEGER* pcbWritten) /* [out] */
673 ULONG bytesRead, bytesWritten, copySize;
674 ULARGE_INTEGER totalBytesRead;
675 ULARGE_INTEGER totalBytesWritten;
677 TRACE("(%p, %p, %ld, %p, %p)\n", iface, pstm,
678 cb.s.LowPart, pcbRead, pcbWritten);
684 return STG_E_INVALIDPOINTER;
686 totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
687 totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
690 * use stack to store data temporarly
691 * there is surely more performant way of doing it, for now this basic
692 * implementation will do the job
694 while ( cb.s.LowPart > 0 )
696 if ( cb.s.LowPart >= 128 )
699 copySize = cb.s.LowPart;
701 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
703 totalBytesRead.s.LowPart += bytesRead;
705 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
707 totalBytesWritten.s.LowPart += bytesWritten;
710 * Check that read & write operations were succesfull
712 if (bytesRead != bytesWritten)
714 hr = STG_E_MEDIUMFULL;
718 if (bytesRead!=copySize)
721 cb.s.LowPart -= bytesRead;
725 * Update number of bytes read and written
729 pcbRead->s.LowPart = totalBytesRead.s.LowPart;
730 pcbRead->s.HighPart = totalBytesRead.s.HighPart;
735 pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
736 pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
742 * This method is part of the IStream interface.
744 * For streams supported by HGLOBALS, this function does nothing.
745 * This is what the documentation tells us.
747 * See the documentation of IStream for more info.
749 HRESULT WINAPI HGLOBALStreamImpl_Commit(
751 DWORD grfCommitFlags) /* [in] */
757 * This method is part of the IStream interface.
759 * For streams supported by HGLOBALS, this function does nothing.
760 * This is what the documentation tells us.
762 * See the documentation of IStream for more info.
764 HRESULT WINAPI HGLOBALStreamImpl_Revert(
771 * This method is part of the IStream interface.
773 * For streams supported by HGLOBALS, this function does nothing.
774 * This is what the documentation tells us.
776 * See the documentation of IStream for more info.
778 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
780 ULARGE_INTEGER libOffset, /* [in] */
781 ULARGE_INTEGER cb, /* [in] */
782 DWORD dwLockType) /* [in] */
788 * This method is part of the IStream interface.
790 * For streams supported by HGLOBALS, this function does nothing.
791 * This is what the documentation tells us.
793 * See the documentation of IStream for more info.
795 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
797 ULARGE_INTEGER libOffset, /* [in] */
798 ULARGE_INTEGER cb, /* [in] */
799 DWORD dwLockType) /* [in] */
805 * This method is part of the IStream interface.
807 * This method returns information about the current
810 * See the documentation of IStream for more info.
812 HRESULT WINAPI HGLOBALStreamImpl_Stat(
814 STATSTG* pstatstg, /* [out] */
815 DWORD grfStatFlag) /* [in] */
817 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
819 memset(pstatstg, 0, sizeof(STATSTG));
821 pstatstg->pwcsName = NULL;
822 pstatstg->type = STGTY_STREAM;
823 pstatstg->cbSize = This->streamSize;
828 HRESULT WINAPI HGLOBALStreamImpl_Clone(
830 IStream** ppstm) /* [out] */
832 FIXME("not implemented!\n");