dinput: Implement GetObjectInfo[A|W] in base device class.
[wine] / dlls / ole32 / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  *
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.
15  *
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.
20  *
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
24  */
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winerror.h"
39 #include "winreg.h"
40 #include "winternl.h"
41 #include "wine/debug.h"
42
43 #include "storage32.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
46
47
48 /***
49  * This is the destructor of the StgStreamImpl class.
50  *
51  * This method will clean-up all the resources used-up by the given StgStreamImpl
52  * class. The pointer passed-in to this function will be freed and will not
53  * be valid anymore.
54  */
55 static void StgStreamImpl_Destroy(StgStreamImpl* This)
56 {
57   TRACE("(%p)\n", This);
58
59   /*
60    * Release the reference we are holding on the parent storage.
61    * IStorage_Release((IStorage*)This->parentStorage);
62    *
63    * No, don't do this. Some apps call IStorage_Release without
64    * calling IStream_Release first. If we grab a reference the
65    * file is not closed, and the app fails when it tries to
66    * reopen the file (Easy-PC, for example). Just inform the
67    * storage that we have closed the stream
68    */
69
70   if(This->parentStorage) {
71
72     StorageBaseImpl_RemoveStream(This->parentStorage, This);
73
74   }
75
76   This->parentStorage = 0;
77
78   /*
79    * Make sure we clean-up the block chain stream objects that we were using.
80    */
81   if (This->bigBlockChain != 0)
82   {
83     BlockChainStream_Destroy(This->bigBlockChain);
84     This->bigBlockChain = 0;
85   }
86
87   if (This->smallBlockChain != 0)
88   {
89     SmallBlockChainStream_Destroy(This->smallBlockChain);
90     This->smallBlockChain = 0;
91   }
92
93   /*
94    * Finally, free the memory used-up by the class.
95    */
96   HeapFree(GetProcessHeap(), 0, This);
97 }
98
99 /***
100  * This implements the IUnknown method QueryInterface for this
101  * class
102  */
103 static HRESULT WINAPI StgStreamImpl_QueryInterface(
104                   IStream*     iface,
105                   REFIID         riid,        /* [in] */
106                   void**         ppvObject)   /* [iid_is][out] */
107 {
108   StgStreamImpl* const This=(StgStreamImpl*)iface;
109
110   /*
111    * Perform a sanity check on the parameters.
112    */
113   if (ppvObject==0)
114     return E_INVALIDARG;
115
116   /*
117    * Initialize the return parameter.
118    */
119   *ppvObject = 0;
120
121   /*
122    * Compare the riid with the interface IDs implemented by this object.
123    */
124   if (IsEqualGUID(&IID_IUnknown, riid)||
125       IsEqualGUID(&IID_IPersistStream, riid)||
126       IsEqualGUID(&IID_IStream, riid))
127   {
128     *ppvObject = (IStream*)This;
129   }
130
131   /*
132    * Check that we obtained an interface.
133    */
134   if ((*ppvObject)==0)
135     return E_NOINTERFACE;
136
137   /*
138    * Query Interface always increases the reference count by one when it is
139    * successful
140    */
141   IStream_AddRef(iface);
142
143   return S_OK;
144 }
145
146 /***
147  * This implements the IUnknown method AddRef for this
148  * class
149  */
150 static ULONG WINAPI StgStreamImpl_AddRef(
151                 IStream* iface)
152 {
153   StgStreamImpl* const This=(StgStreamImpl*)iface;
154   return InterlockedIncrement(&This->ref);
155 }
156
157 /***
158  * This implements the IUnknown method Release for this
159  * class
160  */
161 static ULONG WINAPI StgStreamImpl_Release(
162                 IStream* iface)
163 {
164   StgStreamImpl* const This=(StgStreamImpl*)iface;
165
166   ULONG ref;
167
168   ref = InterlockedDecrement(&This->ref);
169
170   /*
171    * If the reference count goes down to 0, perform suicide.
172    */
173   if (ref==0)
174   {
175     StgStreamImpl_Destroy(This);
176   }
177
178   return ref;
179 }
180
181 /***
182  * This method will open the block chain pointed by the property
183  * that describes the stream.
184  * If the stream's size is null, no chain is opened.
185  */
186 static void StgStreamImpl_OpenBlockChain(
187         StgStreamImpl* This)
188 {
189   StgProperty    curProperty;
190   BOOL         readSuccessful;
191
192   /*
193    * Make sure no old object is left over.
194    */
195   if (This->smallBlockChain != 0)
196   {
197     SmallBlockChainStream_Destroy(This->smallBlockChain);
198     This->smallBlockChain = 0;
199   }
200
201   if (This->bigBlockChain != 0)
202   {
203     BlockChainStream_Destroy(This->bigBlockChain);
204     This->bigBlockChain = 0;
205   }
206
207   /*
208    * Read the information from the property.
209    */
210   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
211                                              This->ownerProperty,
212                                              &curProperty);
213
214   if (readSuccessful)
215   {
216     This->streamSize = curProperty.size;
217
218     /*
219      * This code supports only streams that are <32 bits in size.
220      */
221     assert(This->streamSize.u.HighPart == 0);
222
223     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
224     {
225       assert( (This->streamSize.u.HighPart == 0) && (This->streamSize.u.LowPart == 0) );
226     }
227     else
228     {
229       if ( (This->streamSize.u.HighPart == 0) &&
230            (This->streamSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
231       {
232         This->smallBlockChain = SmallBlockChainStream_Construct(
233                                                                 This->parentStorage->ancestorStorage,
234                                                                 This->ownerProperty);
235       }
236       else
237       {
238         This->bigBlockChain = BlockChainStream_Construct(
239                                                          This->parentStorage->ancestorStorage,
240                                                          NULL,
241                                                          This->ownerProperty);
242       }
243     }
244   }
245 }
246
247 /***
248  * This method is part of the ISequentialStream interface.
249  *
250  * It reads a block of information from the stream at the current
251  * position. It then moves the current position at the end of the
252  * read block
253  *
254  * See the documentation of ISequentialStream for more info.
255  */
256 static HRESULT WINAPI StgStreamImpl_Read(
257                   IStream*     iface,
258                   void*          pv,        /* [length_is][size_is][out] */
259                   ULONG          cb,        /* [in] */
260                   ULONG*         pcbRead)   /* [out] */
261 {
262   StgStreamImpl* const This=(StgStreamImpl*)iface;
263
264   ULONG bytesReadBuffer;
265   ULONG bytesToReadFromBuffer;
266   HRESULT res;
267
268   TRACE("(%p, %p, %d, %p)\n",
269         iface, pv, cb, pcbRead);
270
271   if (!This->parentStorage)
272   {
273     WARN("storage reverted\n");
274     return STG_E_REVERTED;
275   }
276
277   /*
278    * If the caller is not interested in the number of bytes read,
279    * we use another buffer to avoid "if" statements in the code.
280    */
281   if (pcbRead==0)
282     pcbRead = &bytesReadBuffer;
283
284   /*
285    * Using the known size of the stream, calculate the number of bytes
286    * to read from the block chain
287    */
288   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
289
290   /*
291    * Depending on the type of chain that was opened when the stream was constructed,
292    * we delegate the work to the method that reads the block chains.
293    */
294   if (This->smallBlockChain!=0)
295   {
296     res = SmallBlockChainStream_ReadAt(This->smallBlockChain,
297                                  This->currentPosition,
298                                  bytesToReadFromBuffer,
299                                  pv,
300                                  pcbRead);
301
302   }
303   else if (This->bigBlockChain!=0)
304   {
305     res = BlockChainStream_ReadAt(This->bigBlockChain,
306                  This->currentPosition,
307                  bytesToReadFromBuffer,
308                  pv,
309                  pcbRead);
310   }
311   else
312   {
313     /*
314      * Small and big block chains are both NULL. This case will happen
315      * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
316      */
317
318     *pcbRead = 0;
319     res = S_OK;
320     goto end;
321   }
322
323   if (SUCCEEDED(res))
324   {
325       /*
326        * We should always be able to read the proper amount of data from the
327        * chain.
328        */
329       assert(bytesToReadFromBuffer == *pcbRead);
330
331       /*
332        * Advance the pointer for the number of positions read.
333        */
334       This->currentPosition.u.LowPart += *pcbRead;
335   }
336
337 end:
338   TRACE("<-- %08x\n", res);
339   return res;
340 }
341
342 /***
343  * This method is part of the ISequentialStream interface.
344  *
345  * It writes a block of information to the stream at the current
346  * position. It then moves the current position at the end of the
347  * written block. If the stream is too small to fit the block,
348  * the stream is grown to fit.
349  *
350  * See the documentation of ISequentialStream for more info.
351  */
352 static HRESULT WINAPI StgStreamImpl_Write(
353                   IStream*     iface,
354                   const void*    pv,          /* [size_is][in] */
355                   ULONG          cb,          /* [in] */
356                   ULONG*         pcbWritten)  /* [out] */
357 {
358   StgStreamImpl* const This=(StgStreamImpl*)iface;
359
360   ULARGE_INTEGER newSize;
361   ULONG bytesWritten = 0;
362   HRESULT res;
363
364   TRACE("(%p, %p, %d, %p)\n",
365         iface, pv, cb, pcbWritten);
366
367   /*
368    * Do we have permission to write to this stream?
369    */
370   switch(STGM_ACCESS_MODE(This->grfMode))
371   {
372   case STGM_WRITE:
373   case STGM_READWRITE:
374       break;
375   default:
376       WARN("access denied by flags: 0x%x\n", STGM_ACCESS_MODE(This->grfMode));
377       return STG_E_ACCESSDENIED;
378   }
379
380   if (!pv)
381     return STG_E_INVALIDPOINTER;
382
383   if (!This->parentStorage)
384   {
385     WARN("storage reverted\n");
386     return STG_E_REVERTED;
387   }
388  
389   /*
390    * If the caller is not interested in the number of bytes written,
391    * we use another buffer to avoid "if" statements in the code.
392    */
393   if (pcbWritten == 0)
394     pcbWritten = &bytesWritten;
395
396   /*
397    * Initialize the out parameter
398    */
399   *pcbWritten = 0;
400
401   if (cb == 0)
402   {
403     TRACE("<-- S_OK, written 0\n");
404     return S_OK;
405   }
406   else
407   {
408     newSize.u.HighPart = 0;
409     newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
410   }
411
412   /*
413    * Verify if we need to grow the stream
414    */
415   if (newSize.u.LowPart > This->streamSize.u.LowPart)
416   {
417     /* grow stream */
418     res = IStream_SetSize(iface, newSize);
419     if (FAILED(res))
420       return res;
421   }
422
423   /*
424    * Depending on the type of chain that was opened when the stream was constructed,
425    * we delegate the work to the method that readwrites to the block chains.
426    */
427   if (This->smallBlockChain!=0)
428   {
429     res = SmallBlockChainStream_WriteAt(This->smallBlockChain,
430                                   This->currentPosition,
431                                   cb,
432                                   pv,
433                                   pcbWritten);
434
435   }
436   else if (This->bigBlockChain!=0)
437   {
438     res = BlockChainStream_WriteAt(This->bigBlockChain,
439                              This->currentPosition,
440                              cb,
441                              pv,
442                              pcbWritten);
443   }
444   else
445   {
446     /* this should never happen because the IStream_SetSize call above will
447      * make sure a big or small block chain is created */
448     assert(FALSE);
449     res = 0;
450   }
451
452   /*
453    * Advance the position pointer for the number of positions written.
454    */
455   This->currentPosition.u.LowPart += *pcbWritten;
456
457   TRACE("<-- S_OK, written %u\n", *pcbWritten);
458   return res;
459 }
460
461 /***
462  * This method is part of the IStream interface.
463  *
464  * It will move the current stream pointer according to the parameters
465  * given.
466  *
467  * See the documentation of IStream for more info.
468  */
469 static HRESULT WINAPI StgStreamImpl_Seek(
470                   IStream*      iface,
471                   LARGE_INTEGER   dlibMove,         /* [in] */
472                   DWORD           dwOrigin,         /* [in] */
473                   ULARGE_INTEGER* plibNewPosition) /* [out] */
474 {
475   StgStreamImpl* const This=(StgStreamImpl*)iface;
476
477   ULARGE_INTEGER newPosition;
478
479   TRACE("(%p, %d, %d, %p)\n",
480         iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
481
482   /*
483    * fail if the stream has no parent (as does windows)
484    */
485
486   if (!This->parentStorage)
487     return STG_E_REVERTED;
488
489   /*
490    * The caller is allowed to pass in NULL as the new position return value.
491    * If it happens, we assign it to a dynamic variable to avoid special cases
492    * in the code below.
493    */
494   if (plibNewPosition == 0)
495   {
496     plibNewPosition = &newPosition;
497   }
498
499   /*
500    * The file pointer is moved depending on the given "function"
501    * parameter.
502    */
503   switch (dwOrigin)
504   {
505     case STREAM_SEEK_SET:
506       plibNewPosition->u.HighPart = 0;
507       plibNewPosition->u.LowPart  = 0;
508       break;
509     case STREAM_SEEK_CUR:
510       *plibNewPosition = This->currentPosition;
511       break;
512     case STREAM_SEEK_END:
513       *plibNewPosition = This->streamSize;
514       break;
515     default:
516       return STG_E_INVALIDFUNCTION;
517   }
518
519   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
520
521   /*
522    * tell the caller what we calculated
523    */
524   This->currentPosition = *plibNewPosition;
525
526   return S_OK;
527 }
528
529 /***
530  * This method is part of the IStream interface.
531  *
532  * It will change the size of a stream.
533  *
534  * TODO: Switch from small blocks to big blocks and vice versa.
535  *
536  * See the documentation of IStream for more info.
537  */
538 static HRESULT WINAPI StgStreamImpl_SetSize(
539                                      IStream*      iface,
540                                      ULARGE_INTEGER  libNewSize)   /* [in] */
541 {
542   StgStreamImpl* const This=(StgStreamImpl*)iface;
543
544   StgProperty    curProperty;
545   BOOL         Success;
546
547   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
548
549   if(!This->parentStorage)
550     return STG_E_REVERTED;
551
552   /*
553    * As documented.
554    */
555   if (libNewSize.u.HighPart != 0)
556     return STG_E_INVALIDFUNCTION;
557
558   /*
559    * Do we have permission?
560    */
561   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
562     return STG_E_ACCESSDENIED;
563
564   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
565     return S_OK;
566
567   /*
568    * This will happen if we're creating a stream
569    */
570   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
571   {
572     if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
573     {
574       This->smallBlockChain = SmallBlockChainStream_Construct(
575                                     This->parentStorage->ancestorStorage,
576                                     This->ownerProperty);
577     }
578     else
579     {
580       This->bigBlockChain = BlockChainStream_Construct(
581                                 This->parentStorage->ancestorStorage,
582                                 NULL,
583                                 This->ownerProperty);
584     }
585   }
586
587   /*
588    * Read this stream's property to see if it's small blocks or big blocks
589    */
590   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
591                                        This->ownerProperty,
592                                        &curProperty);
593   /*
594    * Determine if we have to switch from small to big blocks or vice versa
595    */
596   if ( (This->smallBlockChain!=0) &&
597        (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
598   {
599     if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
600     {
601       /*
602        * Transform the small block chain into a big block chain
603        */
604       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
605                                 This->parentStorage->ancestorStorage,
606                                 &This->smallBlockChain);
607     }
608   }
609
610   if (This->smallBlockChain!=0)
611   {
612     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
613   }
614   else
615   {
616     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
617   }
618
619   /*
620    * Write the new information about this stream to the property
621    */
622   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
623                                        This->ownerProperty,
624                                        &curProperty);
625
626   curProperty.size.u.HighPart = libNewSize.u.HighPart;
627   curProperty.size.u.LowPart = libNewSize.u.LowPart;
628
629   if (Success)
630   {
631     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
632                                 This->ownerProperty,
633                                 &curProperty);
634   }
635
636   This->streamSize = libNewSize;
637
638   return S_OK;
639 }
640
641 /***
642  * This method is part of the IStream interface.
643  *
644  * It will copy the 'cb' Bytes to 'pstm' IStream.
645  *
646  * See the documentation of IStream for more info.
647  */
648 static HRESULT WINAPI StgStreamImpl_CopyTo(
649                                     IStream*      iface,
650                                     IStream*      pstm,         /* [unique][in] */
651                                     ULARGE_INTEGER  cb,           /* [in] */
652                                     ULARGE_INTEGER* pcbRead,      /* [out] */
653                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
654 {
655   StgStreamImpl* const This=(StgStreamImpl*)iface;
656   HRESULT        hr = S_OK;
657   BYTE           tmpBuffer[128];
658   ULONG          bytesRead, bytesWritten, copySize;
659   ULARGE_INTEGER totalBytesRead;
660   ULARGE_INTEGER totalBytesWritten;
661
662   TRACE("(%p, %p, %d, %p, %p)\n",
663         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
664
665   /*
666    * Sanity check
667    */
668
669   if (!This->parentStorage)
670     return STG_E_REVERTED;
671
672   if ( pstm == 0 )
673     return STG_E_INVALIDPOINTER;
674
675   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
676   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
677
678   /*
679    * use stack to store data temporarily
680    * there is surely a more performant way of doing it, for now this basic
681    * implementation will do the job
682    */
683   while ( cb.u.LowPart > 0 )
684   {
685     if ( cb.u.LowPart >= 128 )
686       copySize = 128;
687     else
688       copySize = cb.u.LowPart;
689
690     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
691
692     totalBytesRead.u.LowPart += bytesRead;
693
694     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
695
696     totalBytesWritten.u.LowPart += bytesWritten;
697
698     /*
699      * Check that read & write operations were successful
700      */
701     if (bytesRead != bytesWritten)
702     {
703       hr = STG_E_MEDIUMFULL;
704       break;
705     }
706
707     if (bytesRead!=copySize)
708       cb.u.LowPart = 0;
709     else
710       cb.u.LowPart -= bytesRead;
711   }
712
713   /*
714    * Update number of bytes read and written
715    */
716   if (pcbRead)
717   {
718     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
719     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
720   }
721
722   if (pcbWritten)
723   {
724     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
725     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
726   }
727   return hr;
728 }
729
730 /***
731  * This method is part of the IStream interface.
732  *
733  * For streams contained in structured storages, this method
734  * does nothing. This is what the documentation tells us.
735  *
736  * See the documentation of IStream for more info.
737  */
738 static HRESULT WINAPI StgStreamImpl_Commit(
739                   IStream*      iface,
740                   DWORD           grfCommitFlags)  /* [in] */
741 {
742   StgStreamImpl* const This=(StgStreamImpl*)iface;
743
744   if (!This->parentStorage)
745     return STG_E_REVERTED;
746
747   return S_OK;
748 }
749
750 /***
751  * This method is part of the IStream interface.
752  *
753  * For streams contained in structured storages, this method
754  * does nothing. This is what the documentation tells us.
755  *
756  * See the documentation of IStream for more info.
757  */
758 static HRESULT WINAPI StgStreamImpl_Revert(
759                   IStream* iface)
760 {
761   return S_OK;
762 }
763
764 static HRESULT WINAPI StgStreamImpl_LockRegion(
765                                         IStream*     iface,
766                                         ULARGE_INTEGER libOffset,   /* [in] */
767                                         ULARGE_INTEGER cb,          /* [in] */
768                                         DWORD          dwLockType)  /* [in] */
769 {
770   StgStreamImpl* const This=(StgStreamImpl*)iface;
771
772   if (!This->parentStorage)
773     return STG_E_REVERTED;
774
775   FIXME("not implemented!\n");
776   return E_NOTIMPL;
777 }
778
779 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
780                                           IStream*     iface,
781                                           ULARGE_INTEGER libOffset,   /* [in] */
782                                           ULARGE_INTEGER cb,          /* [in] */
783                                           DWORD          dwLockType)  /* [in] */
784 {
785   StgStreamImpl* const This=(StgStreamImpl*)iface;
786
787   if (!This->parentStorage)
788     return STG_E_REVERTED;
789
790   FIXME("not implemented!\n");
791   return E_NOTIMPL;
792 }
793
794 /***
795  * This method is part of the IStream interface.
796  *
797  * This method returns information about the current
798  * stream.
799  *
800  * See the documentation of IStream for more info.
801  */
802 static HRESULT WINAPI StgStreamImpl_Stat(
803                   IStream*     iface,
804                   STATSTG*       pstatstg,     /* [out] */
805                   DWORD          grfStatFlag)  /* [in] */
806 {
807   StgStreamImpl* const This=(StgStreamImpl*)iface;
808
809   StgProperty    curProperty;
810   BOOL         readSuccessful;
811
812   TRACE("%p %p %d\n", This, pstatstg, grfStatFlag);
813
814   /*
815    * if stream has no parent, return STG_E_REVERTED
816    */
817
818   if (!This->parentStorage)
819     return STG_E_REVERTED;
820
821   /*
822    * Read the information from the property.
823    */
824   readSuccessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
825                                              This->ownerProperty,
826                                              &curProperty);
827
828   if (readSuccessful)
829   {
830     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
831                                      &curProperty,
832                                      grfStatFlag);
833
834     pstatstg->grfMode = This->grfMode;
835
836     return S_OK;
837   }
838
839   return E_FAIL;
840 }
841
842 /***
843  * This method is part of the IStream interface.
844  *
845  * This method returns a clone of the interface that allows for
846  * another seek pointer
847  *
848  * See the documentation of IStream for more info.
849  *
850  * I am not totally sure what I am doing here but I presume that this
851  * should be basically as simple as creating a new stream with the same
852  * parent etc and positioning its seek cursor.
853  */
854 static HRESULT WINAPI StgStreamImpl_Clone(
855                                    IStream*     iface,
856                                    IStream**    ppstm) /* [out] */
857 {
858   StgStreamImpl* const This=(StgStreamImpl*)iface;
859   HRESULT hres;
860   StgStreamImpl* new_stream;
861   LARGE_INTEGER seek_pos;
862
863   TRACE("%p %p\n", This, ppstm);
864
865   /*
866    * Sanity check
867    */
868
869   if (!This->parentStorage)
870     return STG_E_REVERTED;
871
872   if ( ppstm == 0 )
873     return STG_E_INVALIDPOINTER;
874
875   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
876
877   if (!new_stream)
878     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
879
880   *ppstm = (IStream*) new_stream;
881   seek_pos.QuadPart = This->currentPosition.QuadPart;
882
883   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
884
885   assert (SUCCEEDED(hres));
886
887   return S_OK;
888 }
889
890 /*
891  * Virtual function table for the StgStreamImpl class.
892  */
893 static const IStreamVtbl StgStreamImpl_Vtbl =
894 {
895     StgStreamImpl_QueryInterface,
896     StgStreamImpl_AddRef,
897     StgStreamImpl_Release,
898     StgStreamImpl_Read,
899     StgStreamImpl_Write,
900     StgStreamImpl_Seek,
901     StgStreamImpl_SetSize,
902     StgStreamImpl_CopyTo,
903     StgStreamImpl_Commit,
904     StgStreamImpl_Revert,
905     StgStreamImpl_LockRegion,
906     StgStreamImpl_UnlockRegion,
907     StgStreamImpl_Stat,
908     StgStreamImpl_Clone
909 };
910
911 /******************************************************************************
912 ** StgStreamImpl implementation
913 */
914
915 /***
916  * This is the constructor for the StgStreamImpl class.
917  *
918  * Params:
919  *    parentStorage - Pointer to the storage that contains the stream to open
920  *    ownerProperty - Index of the property that points to this stream.
921  */
922 StgStreamImpl* StgStreamImpl_Construct(
923                 StorageBaseImpl* parentStorage,
924     DWORD            grfMode,
925     ULONG            ownerProperty)
926 {
927   StgStreamImpl* newStream;
928
929   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
930
931   if (newStream!=0)
932   {
933     /*
934      * Set-up the virtual function table and reference count.
935      */
936     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
937     newStream->ref       = 0;
938
939     newStream->parentStorage = parentStorage;
940
941     /*
942      * We want to nail-down the reference to the storage in case the
943      * stream out-lives the storage in the client application.
944      *
945      * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
946      *
947      * No, don't do this. Some apps call IStorage_Release without
948      * calling IStream_Release first. If we grab a reference the
949      * file is not closed, and the app fails when it tries to
950      * reopen the file (Easy-PC, for example)
951      */
952
953     newStream->grfMode = grfMode;
954     newStream->ownerProperty = ownerProperty;
955
956     /*
957      * Start the stream at the beginning.
958      */
959     newStream->currentPosition.u.HighPart = 0;
960     newStream->currentPosition.u.LowPart = 0;
961
962     /*
963      * Initialize the rest of the data.
964      */
965     newStream->streamSize.u.HighPart = 0;
966     newStream->streamSize.u.LowPart  = 0;
967     newStream->bigBlockChain       = 0;
968     newStream->smallBlockChain     = 0;
969
970     /*
971      * Read the size from the property and determine if the blocks forming
972      * this stream are large or small.
973      */
974     StgStreamImpl_OpenBlockChain(newStream);
975   }
976
977   return newStream;
978 }