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, %lu, %p.\n", iface
, pv
, cb
, pcbRead
);
141 if (!This
->parentStorage
)
143 WARN("storage reverted\n");
144 return STG_E_REVERTED
;
148 * If the caller is not interested in the number of bytes read,
149 * we use another buffer to avoid "if" statements in the code.
152 pcbRead
= &bytesReadBuffer
;
154 res
= StorageBaseImpl_StreamReadAt(This
->parentStorage
,
156 This
->currentPosition
,
164 * Advance the pointer for the number of positions read.
166 This
->currentPosition
.QuadPart
+= *pcbRead
;
169 TRACE("<-- %#lx\n", res
);
174 * This method is part of the ISequentialStream interface.
176 * It writes a block of information to the stream at the current
177 * position. It then moves the current position at the end of the
178 * written block. If the stream is too small to fit the block,
179 * the stream is grown to fit.
181 * See the documentation of ISequentialStream for more info.
183 static HRESULT WINAPI
StgStreamImpl_Write(
185 const void* pv
, /* [size_is][in] */
187 ULONG
* pcbWritten
) /* [out] */
189 StgStreamImpl
* This
= impl_from_IStream(iface
);
191 ULONG bytesWritten
= 0;
194 TRACE("%p, %p, %lu, %p.\n", iface
, pv
, cb
, pcbWritten
);
197 * Do we have permission to write to this stream?
199 switch(STGM_ACCESS_MODE(This
->grfMode
))
205 WARN("access denied by flags: %#lx\n", STGM_ACCESS_MODE(This
->grfMode
));
206 return STG_E_ACCESSDENIED
;
210 return STG_E_INVALIDPOINTER
;
212 if (!This
->parentStorage
)
214 WARN("storage reverted\n");
215 return STG_E_REVERTED
;
219 * If the caller is not interested in the number of bytes written,
220 * we use another buffer to avoid "if" statements in the code.
223 pcbWritten
= &bytesWritten
;
226 * Initialize the out parameter
232 TRACE("<-- S_OK, written 0\n");
236 res
= StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
238 This
->currentPosition
,
244 * Advance the position pointer for the number of positions written.
246 This
->currentPosition
.QuadPart
+= *pcbWritten
;
249 res
= StorageBaseImpl_Flush(This
->parentStorage
);
251 TRACE("<-- %#lx, written %lu\n", res
, *pcbWritten
);
256 * This method is part of the IStream interface.
258 * It will move the current stream pointer according to the parameters
261 * See the documentation of IStream for more info.
263 static HRESULT WINAPI
StgStreamImpl_Seek(
265 LARGE_INTEGER dlibMove
, /* [in] */
266 DWORD dwOrigin
, /* [in] */
267 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
269 StgStreamImpl
* This
= impl_from_IStream(iface
);
271 ULARGE_INTEGER newPosition
;
272 DirEntry currentEntry
;
275 TRACE("%p, %ld, %ld, %p.\n", iface
, dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
278 * fail if the stream has no parent (as does windows)
281 if (!This
->parentStorage
)
283 WARN("storage reverted\n");
284 return STG_E_REVERTED
;
288 * The caller is allowed to pass in NULL as the new position return value.
289 * If it happens, we assign it to a dynamic variable to avoid special cases
292 if (plibNewPosition
== 0)
294 plibNewPosition
= &newPosition
;
298 * The file pointer is moved depending on the given "function"
303 case STREAM_SEEK_SET
:
304 plibNewPosition
->u
.HighPart
= 0;
305 plibNewPosition
->u
.LowPart
= 0;
307 case STREAM_SEEK_CUR
:
308 *plibNewPosition
= This
->currentPosition
;
310 case STREAM_SEEK_END
:
311 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, This
->dirEntry
, ¤tEntry
);
312 if (FAILED(hr
)) return hr
;
313 *plibNewPosition
= currentEntry
.size
;
316 WARN("invalid dwOrigin %ld\n", dwOrigin
);
317 return STG_E_INVALIDFUNCTION
;
320 plibNewPosition
->QuadPart
+= dlibMove
.QuadPart
;
323 * tell the caller what we calculated
325 This
->currentPosition
= *plibNewPosition
;
331 * This method is part of the IStream interface.
333 * It will change the size of a stream.
335 * See the documentation of IStream for more info.
337 static HRESULT WINAPI
StgStreamImpl_SetSize(
339 ULARGE_INTEGER libNewSize
) /* [in] */
341 StgStreamImpl
* This
= impl_from_IStream(iface
);
345 TRACE("%p, %ld.\n", iface
, libNewSize
.u
.LowPart
);
347 if(!This
->parentStorage
)
349 WARN("storage reverted\n");
350 return STG_E_REVERTED
;
356 if (libNewSize
.u
.HighPart
!= 0)
358 WARN("invalid value for libNewSize.u.HighPart %ld\n", libNewSize
.u
.HighPart
);
359 return STG_E_INVALIDFUNCTION
;
363 * Do we have permission?
365 if (!(This
->grfMode
& (STGM_WRITE
| STGM_READWRITE
)))
367 WARN("access denied\n");
368 return STG_E_ACCESSDENIED
;
371 hr
= StorageBaseImpl_StreamSetSize(This
->parentStorage
, This
->dirEntry
, libNewSize
);
374 hr
= StorageBaseImpl_Flush(This
->parentStorage
);
380 * This method is part of the IStream interface.
382 * It will copy the 'cb' Bytes to 'pstm' IStream.
384 * See the documentation of IStream for more info.
386 static HRESULT WINAPI
StgStreamImpl_CopyTo(
388 IStream
* pstm
, /* [unique][in] */
389 ULARGE_INTEGER cb
, /* [in] */
390 ULARGE_INTEGER
* pcbRead
, /* [out] */
391 ULARGE_INTEGER
* pcbWritten
) /* [out] */
393 StgStreamImpl
* This
= impl_from_IStream(iface
);
396 ULONG bytesRead
, bytesWritten
, copySize
;
397 ULARGE_INTEGER totalBytesRead
;
398 ULARGE_INTEGER totalBytesWritten
;
400 TRACE("%p, %p, %ld, %p, %p.\n", iface
, pstm
, cb
.u
.LowPart
, pcbRead
, pcbWritten
);
406 if (!This
->parentStorage
)
408 WARN("storage reverted\n");
409 return STG_E_REVERTED
;
413 return STG_E_INVALIDPOINTER
;
415 totalBytesRead
.QuadPart
= 0;
416 totalBytesWritten
.QuadPart
= 0;
418 while ( cb
.QuadPart
> 0 )
420 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
421 copySize
= sizeof(tmpBuffer
);
423 copySize
= cb
.u
.LowPart
;
425 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
427 totalBytesRead
.QuadPart
+= bytesRead
;
429 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
431 totalBytesWritten
.QuadPart
+= bytesWritten
;
434 * Check that read & write operations were successful
436 if (bytesRead
!= bytesWritten
)
438 hr
= STG_E_MEDIUMFULL
;
439 WARN("medium full\n");
443 if (bytesRead
!=copySize
)
446 cb
.QuadPart
-= bytesRead
;
449 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
450 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
456 * This method is part of the IStream interface.
458 * For streams contained in structured storages, this method
459 * does nothing. This is what the documentation tells us.
461 * See the documentation of IStream for more info.
463 static HRESULT WINAPI
StgStreamImpl_Commit(
465 DWORD grfCommitFlags
) /* [in] */
467 StgStreamImpl
* This
= impl_from_IStream(iface
);
469 if (!This
->parentStorage
)
471 WARN("storage reverted\n");
472 return STG_E_REVERTED
;
475 return StorageBaseImpl_Flush(This
->parentStorage
);
479 * This method is part of the IStream interface.
481 * For streams contained in structured storages, this method
482 * does nothing. This is what the documentation tells us.
484 * See the documentation of IStream for more info.
486 static HRESULT WINAPI
StgStreamImpl_Revert(
492 static HRESULT WINAPI
StgStreamImpl_LockRegion(
494 ULARGE_INTEGER libOffset
, /* [in] */
495 ULARGE_INTEGER cb
, /* [in] */
496 DWORD dwLockType
) /* [in] */
498 StgStreamImpl
* This
= impl_from_IStream(iface
);
500 if (!This
->parentStorage
)
502 WARN("storage reverted\n");
503 return STG_E_REVERTED
;
506 FIXME("not implemented!\n");
510 static HRESULT WINAPI
StgStreamImpl_UnlockRegion(
512 ULARGE_INTEGER libOffset
, /* [in] */
513 ULARGE_INTEGER cb
, /* [in] */
514 DWORD dwLockType
) /* [in] */
516 StgStreamImpl
* This
= impl_from_IStream(iface
);
518 if (!This
->parentStorage
)
520 WARN("storage reverted\n");
521 return STG_E_REVERTED
;
524 FIXME("not implemented!\n");
529 * This method is part of the IStream interface.
531 * This method returns information about the current
534 * See the documentation of IStream for more info.
536 static HRESULT WINAPI
StgStreamImpl_Stat(
538 STATSTG
* pstatstg
, /* [out] */
539 DWORD grfStatFlag
) /* [in] */
541 StgStreamImpl
* This
= impl_from_IStream(iface
);
543 DirEntry currentEntry
;
546 TRACE("%p, %p, %#lx.\n", This
, pstatstg
, grfStatFlag
);
549 * if stream has no parent, return STG_E_REVERTED
552 if (!This
->parentStorage
)
554 WARN("storage reverted\n");
555 return STG_E_REVERTED
;
559 * Read the information from the directory entry.
561 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
567 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
572 pstatstg
->grfMode
= This
->grfMode
;
574 /* In simple create mode cbSize is the current pos */
575 if((This
->parentStorage
->openFlags
& STGM_SIMPLE
) && This
->parentStorage
->create
)
576 pstatstg
->cbSize
= This
->currentPosition
;
581 WARN("failed to read entry\n");
586 * This method is part of the IStream interface.
588 * This method returns a clone of the interface that allows for
589 * another seek pointer
591 * See the documentation of IStream for more info.
593 * I am not totally sure what I am doing here but I presume that this
594 * should be basically as simple as creating a new stream with the same
595 * parent etc and positioning its seek cursor.
597 static HRESULT WINAPI
StgStreamImpl_Clone(
599 IStream
** ppstm
) /* [out] */
601 StgStreamImpl
* This
= impl_from_IStream(iface
);
602 StgStreamImpl
* new_stream
;
603 LARGE_INTEGER seek_pos
;
605 TRACE("%p %p\n", This
, ppstm
);
611 if (!This
->parentStorage
)
612 return STG_E_REVERTED
;
615 return STG_E_INVALIDPOINTER
;
617 new_stream
= StgStreamImpl_Construct (This
->parentStorage
, This
->grfMode
, This
->dirEntry
);
620 return STG_E_INSUFFICIENTMEMORY
; /* Currently the only reason for new_stream=0 */
622 *ppstm
= &new_stream
->IStream_iface
;
623 IStream_AddRef(*ppstm
);
625 seek_pos
.QuadPart
= This
->currentPosition
.QuadPart
;
627 return IStream_Seek(*ppstm
, seek_pos
, STREAM_SEEK_SET
, NULL
);
631 * Virtual function table for the StgStreamImpl class.
633 static const IStreamVtbl StgStreamVtbl
=
635 StgStreamImpl_QueryInterface
,
636 StgStreamImpl_AddRef
,
637 StgStreamImpl_Release
,
641 StgStreamImpl_SetSize
,
642 StgStreamImpl_CopyTo
,
643 StgStreamImpl_Commit
,
644 StgStreamImpl_Revert
,
645 StgStreamImpl_LockRegion
,
646 StgStreamImpl_UnlockRegion
,
651 /******************************************************************************
652 ** StgStreamImpl implementation
656 * This is the constructor for the StgStreamImpl class.
659 * parentStorage - Pointer to the storage that contains the stream to open
660 * dirEntry - Index of the directory entry that points to this stream.
662 StgStreamImpl
* StgStreamImpl_Construct(
663 StorageBaseImpl
* parentStorage
,
667 StgStreamImpl
* newStream
;
669 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
674 * Set-up the virtual function table and reference count.
676 newStream
->IStream_iface
.lpVtbl
= &StgStreamVtbl
;
679 newStream
->parentStorage
= parentStorage
;
682 * We want to nail-down the reference to the storage in case the
683 * stream out-lives the storage in the client application.
685 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
687 * No, don't do this. Some apps call IStorage_Release without
688 * calling IStream_Release first. If we grab a reference the
689 * file is not closed, and the app fails when it tries to
690 * reopen the file (Easy-PC, for example)
693 newStream
->grfMode
= grfMode
;
694 newStream
->dirEntry
= dirEntry
;
697 * Start the stream at the beginning.
699 newStream
->currentPosition
.u
.HighPart
= 0;
700 newStream
->currentPosition
.u
.LowPart
= 0;
702 /* add us to the storage's list of active streams */
703 StorageBaseImpl_AddStream(parentStorage
, newStream
);