mstask: Implement ITask::DeleteTrigger().
[wine.git] / dlls / ole32 / stg_stream.c
blob1a5f061250ea60a49b70d675f87d2258e0a3cb14
1 /*
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
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
31 #define COBJMACROS
32 #define NONAMELESSUNION
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
40 #include "storage32.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(storage);
44 /***
45 * This implements the IUnknown method QueryInterface for this
46 * class
48 static HRESULT WINAPI StgStreamImpl_QueryInterface(
49 IStream* iface,
50 REFIID riid, /* [in] */
51 void** ppvObject) /* [iid_is][out] */
53 StgStreamImpl* This = impl_from_IStream(iface);
55 if (ppvObject==0)
56 return E_INVALIDARG;
58 *ppvObject = 0;
60 if (IsEqualIID(&IID_IUnknown, riid) ||
61 IsEqualIID(&IID_ISequentialStream, riid) ||
62 IsEqualIID(&IID_IStream, riid))
64 *ppvObject = &This->IStream_iface;
66 else
67 return E_NOINTERFACE;
69 IStream_AddRef(iface);
71 return S_OK;
74 /***
75 * This implements the IUnknown method AddRef for this
76 * class
78 static ULONG WINAPI StgStreamImpl_AddRef(
79 IStream* iface)
81 StgStreamImpl* This = impl_from_IStream(iface);
82 return InterlockedIncrement(&This->ref);
85 /***
86 * This implements the IUnknown method Release for this
87 * class
89 static ULONG WINAPI StgStreamImpl_Release(
90 IStream* iface)
92 StgStreamImpl* This = impl_from_IStream(iface);
93 ULONG ref = InterlockedDecrement(&This->ref);
95 if (!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);
116 return ref;
119 /***
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
124 * read block
126 * See the documentation of ISequentialStream for more info.
128 static HRESULT WINAPI StgStreamImpl_Read(
129 IStream* iface,
130 void* pv, /* [length_is][size_is][out] */
131 ULONG cb, /* [in] */
132 ULONG* pcbRead) /* [out] */
134 StgStreamImpl* This = impl_from_IStream(iface);
136 ULONG bytesReadBuffer;
137 HRESULT res;
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.
152 if (pcbRead==0)
153 pcbRead = &bytesReadBuffer;
155 res = StorageBaseImpl_StreamReadAt(This->parentStorage,
156 This->dirEntry,
157 This->currentPosition,
160 pcbRead);
162 if (SUCCEEDED(res))
165 * Advance the pointer for the number of positions read.
167 This->currentPosition.QuadPart += *pcbRead;
170 TRACE("<-- %08x\n", res);
171 return res;
174 /***
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(
185 IStream* iface,
186 const void* pv, /* [size_is][in] */
187 ULONG cb, /* [in] */
188 ULONG* pcbWritten) /* [out] */
190 StgStreamImpl* This = impl_from_IStream(iface);
192 ULONG bytesWritten = 0;
193 HRESULT res;
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))
203 case STGM_WRITE:
204 case STGM_READWRITE:
205 break;
206 default:
207 WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode));
208 return STG_E_ACCESSDENIED;
211 if (!pv)
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.
224 if (pcbWritten == 0)
225 pcbWritten = &bytesWritten;
228 * Initialize the out parameter
230 *pcbWritten = 0;
232 if (cb == 0)
234 TRACE("<-- S_OK, written 0\n");
235 return S_OK;
238 res = StorageBaseImpl_StreamWriteAt(This->parentStorage,
239 This->dirEntry,
240 This->currentPosition,
243 pcbWritten);
246 * Advance the position pointer for the number of positions written.
248 This->currentPosition.QuadPart += *pcbWritten;
250 if (SUCCEEDED(res))
251 res = StorageBaseImpl_Flush(This->parentStorage);
253 TRACE("<-- %08x, written %u\n", res, *pcbWritten);
254 return res;
257 /***
258 * This method is part of the IStream interface.
260 * It will move the current stream pointer according to the parameters
261 * given.
263 * See the documentation of IStream for more info.
265 static HRESULT WINAPI StgStreamImpl_Seek(
266 IStream* iface,
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;
275 HRESULT hr;
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
293 * in the code below.
295 if (plibNewPosition == 0)
297 plibNewPosition = &newPosition;
301 * The file pointer is moved depending on the given "function"
302 * parameter.
304 switch (dwOrigin)
306 case STREAM_SEEK_SET:
307 plibNewPosition->u.HighPart = 0;
308 plibNewPosition->u.LowPart = 0;
309 break;
310 case STREAM_SEEK_CUR:
311 *plibNewPosition = This->currentPosition;
312 break;
313 case STREAM_SEEK_END:
314 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, &currentEntry);
315 if (FAILED(hr)) return hr;
316 *plibNewPosition = currentEntry.size;
317 break;
318 default:
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;
330 return S_OK;
333 /***
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(
341 IStream* iface,
342 ULARGE_INTEGER libNewSize) /* [in] */
344 StgStreamImpl* This = impl_from_IStream(iface);
346 HRESULT hr;
348 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
350 if(!This->parentStorage)
352 WARN("storage reverted\n");
353 return STG_E_REVERTED;
357 * As documented.
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);
376 if (SUCCEEDED(hr))
377 hr = StorageBaseImpl_Flush(This->parentStorage);
379 return hr;
382 /***
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(
390 IStream* iface,
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);
397 HRESULT hr = S_OK;
398 BYTE tmpBuffer[128];
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);
407 * Sanity check
410 if (!This->parentStorage)
412 WARN("storage reverted\n");
413 return STG_E_REVERTED;
416 if ( pstm == 0 )
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);
426 else
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");
444 break;
447 if (bytesRead!=copySize)
448 cb.QuadPart = 0;
449 else
450 cb.QuadPart -= bytesRead;
453 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
454 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
456 return hr;
459 /***
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(
468 IStream* iface,
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);
482 /***
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(
491 IStream* iface)
493 return S_OK;
496 static HRESULT WINAPI StgStreamImpl_LockRegion(
497 IStream* iface,
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");
511 return E_NOTIMPL;
514 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
515 IStream* iface,
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");
529 return E_NOTIMPL;
532 /***
533 * This method is part of the IStream interface.
535 * This method returns information about the current
536 * stream.
538 * See the documentation of IStream for more info.
540 static HRESULT WINAPI StgStreamImpl_Stat(
541 IStream* iface,
542 STATSTG* pstatstg, /* [out] */
543 DWORD grfStatFlag) /* [in] */
545 StgStreamImpl* This = impl_from_IStream(iface);
547 DirEntry currentEntry;
548 HRESULT hr;
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,
566 This->dirEntry,
567 &currentEntry);
569 if (SUCCEEDED(hr))
571 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
572 pstatstg,
573 &currentEntry,
574 grfStatFlag);
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;
582 return S_OK;
585 WARN("failed to read entry\n");
586 return hr;
589 /***
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(
602 IStream* iface,
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);
612 * Sanity check
615 if (!This->parentStorage)
616 return STG_E_REVERTED;
618 if ( ppstm == 0 )
619 return STG_E_INVALIDPOINTER;
621 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry);
623 if (!new_stream)
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,
642 StgStreamImpl_Read,
643 StgStreamImpl_Write,
644 StgStreamImpl_Seek,
645 StgStreamImpl_SetSize,
646 StgStreamImpl_CopyTo,
647 StgStreamImpl_Commit,
648 StgStreamImpl_Revert,
649 StgStreamImpl_LockRegion,
650 StgStreamImpl_UnlockRegion,
651 StgStreamImpl_Stat,
652 StgStreamImpl_Clone
655 /******************************************************************************
656 ** StgStreamImpl implementation
659 /***
660 * This is the constructor for the StgStreamImpl class.
662 * Params:
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,
668 DWORD grfMode,
669 DirRef dirEntry)
671 StgStreamImpl* newStream;
673 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
675 if (newStream)
678 * Set-up the virtual function table and reference count.
680 newStream->IStream_iface.lpVtbl = &StgStreamVtbl;
681 newStream->ref = 0;
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);
710 return newStream;