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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(storage);
41 /****************************************************************************
42 * HGLOBALStreamImpl definition.
44 * This class imlements the IStream inteface and represents a stream
45 * supported by an HGLOBAL pointer.
47 struct HGLOBALStreamImpl
49 ICOM_VFIELD(IStream); /* Needs to be the first item in the stuct
50 * since we want to cast this in a IStream pointer */
58 * Support for the stream
60 HGLOBAL supportHandle;
63 * This flag is TRUE if the HGLOBAL is destroyed when the stream
64 * is finally released.
69 * Helper variable that contains the size of the stream
71 ULARGE_INTEGER streamSize;
74 * This is the current position of the cursor in the stream
76 ULARGE_INTEGER currentPosition;
79 typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;
82 * Method definition for the StgStreamImpl class.
84 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
86 BOOL fDeleteOnRelease);
88 void HGLOBALStreamImpl_Destroy(
89 HGLOBALStreamImpl* This);
91 void HGLOBALStreamImpl_OpenBlockChain(
92 HGLOBALStreamImpl* This);
94 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
96 REFIID riid, /* [in] */
97 void** ppvObject); /* [iid_is][out] */
99 ULONG WINAPI HGLOBALStreamImpl_AddRef(
102 ULONG WINAPI HGLOBALStreamImpl_Release(
105 HRESULT WINAPI HGLOBALStreamImpl_Read(
107 void* pv, /* [length_is][size_is][out] */
109 ULONG* pcbRead); /* [out] */
111 HRESULT WINAPI HGLOBALStreamImpl_Write(
113 const void* pv, /* [size_is][in] */
115 ULONG* pcbWritten); /* [out] */
117 HRESULT WINAPI HGLOBALStreamImpl_Seek(
119 LARGE_INTEGER dlibMove, /* [in] */
120 DWORD dwOrigin, /* [in] */
121 ULARGE_INTEGER* plibNewPosition); /* [out] */
123 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
125 ULARGE_INTEGER libNewSize); /* [in] */
127 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
129 IStream* pstm, /* [unique][in] */
130 ULARGE_INTEGER cb, /* [in] */
131 ULARGE_INTEGER* pcbRead, /* [out] */
132 ULARGE_INTEGER* pcbWritten); /* [out] */
134 HRESULT WINAPI HGLOBALStreamImpl_Commit(
136 DWORD grfCommitFlags); /* [in] */
138 HRESULT WINAPI HGLOBALStreamImpl_Revert(
141 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
143 ULARGE_INTEGER libOffset, /* [in] */
144 ULARGE_INTEGER cb, /* [in] */
145 DWORD dwLockType); /* [in] */
147 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
149 ULARGE_INTEGER libOffset, /* [in] */
150 ULARGE_INTEGER cb, /* [in] */
151 DWORD dwLockType); /* [in] */
153 HRESULT WINAPI HGLOBALStreamImpl_Stat(
155 STATSTG* pstatstg, /* [out] */
156 DWORD grfStatFlag); /* [in] */
158 HRESULT WINAPI HGLOBALStreamImpl_Clone(
160 IStream** ppstm); /* [out] */
164 * Virtual function table for the HGLOBALStreamImpl class.
166 static ICOM_VTABLE(IStream) HGLOBALStreamImpl_Vtbl =
168 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
169 HGLOBALStreamImpl_QueryInterface,
170 HGLOBALStreamImpl_AddRef,
171 HGLOBALStreamImpl_Release,
172 HGLOBALStreamImpl_Read,
173 HGLOBALStreamImpl_Write,
174 HGLOBALStreamImpl_Seek,
175 HGLOBALStreamImpl_SetSize,
176 HGLOBALStreamImpl_CopyTo,
177 HGLOBALStreamImpl_Commit,
178 HGLOBALStreamImpl_Revert,
179 HGLOBALStreamImpl_LockRegion,
180 HGLOBALStreamImpl_UnlockRegion,
181 HGLOBALStreamImpl_Stat,
182 HGLOBALStreamImpl_Clone
185 /***********************************************************************
186 * CreateStreamOnHGlobal [OLE32.61]
188 HRESULT WINAPI CreateStreamOnHGlobal(
190 BOOL fDeleteOnRelease,
193 HGLOBALStreamImpl* newStream;
195 newStream = HGLOBALStreamImpl_Construct(hGlobal,
200 return IUnknown_QueryInterface((IUnknown*)newStream,
205 return E_OUTOFMEMORY;
208 /***********************************************************************
209 * GetHGlobalFromStream [OLE32.71]
211 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
213 HGLOBALStreamImpl* pStream;
218 pStream = (HGLOBALStreamImpl*) pstm;
221 * Verify that the stream object was created with CreateStreamOnHGlobal.
223 if (ICOM_VTBL(pStream) == &HGLOBALStreamImpl_Vtbl)
224 *phglobal = pStream->supportHandle;
234 /******************************************************************************
235 ** HGLOBALStreamImpl implementation
239 * This is the constructor for the HGLOBALStreamImpl class.
242 * hGlobal - Handle that will support the stream. can be NULL.
243 * fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
244 * when the IStream object is destroyed.
246 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
248 BOOL fDeleteOnRelease)
250 HGLOBALStreamImpl* newStream;
252 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
257 * Set-up the virtual function table and reference count.
259 ICOM_VTBL(newStream) = &HGLOBALStreamImpl_Vtbl;
263 * Initialize the support.
265 newStream->supportHandle = hGlobal;
266 newStream->deleteOnRelease = fDeleteOnRelease;
269 * This method will allocate a handle if one is not supplied.
271 if (!newStream->supportHandle)
273 newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
278 * Start the stream at the beginning.
280 newStream->currentPosition.s.HighPart = 0;
281 newStream->currentPosition.s.LowPart = 0;
284 * Initialize the size of the stream to the size of the handle.
286 newStream->streamSize.s.HighPart = 0;
287 newStream->streamSize.s.LowPart = GlobalSize(newStream->supportHandle);
294 * This is the destructor of the HGLOBALStreamImpl class.
296 * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl
297 * class. The pointer passed-in to this function will be freed and will not
300 void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
302 TRACE("(%p)\n", This);
305 * Release the HGlobal if the constructor asked for that.
307 if (This->deleteOnRelease)
309 GlobalFree(This->supportHandle);
310 This->supportHandle=0;
314 * Finally, free the memory used-up by the class.
316 HeapFree(GetProcessHeap(), 0, This);
320 * This implements the IUnknown method QueryInterface for this
323 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
325 REFIID riid, /* [in] */
326 void** ppvObject) /* [iid_is][out] */
328 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
331 * Perform a sanity check on the parameters.
337 * Initialize the return parameter.
342 * Compare the riid with the interface IDs implemented by this object.
344 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
346 *ppvObject = (IStream*)This;
348 else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
350 *ppvObject = (IStream*)This;
354 * Check that we obtained an interface.
357 return E_NOINTERFACE;
360 * Query Interface always increases the reference count by one when it is
363 HGLOBALStreamImpl_AddRef(iface);
369 * This implements the IUnknown method AddRef for this
372 ULONG WINAPI HGLOBALStreamImpl_AddRef(
375 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
383 * This implements the IUnknown method Release for this
386 ULONG WINAPI HGLOBALStreamImpl_Release(
389 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
398 * If the reference count goes down to 0, perform suicide.
402 HGLOBALStreamImpl_Destroy(This);
409 * This method is part of the ISequentialStream interface.
411 * If reads a block of information from the stream at the current
412 * position. It then moves the current position at the end of the
415 * See the documentation of ISequentialStream for more info.
417 HRESULT WINAPI HGLOBALStreamImpl_Read(
419 void* pv, /* [length_is][size_is][out] */
421 ULONG* pcbRead) /* [out] */
423 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
426 ULONG bytesReadBuffer;
427 ULONG bytesToReadFromBuffer;
429 TRACE("(%p, %p, %ld, %p)\n", iface,
433 * If the caller is not interested in the nubmer of bytes read,
434 * we use another buffer to avoid "if" statements in the code.
437 pcbRead = &bytesReadBuffer;
440 * Using the known size of the stream, calculate the number of bytes
441 * to read from the block chain
443 bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
446 * Lock the buffer in position and copy the data.
448 supportBuffer = GlobalLock(This->supportHandle);
450 memcpy(pv, (char *) supportBuffer+This->currentPosition.s.LowPart, bytesToReadFromBuffer);
453 * Move the current position to the new position
455 This->currentPosition.s.LowPart+=bytesToReadFromBuffer;
458 * Return the number of bytes read.
460 *pcbRead = bytesToReadFromBuffer;
465 GlobalUnlock(This->supportHandle);
468 * The function returns S_OK if the buffer was filled completely
469 * it returns S_FALSE if the end of the stream is reached before the
479 * This method is part of the ISequentialStream interface.
481 * It writes a block of information to the stream at the current
482 * position. It then moves the current position at the end of the
483 * written block. If the stream is too small to fit the block,
484 * the stream is grown to fit.
486 * See the documentation of ISequentialStream for more info.
488 HRESULT WINAPI HGLOBALStreamImpl_Write(
490 const void* pv, /* [size_is][in] */
492 ULONG* pcbWritten) /* [out] */
494 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
497 ULARGE_INTEGER newSize;
498 ULONG bytesWritten = 0;
500 TRACE("(%p, %p, %ld, %p)\n", iface,
504 * If the caller is not interested in the number of bytes written,
505 * we use another buffer to avoid "if" statements in the code.
508 pcbWritten = &bytesWritten;
516 newSize.s.HighPart = 0;
517 newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
521 * Verify if we need to grow the stream
523 if (newSize.s.LowPart > This->streamSize.s.LowPart)
526 IStream_SetSize(iface, newSize);
530 * Lock the buffer in position and copy the data.
532 supportBuffer = GlobalLock(This->supportHandle);
534 memcpy((char *) supportBuffer+This->currentPosition.s.LowPart, pv, cb);
537 * Move the current position to the new position
539 This->currentPosition.s.LowPart+=cb;
542 * Return the number of bytes read.
549 GlobalUnlock(This->supportHandle);
555 * This method is part of the IStream interface.
557 * It will move the current stream pointer according to the parameters
560 * See the documentation of IStream for more info.
562 HRESULT WINAPI HGLOBALStreamImpl_Seek(
564 LARGE_INTEGER dlibMove, /* [in] */
565 DWORD dwOrigin, /* [in] */
566 ULARGE_INTEGER* plibNewPosition) /* [out] */
568 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
570 ULARGE_INTEGER newPosition;
572 TRACE("(%p, %ld, %ld, %p)\n", iface,
573 dlibMove.s.LowPart, dwOrigin, plibNewPosition);
576 * The caller is allowed to pass in NULL as the new position return value.
577 * If it happens, we assign it to a dynamic variable to avoid special cases
580 if (plibNewPosition == 0)
582 plibNewPosition = &newPosition;
586 * The file pointer is moved depending on the given "function"
591 case STREAM_SEEK_SET:
592 plibNewPosition->s.HighPart = 0;
593 plibNewPosition->s.LowPart = 0;
595 case STREAM_SEEK_CUR:
596 *plibNewPosition = This->currentPosition;
598 case STREAM_SEEK_END:
599 *plibNewPosition = This->streamSize;
602 return STG_E_INVALIDFUNCTION;
606 * We don't support files with offsets of 64 bits.
608 assert(dlibMove.s.HighPart == 0);
611 * Check if we end-up before the beginning of the file. That should trigger an
614 if ( (dlibMove.s.LowPart<0) && (plibNewPosition->s.LowPart < (ULONG)(-dlibMove.s.LowPart)) )
617 * I don't know what error to send there.
623 * Move the actual file pointer
624 * If the file pointer ends-up after the end of the stream, the next Write operation will
625 * make the file larger. This is how it is documented.
627 plibNewPosition->s.LowPart += dlibMove.s.LowPart;
628 This->currentPosition = *plibNewPosition;
634 * This method is part of the IStream interface.
636 * It will change the size of a stream.
638 * TODO: Switch from small blocks to big blocks and vice versa.
640 * See the documentation of IStream for more info.
642 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
644 ULARGE_INTEGER libNewSize) /* [in] */
646 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
648 TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
653 if (libNewSize.s.HighPart != 0)
654 return STG_E_INVALIDFUNCTION;
656 if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
660 * Re allocate the HGlobal to fit the new size of the stream.
662 This->supportHandle = GlobalReAlloc(This->supportHandle,
663 libNewSize.s.LowPart,
666 This->streamSize.s.LowPart = libNewSize.s.LowPart;
672 * This method is part of the IStream interface.
674 * It will copy the 'cb' Bytes to 'pstm' IStream.
676 * See the documentation of IStream for more info.
678 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
680 IStream* pstm, /* [unique][in] */
681 ULARGE_INTEGER cb, /* [in] */
682 ULARGE_INTEGER* pcbRead, /* [out] */
683 ULARGE_INTEGER* pcbWritten) /* [out] */
687 ULONG bytesRead, bytesWritten, copySize;
688 ULARGE_INTEGER totalBytesRead;
689 ULARGE_INTEGER totalBytesWritten;
691 TRACE("(%p, %p, %ld, %p, %p)\n", iface, pstm,
692 cb.s.LowPart, pcbRead, pcbWritten);
698 return STG_E_INVALIDPOINTER;
700 totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
701 totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
704 * use stack to store data temporarly
705 * there is surely more performant way of doing it, for now this basic
706 * implementation will do the job
708 while ( cb.s.LowPart > 0 )
710 if ( cb.s.LowPart >= 128 )
713 copySize = cb.s.LowPart;
715 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
717 totalBytesRead.s.LowPart += bytesRead;
719 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
721 totalBytesWritten.s.LowPart += bytesWritten;
724 * Check that read & write operations were succesfull
726 if (bytesRead != bytesWritten)
728 hr = STG_E_MEDIUMFULL;
732 if (bytesRead!=copySize)
735 cb.s.LowPart -= bytesRead;
739 * Update number of bytes read and written
743 pcbRead->s.LowPart = totalBytesRead.s.LowPart;
744 pcbRead->s.HighPart = totalBytesRead.s.HighPart;
749 pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
750 pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
756 * This method is part of the IStream interface.
758 * For streams supported by HGLOBALS, this function does nothing.
759 * This is what the documentation tells us.
761 * See the documentation of IStream for more info.
763 HRESULT WINAPI HGLOBALStreamImpl_Commit(
765 DWORD grfCommitFlags) /* [in] */
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_Revert(
785 * This method is part of the IStream interface.
787 * For streams supported by HGLOBALS, this function does nothing.
788 * This is what the documentation tells us.
790 * See the documentation of IStream for more info.
792 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
794 ULARGE_INTEGER libOffset, /* [in] */
795 ULARGE_INTEGER cb, /* [in] */
796 DWORD dwLockType) /* [in] */
802 * This method is part of the IStream interface.
804 * For streams supported by HGLOBALS, this function does nothing.
805 * This is what the documentation tells us.
807 * See the documentation of IStream for more info.
809 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
811 ULARGE_INTEGER libOffset, /* [in] */
812 ULARGE_INTEGER cb, /* [in] */
813 DWORD dwLockType) /* [in] */
819 * This method is part of the IStream interface.
821 * This method returns information about the current
824 * See the documentation of IStream for more info.
826 HRESULT WINAPI HGLOBALStreamImpl_Stat(
828 STATSTG* pstatstg, /* [out] */
829 DWORD grfStatFlag) /* [in] */
831 HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
833 memset(pstatstg, 0, sizeof(STATSTG));
835 pstatstg->pwcsName = NULL;
836 pstatstg->type = STGTY_STREAM;
837 pstatstg->cbSize = This->streamSize;
842 HRESULT WINAPI HGLOBALStreamImpl_Clone(
844 IStream** ppstm) /* [out] */
846 FIXME("not implemented!\n");