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
32 #define NONAMELESSUNION
38 #include "wine/debug.h"
40 #include "storage32.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
45 * This implements the IUnknown method QueryInterface for this
48 static HRESULT WINAPI
StgStreamImpl_QueryInterface(
50 REFIID riid
, /* [in] */
51 void** ppvObject
) /* [iid_is][out] */
53 StgStreamImpl
* This
= impl_from_IStream(iface
);
60 if (IsEqualIID(&IID_IUnknown
, riid
) ||
61 IsEqualIID(&IID_ISequentialStream
, riid
) ||
62 IsEqualIID(&IID_IStream
, riid
))
64 *ppvObject
= &This
->IStream_iface
;
69 IStream_AddRef(iface
);
75 * This implements the IUnknown method AddRef for this
78 static ULONG WINAPI
StgStreamImpl_AddRef(
81 StgStreamImpl
* This
= impl_from_IStream(iface
);
82 return InterlockedIncrement(&This
->ref
);
86 * This implements the IUnknown method Release for this
89 static ULONG WINAPI
StgStreamImpl_Release(
92 StgStreamImpl
* This
= impl_from_IStream(iface
);
93 ULONG ref
= InterlockedDecrement(&This
->ref
);
97 TRACE("(%p)\n", This
);
100 * Release the reference we are holding on the parent storage.
101 * IStorage_Release(&This->parentStorage->IStorage_iface);
103 * No, don't do this. Some apps call IStorage_Release without
104 * calling IStream_Release first. If we grab a reference the
105 * file is not closed, and the app fails when it tries to
106 * reopen the file (Easy-PC, for example). Just inform the
107 * storage that we have closed the stream
110 if (This
->parentStorage
)
111 StorageBaseImpl_RemoveStream(This
->parentStorage
, This
);
112 This
->parentStorage
= 0;
113 HeapFree(GetProcessHeap(), 0, This
);
120 * This method is part of the ISequentialStream interface.
122 * It reads a block of information from the stream at the current
123 * position. It then moves the current position at the end of the
126 * See the documentation of ISequentialStream for more info.
128 static HRESULT WINAPI
StgStreamImpl_Read(
130 void* pv
, /* [length_is][size_is][out] */
132 ULONG
* pcbRead
) /* [out] */
134 StgStreamImpl
* This
= impl_from_IStream(iface
);
136 ULONG bytesReadBuffer
;
139 TRACE("(%p, %p, %d, %p)\n",
140 iface
, pv
, cb
, pcbRead
);
142 if (!This
->parentStorage
)
144 WARN("storage reverted\n");
145 return STG_E_REVERTED
;
149 * If the caller is not interested in the number of bytes read,
150 * we use another buffer to avoid "if" statements in the code.
153 pcbRead
= &bytesReadBuffer
;
155 res
= StorageBaseImpl_StreamReadAt(This
->parentStorage
,
157 This
->currentPosition
,
165 * Advance the pointer for the number of positions read.
167 This
->currentPosition
.QuadPart
+= *pcbRead
;
170 TRACE("<-- %08x\n", res
);
175 * This method is part of the ISequentialStream interface.
177 * It writes a block of information to the stream at the current
178 * position. It then moves the current position at the end of the
179 * written block. If the stream is too small to fit the block,
180 * the stream is grown to fit.
182 * See the documentation of ISequentialStream for more info.
184 static HRESULT WINAPI
StgStreamImpl_Write(
186 const void* pv
, /* [size_is][in] */
188 ULONG
* pcbWritten
) /* [out] */
190 StgStreamImpl
* This
= impl_from_IStream(iface
);
192 ULONG bytesWritten
= 0;
195 TRACE("(%p, %p, %d, %p)\n",
196 iface
, pv
, cb
, pcbWritten
);
199 * Do we have permission to write to this stream?
201 switch(STGM_ACCESS_MODE(This
->grfMode
))
207 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This
->grfMode
));
208 return STG_E_ACCESSDENIED
;
212 return STG_E_INVALIDPOINTER
;
214 if (!This
->parentStorage
)
216 WARN("storage reverted\n");
217 return STG_E_REVERTED
;
221 * If the caller is not interested in the number of bytes written,
222 * we use another buffer to avoid "if" statements in the code.
225 pcbWritten
= &bytesWritten
;
228 * Initialize the out parameter
234 TRACE("<-- S_OK, written 0\n");
238 res
= StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
240 This
->currentPosition
,
246 * Advance the position pointer for the number of positions written.
248 This
->currentPosition
.QuadPart
+= *pcbWritten
;
251 res
= StorageBaseImpl_Flush(This
->parentStorage
);
253 TRACE("<-- %08x, written %u\n", res
, *pcbWritten
);
258 * This method is part of the IStream interface.
260 * It will move the current stream pointer according to the parameters
263 * See the documentation of IStream for more info.
265 static HRESULT WINAPI
StgStreamImpl_Seek(
267 LARGE_INTEGER dlibMove
, /* [in] */
268 DWORD dwOrigin
, /* [in] */
269 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
271 StgStreamImpl
* This
= impl_from_IStream(iface
);
273 ULARGE_INTEGER newPosition
;
274 DirEntry currentEntry
;
277 TRACE("(%p, %d, %d, %p)\n",
278 iface
, dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
281 * fail if the stream has no parent (as does windows)
284 if (!This
->parentStorage
)
286 WARN("storage reverted\n");
287 return STG_E_REVERTED
;
291 * The caller is allowed to pass in NULL as the new position return value.
292 * If it happens, we assign it to a dynamic variable to avoid special cases
295 if (plibNewPosition
== 0)
297 plibNewPosition
= &newPosition
;
301 * The file pointer is moved depending on the given "function"
306 case STREAM_SEEK_SET
:
307 plibNewPosition
->u
.HighPart
= 0;
308 plibNewPosition
->u
.LowPart
= 0;
310 case STREAM_SEEK_CUR
:
311 *plibNewPosition
= This
->currentPosition
;
313 case STREAM_SEEK_END
:
314 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, This
->dirEntry
, ¤tEntry
);
315 if (FAILED(hr
)) return hr
;
316 *plibNewPosition
= currentEntry
.size
;
319 WARN("invalid dwOrigin %d\n", dwOrigin
);
320 return STG_E_INVALIDFUNCTION
;
323 plibNewPosition
->QuadPart
+= dlibMove
.QuadPart
;
326 * tell the caller what we calculated
328 This
->currentPosition
= *plibNewPosition
;
334 * This method is part of the IStream interface.
336 * It will change the size of a stream.
338 * See the documentation of IStream for more info.
340 static HRESULT WINAPI
StgStreamImpl_SetSize(
342 ULARGE_INTEGER libNewSize
) /* [in] */
344 StgStreamImpl
* This
= impl_from_IStream(iface
);
348 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
350 if(!This
->parentStorage
)
352 WARN("storage reverted\n");
353 return STG_E_REVERTED
;
359 if (libNewSize
.u
.HighPart
!= 0)
361 WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize
.u
.HighPart
);
362 return STG_E_INVALIDFUNCTION
;
366 * Do we have permission?
368 if (!(This
->grfMode
& (STGM_WRITE
| STGM_READWRITE
)))
370 WARN("access denied\n");
371 return STG_E_ACCESSDENIED
;
374 hr
= StorageBaseImpl_StreamSetSize(This
->parentStorage
, This
->dirEntry
, libNewSize
);
377 hr
= StorageBaseImpl_Flush(This
->parentStorage
);
383 * This method is part of the IStream interface.
385 * It will copy the 'cb' Bytes to 'pstm' IStream.
387 * See the documentation of IStream for more info.
389 static HRESULT WINAPI
StgStreamImpl_CopyTo(
391 IStream
* pstm
, /* [unique][in] */
392 ULARGE_INTEGER cb
, /* [in] */
393 ULARGE_INTEGER
* pcbRead
, /* [out] */
394 ULARGE_INTEGER
* pcbWritten
) /* [out] */
396 StgStreamImpl
* This
= impl_from_IStream(iface
);
399 ULONG bytesRead
, bytesWritten
, copySize
;
400 ULARGE_INTEGER totalBytesRead
;
401 ULARGE_INTEGER totalBytesWritten
;
403 TRACE("(%p, %p, %d, %p, %p)\n",
404 iface
, pstm
, cb
.u
.LowPart
, pcbRead
, pcbWritten
);
410 if (!This
->parentStorage
)
412 WARN("storage reverted\n");
413 return STG_E_REVERTED
;
417 return STG_E_INVALIDPOINTER
;
419 totalBytesRead
.QuadPart
= 0;
420 totalBytesWritten
.QuadPart
= 0;
422 while ( cb
.QuadPart
> 0 )
424 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
425 copySize
= sizeof(tmpBuffer
);
427 copySize
= cb
.u
.LowPart
;
429 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
431 totalBytesRead
.QuadPart
+= bytesRead
;
433 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
435 totalBytesWritten
.QuadPart
+= bytesWritten
;
438 * Check that read & write operations were successful
440 if (bytesRead
!= bytesWritten
)
442 hr
= STG_E_MEDIUMFULL
;
443 WARN("medium full\n");
447 if (bytesRead
!=copySize
)
450 cb
.QuadPart
-= bytesRead
;
453 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
454 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
460 * This method is part of the IStream interface.
462 * For streams contained in structured storages, this method
463 * does nothing. This is what the documentation tells us.
465 * See the documentation of IStream for more info.
467 static HRESULT WINAPI
StgStreamImpl_Commit(
469 DWORD grfCommitFlags
) /* [in] */
471 StgStreamImpl
* This
= impl_from_IStream(iface
);
473 if (!This
->parentStorage
)
475 WARN("storage reverted\n");
476 return STG_E_REVERTED
;
479 return StorageBaseImpl_Flush(This
->parentStorage
);
483 * This method is part of the IStream interface.
485 * For streams contained in structured storages, this method
486 * does nothing. This is what the documentation tells us.
488 * See the documentation of IStream for more info.
490 static HRESULT WINAPI
StgStreamImpl_Revert(
496 static HRESULT WINAPI
StgStreamImpl_LockRegion(
498 ULARGE_INTEGER libOffset
, /* [in] */
499 ULARGE_INTEGER cb
, /* [in] */
500 DWORD dwLockType
) /* [in] */
502 StgStreamImpl
* This
= impl_from_IStream(iface
);
504 if (!This
->parentStorage
)
506 WARN("storage reverted\n");
507 return STG_E_REVERTED
;
510 FIXME("not implemented!\n");
514 static HRESULT WINAPI
StgStreamImpl_UnlockRegion(
516 ULARGE_INTEGER libOffset
, /* [in] */
517 ULARGE_INTEGER cb
, /* [in] */
518 DWORD dwLockType
) /* [in] */
520 StgStreamImpl
* This
= impl_from_IStream(iface
);
522 if (!This
->parentStorage
)
524 WARN("storage reverted\n");
525 return STG_E_REVERTED
;
528 FIXME("not implemented!\n");
533 * This method is part of the IStream interface.
535 * This method returns information about the current
538 * See the documentation of IStream for more info.
540 static HRESULT WINAPI
StgStreamImpl_Stat(
542 STATSTG
* pstatstg
, /* [out] */
543 DWORD grfStatFlag
) /* [in] */
545 StgStreamImpl
* This
= impl_from_IStream(iface
);
547 DirEntry currentEntry
;
550 TRACE("%p %p %d\n", This
, pstatstg
, grfStatFlag
);
553 * if stream has no parent, return STG_E_REVERTED
556 if (!This
->parentStorage
)
558 WARN("storage reverted\n");
559 return STG_E_REVERTED
;
563 * Read the information from the directory entry.
565 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
571 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
576 pstatstg
->grfMode
= This
->grfMode
;
578 /* In simple create mode cbSize is the current pos */
579 if((This
->parentStorage
->openFlags
& STGM_SIMPLE
) && This
->parentStorage
->create
)
580 pstatstg
->cbSize
= This
->currentPosition
;
585 WARN("failed to read entry\n");
590 * This method is part of the IStream interface.
592 * This method returns a clone of the interface that allows for
593 * another seek pointer
595 * See the documentation of IStream for more info.
597 * I am not totally sure what I am doing here but I presume that this
598 * should be basically as simple as creating a new stream with the same
599 * parent etc and positioning its seek cursor.
601 static HRESULT WINAPI
StgStreamImpl_Clone(
603 IStream
** ppstm
) /* [out] */
605 StgStreamImpl
* This
= impl_from_IStream(iface
);
606 StgStreamImpl
* new_stream
;
607 LARGE_INTEGER seek_pos
;
609 TRACE("%p %p\n", This
, ppstm
);
615 if (!This
->parentStorage
)
616 return STG_E_REVERTED
;
619 return STG_E_INVALIDPOINTER
;
621 new_stream
= StgStreamImpl_Construct (This
->parentStorage
, This
->grfMode
, This
->dirEntry
);
624 return STG_E_INSUFFICIENTMEMORY
; /* Currently the only reason for new_stream=0 */
626 *ppstm
= &new_stream
->IStream_iface
;
627 IStream_AddRef(*ppstm
);
629 seek_pos
.QuadPart
= This
->currentPosition
.QuadPart
;
631 return IStream_Seek(*ppstm
, seek_pos
, STREAM_SEEK_SET
, NULL
);
635 * Virtual function table for the StgStreamImpl class.
637 static const IStreamVtbl StgStreamVtbl
=
639 StgStreamImpl_QueryInterface
,
640 StgStreamImpl_AddRef
,
641 StgStreamImpl_Release
,
645 StgStreamImpl_SetSize
,
646 StgStreamImpl_CopyTo
,
647 StgStreamImpl_Commit
,
648 StgStreamImpl_Revert
,
649 StgStreamImpl_LockRegion
,
650 StgStreamImpl_UnlockRegion
,
655 /******************************************************************************
656 ** StgStreamImpl implementation
660 * This is the constructor for the StgStreamImpl class.
663 * parentStorage - Pointer to the storage that contains the stream to open
664 * dirEntry - Index of the directory entry that points to this stream.
666 StgStreamImpl
* StgStreamImpl_Construct(
667 StorageBaseImpl
* parentStorage
,
671 StgStreamImpl
* newStream
;
673 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
678 * Set-up the virtual function table and reference count.
680 newStream
->IStream_iface
.lpVtbl
= &StgStreamVtbl
;
683 newStream
->parentStorage
= parentStorage
;
686 * We want to nail-down the reference to the storage in case the
687 * stream out-lives the storage in the client application.
689 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
691 * No, don't do this. Some apps call IStorage_Release without
692 * calling IStream_Release first. If we grab a reference the
693 * file is not closed, and the app fails when it tries to
694 * reopen the file (Easy-PC, for example)
697 newStream
->grfMode
= grfMode
;
698 newStream
->dirEntry
= dirEntry
;
701 * Start the stream at the beginning.
703 newStream
->currentPosition
.u
.HighPart
= 0;
704 newStream
->currentPosition
.u
.LowPart
= 0;
706 /* add us to the storage's list of active streams */
707 StorageBaseImpl_AddStream(parentStorage
, newStream
);