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
36 #include "wine/debug.h"
38 #include "storage32.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
43 * This implements the IUnknown method QueryInterface for this
46 static HRESULT WINAPI
StgStreamImpl_QueryInterface(
48 REFIID riid
, /* [in] */
49 void** ppvObject
) /* [iid_is][out] */
51 StgStreamImpl
* This
= impl_from_IStream(iface
);
58 if (IsEqualIID(&IID_IUnknown
, riid
) ||
59 IsEqualIID(&IID_ISequentialStream
, riid
) ||
60 IsEqualIID(&IID_IStream
, riid
))
62 *ppvObject
= &This
->IStream_iface
;
67 IStream_AddRef(iface
);
73 * This implements the IUnknown method AddRef for this
76 static ULONG WINAPI
StgStreamImpl_AddRef(
79 StgStreamImpl
* This
= impl_from_IStream(iface
);
80 return InterlockedIncrement(&This
->ref
);
84 * This implements the IUnknown method Release for this
87 static ULONG WINAPI
StgStreamImpl_Release(
90 StgStreamImpl
* This
= impl_from_IStream(iface
);
91 ULONG ref
= InterlockedDecrement(&This
->ref
);
95 TRACE("(%p)\n", This
);
98 * Release the reference we are holding on the parent storage.
99 * IStorage_Release(&This->parentStorage->IStorage_iface);
101 * No, don't do this. Some apps call IStorage_Release without
102 * calling IStream_Release first. If we grab a reference the
103 * file is not closed, and the app fails when it tries to
104 * reopen the file (Easy-PC, for example). Just inform the
105 * storage that we have closed the stream
108 if (This
->parentStorage
)
109 StorageBaseImpl_RemoveStream(This
->parentStorage
, This
);
110 This
->parentStorage
= 0;
111 HeapFree(GetProcessHeap(), 0, This
);
118 * This method is part of the ISequentialStream interface.
120 * It reads a block of information from the stream at the current
121 * position. It then moves the current position at the end of the
124 * See the documentation of ISequentialStream for more info.
126 static HRESULT WINAPI
StgStreamImpl_Read(
128 void* pv
, /* [length_is][size_is][out] */
130 ULONG
* pcbRead
) /* [out] */
132 StgStreamImpl
* This
= impl_from_IStream(iface
);
134 ULONG bytesReadBuffer
;
137 TRACE("%p, %p, %lu, %p.\n", iface
, pv
, cb
, pcbRead
);
139 if (!This
->parentStorage
)
141 WARN("storage reverted\n");
142 return STG_E_REVERTED
;
146 * If the caller is not interested in the number of bytes read,
147 * we use another buffer to avoid "if" statements in the code.
150 pcbRead
= &bytesReadBuffer
;
152 res
= StorageBaseImpl_StreamReadAt(This
->parentStorage
,
154 This
->currentPosition
,
162 * Advance the pointer for the number of positions read.
164 This
->currentPosition
.QuadPart
+= *pcbRead
;
167 TRACE("<-- %#lx\n", res
);
172 * This method is part of the ISequentialStream interface.
174 * It writes a block of information to the stream at the current
175 * position. It then moves the current position at the end of the
176 * written block. If the stream is too small to fit the block,
177 * the stream is grown to fit.
179 * See the documentation of ISequentialStream for more info.
181 static HRESULT WINAPI
StgStreamImpl_Write(
183 const void* pv
, /* [size_is][in] */
185 ULONG
* pcbWritten
) /* [out] */
187 StgStreamImpl
* This
= impl_from_IStream(iface
);
189 ULONG bytesWritten
= 0;
192 TRACE("%p, %p, %lu, %p.\n", iface
, pv
, cb
, pcbWritten
);
195 * Do we have permission to write to this stream?
197 switch(STGM_ACCESS_MODE(This
->grfMode
))
203 WARN("access denied by flags: %#lx\n", STGM_ACCESS_MODE(This
->grfMode
));
204 return STG_E_ACCESSDENIED
;
208 return STG_E_INVALIDPOINTER
;
210 if (!This
->parentStorage
)
212 WARN("storage reverted\n");
213 return STG_E_REVERTED
;
217 * If the caller is not interested in the number of bytes written,
218 * we use another buffer to avoid "if" statements in the code.
221 pcbWritten
= &bytesWritten
;
224 * Initialize the out parameter
230 TRACE("<-- S_OK, written 0\n");
234 res
= StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
236 This
->currentPosition
,
242 * Advance the position pointer for the number of positions written.
244 This
->currentPosition
.QuadPart
+= *pcbWritten
;
247 res
= StorageBaseImpl_Flush(This
->parentStorage
);
249 TRACE("<-- %#lx, written %lu\n", res
, *pcbWritten
);
254 * This method is part of the IStream interface.
256 * It will move the current stream pointer according to the parameters
259 * See the documentation of IStream for more info.
261 static HRESULT WINAPI
StgStreamImpl_Seek(
263 LARGE_INTEGER dlibMove
, /* [in] */
264 DWORD dwOrigin
, /* [in] */
265 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
267 StgStreamImpl
* This
= impl_from_IStream(iface
);
269 ULARGE_INTEGER newPosition
;
270 DirEntry currentEntry
;
273 TRACE("%p, %ld, %ld, %p.\n", iface
, dlibMove
.LowPart
, dwOrigin
, plibNewPosition
);
276 * fail if the stream has no parent (as does windows)
279 if (!This
->parentStorage
)
281 WARN("storage reverted\n");
282 return STG_E_REVERTED
;
286 * The caller is allowed to pass in NULL as the new position return value.
287 * If it happens, we assign it to a dynamic variable to avoid special cases
290 if (plibNewPosition
== 0)
292 plibNewPosition
= &newPosition
;
296 * The file pointer is moved depending on the given "function"
301 case STREAM_SEEK_SET
:
302 plibNewPosition
->u
.HighPart
= 0;
303 plibNewPosition
->u
.LowPart
= 0;
305 case STREAM_SEEK_CUR
:
306 *plibNewPosition
= This
->currentPosition
;
308 case STREAM_SEEK_END
:
309 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, This
->dirEntry
, ¤tEntry
);
310 if (FAILED(hr
)) return hr
;
311 *plibNewPosition
= currentEntry
.size
;
314 WARN("invalid dwOrigin %ld\n", dwOrigin
);
315 return STG_E_INVALIDFUNCTION
;
318 plibNewPosition
->QuadPart
+= dlibMove
.QuadPart
;
321 * tell the caller what we calculated
323 This
->currentPosition
= *plibNewPosition
;
329 * This method is part of the IStream interface.
331 * It will change the size of a stream.
333 * See the documentation of IStream for more info.
335 static HRESULT WINAPI
StgStreamImpl_SetSize(
337 ULARGE_INTEGER libNewSize
) /* [in] */
339 StgStreamImpl
* This
= impl_from_IStream(iface
);
343 TRACE("%p, %ld.\n", iface
, libNewSize
.LowPart
);
345 if(!This
->parentStorage
)
347 WARN("storage reverted\n");
348 return STG_E_REVERTED
;
354 if (libNewSize
.HighPart
!= 0)
356 WARN("invalid value for libNewSize.HighPart %ld\n", libNewSize
.HighPart
);
357 return STG_E_INVALIDFUNCTION
;
361 * Do we have permission?
363 if (!(This
->grfMode
& (STGM_WRITE
| STGM_READWRITE
)))
365 WARN("access denied\n");
366 return STG_E_ACCESSDENIED
;
369 hr
= StorageBaseImpl_StreamSetSize(This
->parentStorage
, This
->dirEntry
, libNewSize
);
372 hr
= StorageBaseImpl_Flush(This
->parentStorage
);
378 * This method is part of the IStream interface.
380 * It will copy the 'cb' Bytes to 'pstm' IStream.
382 * See the documentation of IStream for more info.
384 static HRESULT WINAPI
StgStreamImpl_CopyTo(
386 IStream
* pstm
, /* [unique][in] */
387 ULARGE_INTEGER cb
, /* [in] */
388 ULARGE_INTEGER
* pcbRead
, /* [out] */
389 ULARGE_INTEGER
* pcbWritten
) /* [out] */
391 StgStreamImpl
* This
= impl_from_IStream(iface
);
394 ULONG bytesRead
, bytesWritten
, copySize
;
395 ULARGE_INTEGER totalBytesRead
;
396 ULARGE_INTEGER totalBytesWritten
;
398 TRACE("%p, %p, %ld, %p, %p.\n", iface
, pstm
, cb
.LowPart
, pcbRead
, pcbWritten
);
404 if (!This
->parentStorage
)
406 WARN("storage reverted\n");
407 return STG_E_REVERTED
;
411 return STG_E_INVALIDPOINTER
;
413 totalBytesRead
.QuadPart
= 0;
414 totalBytesWritten
.QuadPart
= 0;
416 while ( cb
.QuadPart
> 0 )
418 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
419 copySize
= sizeof(tmpBuffer
);
421 copySize
= cb
.LowPart
;
423 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
425 totalBytesRead
.QuadPart
+= bytesRead
;
427 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
429 totalBytesWritten
.QuadPart
+= bytesWritten
;
432 * Check that read & write operations were successful
434 if (bytesRead
!= bytesWritten
)
436 hr
= STG_E_MEDIUMFULL
;
437 WARN("medium full\n");
441 if (bytesRead
!=copySize
)
444 cb
.QuadPart
-= bytesRead
;
447 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
448 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
454 * This method is part of the IStream interface.
456 * For streams contained in structured storages, this method
457 * does nothing. This is what the documentation tells us.
459 * See the documentation of IStream for more info.
461 static HRESULT WINAPI
StgStreamImpl_Commit(
463 DWORD grfCommitFlags
) /* [in] */
465 StgStreamImpl
* This
= impl_from_IStream(iface
);
467 if (!This
->parentStorage
)
469 WARN("storage reverted\n");
470 return STG_E_REVERTED
;
473 return StorageBaseImpl_Flush(This
->parentStorage
);
477 * This method is part of the IStream interface.
479 * For streams contained in structured storages, this method
480 * does nothing. This is what the documentation tells us.
482 * See the documentation of IStream for more info.
484 static HRESULT WINAPI
StgStreamImpl_Revert(
490 static HRESULT WINAPI
StgStreamImpl_LockRegion(
492 ULARGE_INTEGER libOffset
, /* [in] */
493 ULARGE_INTEGER cb
, /* [in] */
494 DWORD dwLockType
) /* [in] */
496 StgStreamImpl
* This
= impl_from_IStream(iface
);
498 if (!This
->parentStorage
)
500 WARN("storage reverted\n");
501 return STG_E_REVERTED
;
504 FIXME("not implemented!\n");
508 static HRESULT WINAPI
StgStreamImpl_UnlockRegion(
510 ULARGE_INTEGER libOffset
, /* [in] */
511 ULARGE_INTEGER cb
, /* [in] */
512 DWORD dwLockType
) /* [in] */
514 StgStreamImpl
* This
= impl_from_IStream(iface
);
516 if (!This
->parentStorage
)
518 WARN("storage reverted\n");
519 return STG_E_REVERTED
;
522 FIXME("not implemented!\n");
527 * This method is part of the IStream interface.
529 * This method returns information about the current
532 * See the documentation of IStream for more info.
534 static HRESULT WINAPI
StgStreamImpl_Stat(
536 STATSTG
* pstatstg
, /* [out] */
537 DWORD grfStatFlag
) /* [in] */
539 StgStreamImpl
* This
= impl_from_IStream(iface
);
541 DirEntry currentEntry
;
544 TRACE("%p, %p, %#lx.\n", This
, pstatstg
, grfStatFlag
);
547 * if stream has no parent, return STG_E_REVERTED
550 if (!This
->parentStorage
)
552 WARN("storage reverted\n");
553 return STG_E_REVERTED
;
557 * Read the information from the directory entry.
559 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
565 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
570 pstatstg
->grfMode
= This
->grfMode
;
572 /* In simple create mode cbSize is the current pos */
573 if((This
->parentStorage
->openFlags
& STGM_SIMPLE
) && This
->parentStorage
->create
)
574 pstatstg
->cbSize
= This
->currentPosition
;
579 WARN("failed to read entry\n");
584 * This method is part of the IStream interface.
586 * This method returns a clone of the interface that allows for
587 * another seek pointer
589 * See the documentation of IStream for more info.
591 * I am not totally sure what I am doing here but I presume that this
592 * should be basically as simple as creating a new stream with the same
593 * parent etc and positioning its seek cursor.
595 static HRESULT WINAPI
StgStreamImpl_Clone(
597 IStream
** ppstm
) /* [out] */
599 StgStreamImpl
* This
= impl_from_IStream(iface
);
600 StgStreamImpl
* new_stream
;
601 LARGE_INTEGER seek_pos
;
603 TRACE("%p %p\n", This
, ppstm
);
609 if (!This
->parentStorage
)
610 return STG_E_REVERTED
;
613 return STG_E_INVALIDPOINTER
;
615 new_stream
= StgStreamImpl_Construct (This
->parentStorage
, This
->grfMode
, This
->dirEntry
);
618 return STG_E_INSUFFICIENTMEMORY
; /* Currently the only reason for new_stream=0 */
620 *ppstm
= &new_stream
->IStream_iface
;
621 IStream_AddRef(*ppstm
);
623 seek_pos
.QuadPart
= This
->currentPosition
.QuadPart
;
625 return IStream_Seek(*ppstm
, seek_pos
, STREAM_SEEK_SET
, NULL
);
629 * Virtual function table for the StgStreamImpl class.
631 static const IStreamVtbl StgStreamVtbl
=
633 StgStreamImpl_QueryInterface
,
634 StgStreamImpl_AddRef
,
635 StgStreamImpl_Release
,
639 StgStreamImpl_SetSize
,
640 StgStreamImpl_CopyTo
,
641 StgStreamImpl_Commit
,
642 StgStreamImpl_Revert
,
643 StgStreamImpl_LockRegion
,
644 StgStreamImpl_UnlockRegion
,
649 /******************************************************************************
650 ** StgStreamImpl implementation
654 * This is the constructor for the StgStreamImpl class.
657 * parentStorage - Pointer to the storage that contains the stream to open
658 * dirEntry - Index of the directory entry that points to this stream.
660 StgStreamImpl
* StgStreamImpl_Construct(
661 StorageBaseImpl
* parentStorage
,
665 StgStreamImpl
* newStream
;
667 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
672 * Set-up the virtual function table and reference count.
674 newStream
->IStream_iface
.lpVtbl
= &StgStreamVtbl
;
677 newStream
->parentStorage
= parentStorage
;
680 * We want to nail-down the reference to the storage in case the
681 * stream out-lives the storage in the client application.
683 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
685 * No, don't do this. Some apps call IStorage_Release without
686 * calling IStream_Release first. If we grab a reference the
687 * file is not closed, and the app fails when it tries to
688 * reopen the file (Easy-PC, for example)
691 newStream
->grfMode
= grfMode
;
692 newStream
->dirEntry
= dirEntry
;
695 * Start the stream at the beginning.
697 newStream
->currentPosition
.HighPart
= 0;
698 newStream
->currentPosition
.LowPart
= 0;
700 /* add us to the storage's list of active streams */
701 StorageBaseImpl_AddStream(parentStorage
, newStream
);