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
27 #include "storage32.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
32 * This implements the IUnknown method QueryInterface for this
35 static HRESULT WINAPI
StgStreamImpl_QueryInterface(
37 REFIID riid
, /* [in] */
38 void** ppvObject
) /* [iid_is][out] */
40 StgStreamImpl
* This
= impl_from_IStream(iface
);
47 if (IsEqualIID(&IID_IUnknown
, riid
) ||
48 IsEqualIID(&IID_ISequentialStream
, riid
) ||
49 IsEqualIID(&IID_IStream
, riid
))
51 *ppvObject
= &This
->IStream_iface
;
56 IStream_AddRef(iface
);
62 * This implements the IUnknown method AddRef for this
65 static ULONG WINAPI
StgStreamImpl_AddRef(
68 StgStreamImpl
* This
= impl_from_IStream(iface
);
69 return InterlockedIncrement(&This
->ref
);
73 * This implements the IUnknown method Release for this
76 static ULONG WINAPI
StgStreamImpl_Release(
79 StgStreamImpl
* This
= impl_from_IStream(iface
);
80 ULONG ref
= InterlockedDecrement(&This
->ref
);
84 TRACE("(%p)\n", This
);
87 * Release the reference we are holding on the parent storage.
88 * IStorage_Release(&This->parentStorage->IStorage_iface);
90 * No, don't do this. Some apps call IStorage_Release without
91 * calling IStream_Release first. If we grab a reference the
92 * file is not closed, and the app fails when it tries to
93 * reopen the file (Easy-PC, for example). Just inform the
94 * storage that we have closed the stream
97 if (This
->parentStorage
)
98 StorageBaseImpl_RemoveStream(This
->parentStorage
, This
);
99 This
->parentStorage
= 0;
100 HeapFree(GetProcessHeap(), 0, This
);
107 * This method is part of the ISequentialStream interface.
109 * It reads a block of information from the stream at the current
110 * position. It then moves the current position at the end of the
113 * See the documentation of ISequentialStream for more info.
115 static HRESULT WINAPI
StgStreamImpl_Read(
117 void* pv
, /* [length_is][size_is][out] */
119 ULONG
* pcbRead
) /* [out] */
121 StgStreamImpl
* This
= impl_from_IStream(iface
);
123 ULONG bytesReadBuffer
;
126 TRACE("(%p, %p, %d, %p)\n",
127 iface
, pv
, cb
, pcbRead
);
129 if (!This
->parentStorage
)
131 WARN("storage reverted\n");
132 return STG_E_REVERTED
;
136 * If the caller is not interested in the number of bytes read,
137 * we use another buffer to avoid "if" statements in the code.
140 pcbRead
= &bytesReadBuffer
;
142 res
= StorageBaseImpl_StreamReadAt(This
->parentStorage
,
144 This
->currentPosition
,
152 * Advance the pointer for the number of positions read.
154 This
->currentPosition
.u
.LowPart
+= *pcbRead
;
157 TRACE("<-- %08x\n", res
);
162 * This method is part of the ISequentialStream interface.
164 * It writes a block of information to the stream at the current
165 * position. It then moves the current position at the end of the
166 * written block. If the stream is too small to fit the block,
167 * the stream is grown to fit.
169 * See the documentation of ISequentialStream for more info.
171 static HRESULT WINAPI
StgStreamImpl_Write(
173 const void* pv
, /* [size_is][in] */
175 ULONG
* pcbWritten
) /* [out] */
177 StgStreamImpl
* This
= impl_from_IStream(iface
);
179 ULONG bytesWritten
= 0;
182 TRACE("(%p, %p, %d, %p)\n",
183 iface
, pv
, cb
, pcbWritten
);
186 * Do we have permission to write to this stream?
188 switch(STGM_ACCESS_MODE(This
->grfMode
))
194 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This
->grfMode
));
195 return STG_E_ACCESSDENIED
;
199 return STG_E_INVALIDPOINTER
;
201 if (!This
->parentStorage
)
203 WARN("storage reverted\n");
204 return STG_E_REVERTED
;
208 * If the caller is not interested in the number of bytes written,
209 * we use another buffer to avoid "if" statements in the code.
212 pcbWritten
= &bytesWritten
;
215 * Initialize the out parameter
221 TRACE("<-- S_OK, written 0\n");
225 res
= StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
227 This
->currentPosition
,
233 * Advance the position pointer for the number of positions written.
235 This
->currentPosition
.u
.LowPart
+= *pcbWritten
;
238 res
= StorageBaseImpl_Flush(This
->parentStorage
);
240 TRACE("<-- S_OK, written %u\n", *pcbWritten
);
245 * This method is part of the IStream interface.
247 * It will move the current stream pointer according to the parameters
250 * See the documentation of IStream for more info.
252 static HRESULT WINAPI
StgStreamImpl_Seek(
254 LARGE_INTEGER dlibMove
, /* [in] */
255 DWORD dwOrigin
, /* [in] */
256 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
258 StgStreamImpl
* This
= impl_from_IStream(iface
);
260 ULARGE_INTEGER newPosition
;
261 DirEntry currentEntry
;
264 TRACE("(%p, %d, %d, %p)\n",
265 iface
, dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
268 * fail if the stream has no parent (as does windows)
271 if (!This
->parentStorage
)
273 WARN("storage reverted\n");
274 return STG_E_REVERTED
;
278 * The caller is allowed to pass in NULL as the new position return value.
279 * If it happens, we assign it to a dynamic variable to avoid special cases
282 if (plibNewPosition
== 0)
284 plibNewPosition
= &newPosition
;
288 * The file pointer is moved depending on the given "function"
293 case STREAM_SEEK_SET
:
294 plibNewPosition
->u
.HighPart
= 0;
295 plibNewPosition
->u
.LowPart
= 0;
297 case STREAM_SEEK_CUR
:
298 *plibNewPosition
= This
->currentPosition
;
300 case STREAM_SEEK_END
:
301 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, This
->dirEntry
, ¤tEntry
);
302 if (FAILED(hr
)) return hr
;
303 *plibNewPosition
= currentEntry
.size
;
306 WARN("invalid dwOrigin %d\n", dwOrigin
);
307 return STG_E_INVALIDFUNCTION
;
310 plibNewPosition
->QuadPart
+= dlibMove
.QuadPart
;
313 * tell the caller what we calculated
315 This
->currentPosition
= *plibNewPosition
;
321 * This method is part of the IStream interface.
323 * It will change the size of a stream.
325 * See the documentation of IStream for more info.
327 static HRESULT WINAPI
StgStreamImpl_SetSize(
329 ULARGE_INTEGER libNewSize
) /* [in] */
331 StgStreamImpl
* This
= impl_from_IStream(iface
);
335 TRACE("(%p, %d)\n", iface
, libNewSize
.u
.LowPart
);
337 if(!This
->parentStorage
)
339 WARN("storage reverted\n");
340 return STG_E_REVERTED
;
346 if (libNewSize
.u
.HighPart
!= 0)
348 WARN("invalid value for libNewSize.u.HighPart %d\n", libNewSize
.u
.HighPart
);
349 return STG_E_INVALIDFUNCTION
;
353 * Do we have permission?
355 if (!(This
->grfMode
& (STGM_WRITE
| STGM_READWRITE
)))
357 WARN("access denied\n");
358 return STG_E_ACCESSDENIED
;
361 hr
= StorageBaseImpl_StreamSetSize(This
->parentStorage
, This
->dirEntry
, libNewSize
);
364 hr
= StorageBaseImpl_Flush(This
->parentStorage
);
370 * This method is part of the IStream interface.
372 * It will copy the 'cb' Bytes to 'pstm' IStream.
374 * See the documentation of IStream for more info.
376 static HRESULT WINAPI
StgStreamImpl_CopyTo(
378 IStream
* pstm
, /* [unique][in] */
379 ULARGE_INTEGER cb
, /* [in] */
380 ULARGE_INTEGER
* pcbRead
, /* [out] */
381 ULARGE_INTEGER
* pcbWritten
) /* [out] */
383 StgStreamImpl
* This
= impl_from_IStream(iface
);
386 ULONG bytesRead
, bytesWritten
, copySize
;
387 ULARGE_INTEGER totalBytesRead
;
388 ULARGE_INTEGER totalBytesWritten
;
390 TRACE("(%p, %p, %d, %p, %p)\n",
391 iface
, pstm
, cb
.u
.LowPart
, pcbRead
, pcbWritten
);
397 if (!This
->parentStorage
)
399 WARN("storage reverted\n");
400 return STG_E_REVERTED
;
404 return STG_E_INVALIDPOINTER
;
406 totalBytesRead
.QuadPart
= 0;
407 totalBytesWritten
.QuadPart
= 0;
409 while ( cb
.QuadPart
> 0 )
411 if ( cb
.QuadPart
>= sizeof(tmpBuffer
) )
412 copySize
= sizeof(tmpBuffer
);
414 copySize
= cb
.u
.LowPart
;
416 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
418 totalBytesRead
.QuadPart
+= bytesRead
;
420 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
422 totalBytesWritten
.QuadPart
+= bytesWritten
;
425 * Check that read & write operations were successful
427 if (bytesRead
!= bytesWritten
)
429 hr
= STG_E_MEDIUMFULL
;
430 WARN("medium full\n");
434 if (bytesRead
!=copySize
)
437 cb
.QuadPart
-= bytesRead
;
440 if (pcbRead
) pcbRead
->QuadPart
= totalBytesRead
.QuadPart
;
441 if (pcbWritten
) pcbWritten
->QuadPart
= totalBytesWritten
.QuadPart
;
447 * This method is part of the IStream interface.
449 * For streams contained in structured storages, this method
450 * does nothing. This is what the documentation tells us.
452 * See the documentation of IStream for more info.
454 static HRESULT WINAPI
StgStreamImpl_Commit(
456 DWORD grfCommitFlags
) /* [in] */
458 StgStreamImpl
* This
= impl_from_IStream(iface
);
460 if (!This
->parentStorage
)
462 WARN("storage reverted\n");
463 return STG_E_REVERTED
;
466 return StorageBaseImpl_Flush(This
->parentStorage
);
470 * This method is part of the IStream interface.
472 * For streams contained in structured storages, this method
473 * does nothing. This is what the documentation tells us.
475 * See the documentation of IStream for more info.
477 static HRESULT WINAPI
StgStreamImpl_Revert(
483 static HRESULT WINAPI
StgStreamImpl_LockRegion(
485 ULARGE_INTEGER libOffset
, /* [in] */
486 ULARGE_INTEGER cb
, /* [in] */
487 DWORD dwLockType
) /* [in] */
489 StgStreamImpl
* This
= impl_from_IStream(iface
);
491 if (!This
->parentStorage
)
493 WARN("storage reverted\n");
494 return STG_E_REVERTED
;
497 FIXME("not implemented!\n");
501 static HRESULT WINAPI
StgStreamImpl_UnlockRegion(
503 ULARGE_INTEGER libOffset
, /* [in] */
504 ULARGE_INTEGER cb
, /* [in] */
505 DWORD dwLockType
) /* [in] */
507 StgStreamImpl
* This
= impl_from_IStream(iface
);
509 if (!This
->parentStorage
)
511 WARN("storage reverted\n");
512 return STG_E_REVERTED
;
515 FIXME("not implemented!\n");
520 * This method is part of the IStream interface.
522 * This method returns information about the current
525 * See the documentation of IStream for more info.
527 static HRESULT WINAPI
StgStreamImpl_Stat(
529 STATSTG
* pstatstg
, /* [out] */
530 DWORD grfStatFlag
) /* [in] */
532 StgStreamImpl
* This
= impl_from_IStream(iface
);
534 DirEntry currentEntry
;
537 TRACE("%p %p %d\n", This
, pstatstg
, grfStatFlag
);
540 * if stream has no parent, return STG_E_REVERTED
543 if (!This
->parentStorage
)
545 WARN("storage reverted\n");
546 return STG_E_REVERTED
;
550 * Read the information from the directory entry.
552 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
558 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
563 pstatstg
->grfMode
= This
->grfMode
;
565 /* In simple create mode cbSize is the current pos */
566 if((This
->parentStorage
->openFlags
& STGM_SIMPLE
) && This
->parentStorage
->create
)
567 pstatstg
->cbSize
= This
->currentPosition
;
572 WARN("failed to read entry\n");
577 * This method is part of the IStream interface.
579 * This method returns a clone of the interface that allows for
580 * another seek pointer
582 * See the documentation of IStream for more info.
584 * I am not totally sure what I am doing here but I presume that this
585 * should be basically as simple as creating a new stream with the same
586 * parent etc and positioning its seek cursor.
588 static HRESULT WINAPI
StgStreamImpl_Clone(
590 IStream
** ppstm
) /* [out] */
592 StgStreamImpl
* This
= impl_from_IStream(iface
);
594 StgStreamImpl
* new_stream
;
595 LARGE_INTEGER seek_pos
;
597 TRACE("%p %p\n", This
, ppstm
);
603 if (!This
->parentStorage
)
604 return STG_E_REVERTED
;
607 return STG_E_INVALIDPOINTER
;
609 new_stream
= StgStreamImpl_Construct (This
->parentStorage
, This
->grfMode
, This
->dirEntry
);
612 return STG_E_INSUFFICIENTMEMORY
; /* Currently the only reason for new_stream=0 */
614 *ppstm
= &new_stream
->IStream_iface
;
615 IStream_AddRef(*ppstm
);
617 seek_pos
.QuadPart
= This
->currentPosition
.QuadPart
;
619 hres
= IStream_Seek(*ppstm
, seek_pos
, STREAM_SEEK_SET
, NULL
);
621 assert (SUCCEEDED(hres
));
627 * Virtual function table for the StgStreamImpl class.
629 static const IStreamVtbl StgStreamVtbl
=
631 StgStreamImpl_QueryInterface
,
632 StgStreamImpl_AddRef
,
633 StgStreamImpl_Release
,
637 StgStreamImpl_SetSize
,
638 StgStreamImpl_CopyTo
,
639 StgStreamImpl_Commit
,
640 StgStreamImpl_Revert
,
641 StgStreamImpl_LockRegion
,
642 StgStreamImpl_UnlockRegion
,
647 /******************************************************************************
648 ** StgStreamImpl implementation
652 * This is the constructor for the StgStreamImpl class.
655 * parentStorage - Pointer to the storage that contains the stream to open
656 * dirEntry - Index of the directory entry that points to this stream.
658 StgStreamImpl
* StgStreamImpl_Construct(
659 StorageBaseImpl
* parentStorage
,
663 StgStreamImpl
* newStream
;
665 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
670 * Set-up the virtual function table and reference count.
672 newStream
->IStream_iface
.lpVtbl
= &StgStreamVtbl
;
675 newStream
->parentStorage
= parentStorage
;
678 * We want to nail-down the reference to the storage in case the
679 * stream out-lives the storage in the client application.
681 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
683 * No, don't do this. Some apps call IStorage_Release without
684 * calling IStream_Release first. If we grab a reference the
685 * file is not closed, and the app fails when it tries to
686 * reopen the file (Easy-PC, for example)
689 newStream
->grfMode
= grfMode
;
690 newStream
->dirEntry
= dirEntry
;
693 * Start the stream at the beginning.
695 newStream
->currentPosition
.u
.HighPart
= 0;
696 newStream
->currentPosition
.u
.LowPart
= 0;
698 /* add us to the storage's list of active streams */
699 StorageBaseImpl_AddStream(parentStorage
, newStream
);