2 * Compound Storage (32 bit version)
3 * Stream implementation
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #define NONAMELESSUNION
39 #include "wine/debug.h"
41 #include "storage32.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
46 * This implements the IUnknown method QueryInterface for this
49 static HRESULT WINAPI
StgStreamImpl_QueryInterface(
51 REFIID riid
, /* [in] */
52 void** ppvObject
) /* [iid_is][out] */
54 StgStreamImpl
* This
= impl_from_IStream(iface
);
61 if (IsEqualIID(&IID_IUnknown
, riid
) ||
62 IsEqualIID(&IID_ISequentialStream
, riid
) ||
63 IsEqualIID(&IID_IStream
, riid
))
65 *ppvObject
= &This
->IStream_iface
;
70 IStream_AddRef(iface
);
76 * This implements the IUnknown method AddRef for this
79 static ULONG WINAPI
StgStreamImpl_AddRef(
82 StgStreamImpl
* This
= impl_from_IStream(iface
);
83 return InterlockedIncrement(&This
->ref
);
87 * This implements the IUnknown method Release for this
90 static ULONG WINAPI
StgStreamImpl_Release(
93 StgStreamImpl
* This
= impl_from_IStream(iface
);
94 ULONG ref
= InterlockedDecrement(&This
->ref
);
98 TRACE("(%p)\n", This
);
101 * Release the reference we are holding on the parent storage.
102 * IStorage_Release(&This->parentStorage->IStorage_iface);
104 * No, don't do this. Some apps call IStorage_Release without
105 * calling IStream_Release first. If we grab a reference the
106 * file is not closed, and the app fails when it tries to
107 * reopen the file (Easy-PC, for example). Just inform the
108 * storage that we have closed the stream
111 if (This
->parentStorage
)
112 StorageBaseImpl_RemoveStream(This
->parentStorage
, This
);
113 This
->parentStorage
= 0;
114 HeapFree(GetProcessHeap(), 0, This
);
121 * This method is part of the ISequentialStream interface.
123 * It reads a block of information from the stream at the current
124 * position. It then moves the current position at the end of the
127 * See the documentation of ISequentialStream for more info.
129 static HRESULT WINAPI
StgStreamImpl_Read(
131 void* pv
, /* [length_is][size_is][out] */
133 ULONG
* pcbRead
) /* [out] */
135 StgStreamImpl
* This
= impl_from_IStream(iface
);
137 ULONG bytesReadBuffer
;
140 TRACE("(%p, %p, %d, %p)\n",
141 iface
, pv
, cb
, pcbRead
);
143 if (!This
->parentStorage
)
145 WARN("storage reverted\n");
146 return STG_E_REVERTED
;
150 * If the caller is not interested in the number of bytes read,
151 * we use another buffer to avoid "if" statements in the code.
154 pcbRead
= &bytesReadBuffer
;
156 res
= StorageBaseImpl_StreamReadAt(This
->parentStorage
,
158 This
->currentPosition
,
166 * Advance the pointer for the number of positions read.
168 This
->currentPosition
.QuadPart
+= *pcbRead
;
171 TRACE("<-- %08x\n", res
);
176 * This method is part of the ISequentialStream interface.
178 * It writes a block of information to the stream at the current
179 * position. It then moves the current position at the end of the
180 * written block. If the stream is too small to fit the block,
181 * the stream is grown to fit.
183 * See the documentation of ISequentialStream for more info.
185 static HRESULT WINAPI
StgStreamImpl_Write(
187 const void* pv
, /* [size_is][in] */
189 ULONG
* pcbWritten
) /* [out] */
191 StgStreamImpl
* This
= impl_from_IStream(iface
);
193 ULONG bytesWritten
= 0;
196 TRACE("(%p, %p, %d, %p)\n",
197 iface
, pv
, cb
, pcbWritten
);
200 * Do we have permission to write to this stream?
202 switch(STGM_ACCESS_MODE(This
->grfMode
))
208 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This
->grfMode
));
209 return STG_E_ACCESSDENIED
;
213 return STG_E_INVALIDPOINTER
;
215 if (!This
->parentStorage
)
217 WARN("storage reverted\n");
218 return STG_E_REVERTED
;
222 * If the caller is not interested in the number of bytes written,
223 * we use another buffer to avoid "if" statements in the code.
226 pcbWritten
= &bytesWritten
;
229 * Initialize the out parameter
235 TRACE("<-- S_OK, written 0\n");
239 res
= StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
241 This
->currentPosition
,
247 * Advance the position pointer for the number of positions written.
249 This
->currentPosition
.QuadPart
+= *pcbWritten
;
252 res
= StorageBaseImpl_Flush(This
->parentStorage
);
254 TRACE("<-- %08x, written %u\n", res
, *pcbWritten
);
259 * This method is part of the IStream interface.
261 * It will move the current stream pointer according to the parameters
264 * See the documentation of IStream for more info.
266 static HRESULT WINAPI
StgStreamImpl_Seek(
268 LARGE_INTEGER dlibMove
, /* [in] */
269 DWORD dwOrigin
, /* [in] */
270 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
272 StgStreamImpl
* This
= impl_from_IStream(iface
);
274 ULARGE_INTEGER newPosition
;
275 DirEntry currentEntry
;
278 TRACE("(%p, %d, %d, %p)\n",
279 iface
, dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
282 * fail if the stream has no parent (as does windows)
285 if (!This
->parentStorage
)
287 WARN("storage reverted\n");
288 return STG_E_REVERTED
;
292 * The caller is allowed to pass in NULL as the new position return value.
293 * If it happens, we assign it to a dynamic variable to avoid special cases
296 if (plibNewPosition
== 0)
298 plibNewPosition
= &newPosition
;
302 * The file pointer is moved depending on the given "function"
307 case STREAM_SEEK_SET
:
308 plibNewPosition
->u
.HighPart
= 0;
309 plibNewPosition
->u
.LowPart
= 0;
311 case STREAM_SEEK_CUR
:
312 *plibNewPosition
= This
->currentPosition
;
314 case STREAM_SEEK_END
:
315 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, This
->dirEntry
, ¤tEntry
);
316 if (FAILED(hr
)) return hr
;
317 *plibNewPosition
= currentEntry
.size
;
320 WARN("invalid dwOrigin %d\n", dwOrigin
);
321 return STG_E_INVALIDFUNCTION
;
324 plibNewPosition
->QuadPart
+= dlibMove
.QuadPart
;
327 * tell the caller what we calculated
329 This
->currentPosition
= *plibNewPosition
;
335 * This method is part of the IStream interface.
337 * It will change the size of a stream.
339 * See the documentation of IStream for more info.
341 static HRESULT WINAPI
StgStreamImpl_SetSize(
343 ULARGE_INTEGER libNewSize
) /* [in] */
345 StgStreamImpl
* This
= impl_from_IStream(iface
);
349 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
351 if(!This
->parentStorage
)
353 WARN("storage reverted\n");
354 return STG_E_REVERTED
;
360 if (libNewSize
.u
.HighPart
!= 0)
362 WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize
.u
.HighPart
);
363 return STG_E_INVALIDFUNCTION
;
367 * Do we have permission?
369 if (!(This
->grfMode
& (STGM_WRITE
| STGM_READWRITE
)))
371 WARN("access denied\n");
372 return STG_E_ACCESSDENIED
;
375 hr
= StorageBaseImpl_StreamSetSize(This
->parentStorage
, This
->dirEntry
, libNewSize
);
378 hr
= StorageBaseImpl_Flush(This
->parentStorage
);
384 * This method is part of the IStream interface.
386 * It will copy the 'cb' Bytes to 'pstm' IStream.
388 * See the documentation of IStream for more info.
390 static HRESULT WINAPI
StgStreamImpl_CopyTo(
392 IStream
* pstm
, /* [unique][in] */
393 ULARGE_INTEGER cb
, /* [in] */
394 ULARGE_INTEGER
* pcbRead
, /* [out] */
395 ULARGE_INTEGER
* pcbWritten
) /* [out] */
397 StgStreamImpl
* This
= impl_from_IStream(iface
);
400 ULONG bytesRead
, bytesWritten
, copySize
;
401 ULARGE_INTEGER totalBytesRead
;
402 ULARGE_INTEGER totalBytesWritten
;
404 TRACE("(%p, %p, %d, %p, %p)\n",
405 iface
, pstm
, cb
.u
.LowPart
, pcbRead
, pcbWritten
);
411 if (!This
->parentStorage
)
413 WARN("storage reverted\n");
414 return STG_E_REVERTED
;
418 return STG_E_INVALIDPOINTER
;
420 totalBytesRead
.QuadPart
= 0;
421 totalBytesWritten
.QuadPart
= 0;
423 while ( cb
.QuadPart
> 0 )
425 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
426 copySize
= sizeof(tmpBuffer
);
428 copySize
= cb
.u
.LowPart
;
430 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
432 totalBytesRead
.QuadPart
+= bytesRead
;
434 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
436 totalBytesWritten
.QuadPart
+= bytesWritten
;
439 * Check that read & write operations were successful
441 if (bytesRead
!= bytesWritten
)
443 hr
= STG_E_MEDIUMFULL
;
444 WARN("medium full\n");
448 if (bytesRead
!=copySize
)
451 cb
.QuadPart
-= bytesRead
;
454 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
455 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
461 * This method is part of the IStream interface.
463 * For streams contained in structured storages, this method
464 * does nothing. This is what the documentation tells us.
466 * See the documentation of IStream for more info.
468 static HRESULT WINAPI
StgStreamImpl_Commit(
470 DWORD grfCommitFlags
) /* [in] */
472 StgStreamImpl
* This
= impl_from_IStream(iface
);
474 if (!This
->parentStorage
)
476 WARN("storage reverted\n");
477 return STG_E_REVERTED
;
480 return StorageBaseImpl_Flush(This
->parentStorage
);
484 * This method is part of the IStream interface.
486 * For streams contained in structured storages, this method
487 * does nothing. This is what the documentation tells us.
489 * See the documentation of IStream for more info.
491 static HRESULT WINAPI
StgStreamImpl_Revert(
497 static HRESULT WINAPI
StgStreamImpl_LockRegion(
499 ULARGE_INTEGER libOffset
, /* [in] */
500 ULARGE_INTEGER cb
, /* [in] */
501 DWORD dwLockType
) /* [in] */
503 StgStreamImpl
* This
= impl_from_IStream(iface
);
505 if (!This
->parentStorage
)
507 WARN("storage reverted\n");
508 return STG_E_REVERTED
;
511 FIXME("not implemented!\n");
515 static HRESULT WINAPI
StgStreamImpl_UnlockRegion(
517 ULARGE_INTEGER libOffset
, /* [in] */
518 ULARGE_INTEGER cb
, /* [in] */
519 DWORD dwLockType
) /* [in] */
521 StgStreamImpl
* This
= impl_from_IStream(iface
);
523 if (!This
->parentStorage
)
525 WARN("storage reverted\n");
526 return STG_E_REVERTED
;
529 FIXME("not implemented!\n");
534 * This method is part of the IStream interface.
536 * This method returns information about the current
539 * See the documentation of IStream for more info.
541 static HRESULT WINAPI
StgStreamImpl_Stat(
543 STATSTG
* pstatstg
, /* [out] */
544 DWORD grfStatFlag
) /* [in] */
546 StgStreamImpl
* This
= impl_from_IStream(iface
);
548 DirEntry currentEntry
;
551 TRACE("%p %p %d\n", This
, pstatstg
, grfStatFlag
);
554 * if stream has no parent, return STG_E_REVERTED
557 if (!This
->parentStorage
)
559 WARN("storage reverted\n");
560 return STG_E_REVERTED
;
564 * Read the information from the directory entry.
566 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
572 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
577 pstatstg
->grfMode
= This
->grfMode
;
579 /* In simple create mode cbSize is the current pos */
580 if((This
->parentStorage
->openFlags
& STGM_SIMPLE
) && This
->parentStorage
->create
)
581 pstatstg
->cbSize
= This
->currentPosition
;
586 WARN("failed to read entry\n");
591 * This method is part of the IStream interface.
593 * This method returns a clone of the interface that allows for
594 * another seek pointer
596 * See the documentation of IStream for more info.
598 * I am not totally sure what I am doing here but I presume that this
599 * should be basically as simple as creating a new stream with the same
600 * parent etc and positioning its seek cursor.
602 static HRESULT WINAPI
StgStreamImpl_Clone(
604 IStream
** ppstm
) /* [out] */
606 StgStreamImpl
* This
= impl_from_IStream(iface
);
608 StgStreamImpl
* new_stream
;
609 LARGE_INTEGER seek_pos
;
611 TRACE("%p %p\n", This
, ppstm
);
617 if (!This
->parentStorage
)
618 return STG_E_REVERTED
;
621 return STG_E_INVALIDPOINTER
;
623 new_stream
= StgStreamImpl_Construct (This
->parentStorage
, This
->grfMode
, This
->dirEntry
);
626 return STG_E_INSUFFICIENTMEMORY
; /* Currently the only reason for new_stream=0 */
628 *ppstm
= &new_stream
->IStream_iface
;
629 IStream_AddRef(*ppstm
);
631 seek_pos
.QuadPart
= This
->currentPosition
.QuadPart
;
633 hres
= IStream_Seek(*ppstm
, seek_pos
, STREAM_SEEK_SET
, NULL
);
635 assert (SUCCEEDED(hres
));
641 * Virtual function table for the StgStreamImpl class.
643 static const IStreamVtbl StgStreamVtbl
=
645 StgStreamImpl_QueryInterface
,
646 StgStreamImpl_AddRef
,
647 StgStreamImpl_Release
,
651 StgStreamImpl_SetSize
,
652 StgStreamImpl_CopyTo
,
653 StgStreamImpl_Commit
,
654 StgStreamImpl_Revert
,
655 StgStreamImpl_LockRegion
,
656 StgStreamImpl_UnlockRegion
,
661 /******************************************************************************
662 ** StgStreamImpl implementation
666 * This is the constructor for the StgStreamImpl class.
669 * parentStorage - Pointer to the storage that contains the stream to open
670 * dirEntry - Index of the directory entry that points to this stream.
672 StgStreamImpl
* StgStreamImpl_Construct(
673 StorageBaseImpl
* parentStorage
,
677 StgStreamImpl
* newStream
;
679 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
684 * Set-up the virtual function table and reference count.
686 newStream
->IStream_iface
.lpVtbl
= &StgStreamVtbl
;
689 newStream
->parentStorage
= parentStorage
;
692 * We want to nail-down the reference to the storage in case the
693 * stream out-lives the storage in the client application.
695 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
697 * No, don't do this. Some apps call IStorage_Release without
698 * calling IStream_Release first. If we grab a reference the
699 * file is not closed, and the app fails when it tries to
700 * reopen the file (Easy-PC, for example)
703 newStream
->grfMode
= grfMode
;
704 newStream
->dirEntry
= dirEntry
;
707 * Start the stream at the beginning.
709 newStream
->currentPosition
.u
.HighPart
= 0;
710 newStream
->currentPosition
.u
.LowPart
= 0;
712 /* add us to the storage's list of active streams */
713 StorageBaseImpl_AddStream(parentStorage
, newStream
);